[
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: [email protected]
For additional commands, e-mail: [email protected]