You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@ozone.apache.org by "ASF GitHub Bot (Jira)" <ji...@apache.org> on 2026/10/15 06:45:00 UTC
[jira] [Updated] (HDDS-16943) Avoid the extra ListStatus RPC at the end of every file system listing
[ https://issues.apache.org/jira/browse/HDDS-16943?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
ASF GitHub Bot updated HDDS-16943:
----------------------------------
Labels: pull-request-available (was: )
> Avoid the extra ListStatus RPC at the end of every file system listing
> ----------------------------------------------------------------------
>
> Key: HDDS-16943
> URL: https://issues.apache.org/jira/browse/HDDS-16943
> Project: Apache Ozone
> Issue Type: Improvement
> Components: Ozone Manager
> Reporter: Andrey Yarovoy
> Assignee: Andrey Yarovoy
> Priority: Major
> Labels: pull-request-available
>
> h2. Problem
> The file system clients keep listing until OM returns a page that adds no new entry:
> {code:java}
> // BasicRootedOzoneFileSystem.listStatusAdapter (same loop in BasicOzoneFileSystem)
> do {
> tmpStatusList = adapter.listStatus(pathToKey(f), false, startPath, numEntries, ...);
> // add entries, dropping a repeated first entry
> ...
> } while (entriesAdded > 0);
> {code}
> So every \{{FileSystem.listStatus}} costs at least two OM listing RPCs, even when the whole directory fits in the
> first page. The second call returns only the repeated start key (OM includes \{{startKey}} in the result) and adds
> nothing. The \{{listStatusIterator}} path (\{{hasNextNoFilter}}) has the same rule: it fetches another page whenever
> the current one was not empty.
> In a benchmark of 2000 \{{ofs}} \{{listStatus}} calls on directories of 10 entries (\{{TestOfsListStatusRpcBenchmark}},
> HDDS-16760), every call made exactly two ListStatusLight RPCs. One of them is not needed.
> h2. Why the client cannot stop on a short page
> Before HDDS-11696 the loop stopped when a page was shorter than \{{numEntries}}. HDDS-11696 changed it to stop on an
> empty page, because OM caps the page size on its own:
> * \{{OzoneManagerRequestHandler.limitListSize}}: \{{min(numEntries, ozone.om.server.list.max.size)}} (default *1000*);
> * \{{OmMetadataReader.listStatus}} / \{{listStatusLight}}: \{{min(n, ozone.fs.listing.page.size.max)}} (default 1024),
> raised to at least 2 by \{{OzoneConfigUtil.limitValue}}.
> The client asks for \{{ozone.fs.listing.page.size}} (default *1024*), so with default settings a full page of 1000
> entries is shorter than the request. The client cannot know OM's caps, which are server configuration, so a
> client-only "stop on a short page" rule would silently cut off any directory with more than 1000 entries. This
> must not be reintroduced.
> h2. Proposal
> Let OM say whether the listing may continue, as \{{ListKeysResponse}} / \{{ListKeysLightResponse}} already do with
> {\{isTruncated}}.
> # *Proto.* Add \{{optional bool isTruncated}} to \{{ListStatusResponse}} and \{{ListStatusLightResponse}}.
> # *OM.* Set it to \{{returnedCount >= effectiveLimit}}, where \{{effectiveLimit}} is the page size OM actually used
> after both caps above. Compare with that value, not with the requested \{{numEntries}}.
> Both listing paths return \{{min(effectiveLimit, remaining live entries)}}:
> ** FSO, \{{OzoneListStatusHelper.listStatusFSO}}: a single loop {{while (map.size() < numEntries &&
> heapIterator.hasNext())}}; cache tombstones and DB keys shadowed by the cache never reach the heap, so they do
> not use up the limit.
> ** LEGACY/OBS, \{{KeyManagerImpl.listStatus}}: entries are counted only when a live status is added, and the final
> loop stops at \{{numEntries}}.
> So \{{isTruncated = false}} always means the listing is complete. When exactly \{{effectiveLimit}} entries remain,
> the flag is \{{true}} and the client makes one more call, as it does today. A look-ahead
> (\{{heapIterator.hasNext()}} on FSO, remaining map/iterator entries on LEGACY) could avoid that last call, but it is
> not needed for correctness.
> # *Client.* Stop after a page whose response has \{{isTruncated}} set to \{{false}}. If the field is absent (an older
> OM), keep the current loop. Field presence distinguishes the two, so no \{{OzoneManagerVersion}} gate is needed.
> # *Plumbing.* The listing calls return a plain \{{List}} today through \{{OzoneManagerProtocol}},
> \{{ClientProtocol}} / \{{RpcClient}}, \{{OzoneClientAdapter}} and the two file systems. The flag needs a way up,
> for example a small result type holding the entries and the flag, used by the file system listing loops and by
> \{{hasNextNoFilter}}. Existing \{{List}}-returning methods can stay for other callers.
> Both \{{ofs}} (\{{BasicRootedOzoneFileSystem}}) and \{{o3fs}} (\{{BasicOzoneFileSystem}}) benefit.
> h2. Expected effect
> A listing that fits in one page goes from two OM listing RPCs to one. On top of HDDS-16760, an \{{ofs}}
> {\{listStatus}} of such a directory goes from 2 OM RPCs to 1; before HDDS-16760 it was 4.
> h2. Compatibility
> * One new optional field in two response messages; no new request field, result code or configuration.
> * New client, old OM: the field is absent, so the client keeps the current loop.
> * Old client, new OM: the field is ignored; behavior unchanged.
> * Listing results are unchanged; only the number of calls changes.
> h2. Testing
> * OM unit tests for both \{{ListStatus}} and \{{ListStatusLight}}, FSO and LEGACY: \{{isTruncated}} is \{{false}} when
> fewer than the effective limit entries are returned and \{{true}} on a full page, with the effective limit set by
> each cap (\{{ozone.om.server.list.max.size}} below and above \{{ozone.fs.listing.page.size.max}}, and a request of
> size 1, which OM raises to 2).
> * A full page whose only remaining entries are cache tombstones still ends the listing on the next call.
> * Client unit tests: the loop stops after one call when \{{isTruncated}} is \{{false}}, and keeps the current loop
> when the field is absent.
> * Integration: a directory larger than \{{ozone.om.server.list.max.size}} with the client page size above it, listed
> in full through \{{ofs}} and \{{o3fs}}, by \{{listStatus}} and \{{listStatusIterator}}. This is the regression guard
> for HDDS-11696.
> * \{{TestOfsListStatusRpcBenchmark}}: ListStatus RPCs per call drop from 2 to 1.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@ozone.apache.org
For additional commands, e-mail: issues-help@ozone.apache.org