HDDS-16220. Reduce OM bucket write-lock hold time during FSO directory purge - #11118
HDDS-16220. Reduce OM bucket write-lock hold time during FSO directory purge#11118yandrey321 wants to merge 8 commits into
Conversation
|
@yandrey321 thanks for the patch! |
There was a problem hiding this comment.
Pull request overview
This PR targets OM write-path latency during FSO directory purges by reducing bucket write-lock hold time in the purge apply handler and preventing multi-bucket lock stacking by ensuring purge transactions are submitted per bucket. It does so by splitting the purge apply into a lock-free “prepare” phase and a lock-held “apply” phase, and by grouping DirectoryDeletingService submissions by (volumeId, bucketId).
Changes:
- Refactors
OMDirectoriesPurgeRequestWithFSO.validateAndUpdateCacheinto a two-phase flow (precompute outside the bucket lock; apply tombstones/quota/hsync cleanup under the lock). - Updates
DirectoryDeletingServicebatching to group purge paths per bucket so eachPurgeDirectoriestransaction takes only one bucket write lock. - Adds/extends unit tests plus new benchmark-tagged micro/e2e benchmarks (and a small
CodecBufferhelper to disable leak detection for perf measurements).
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestDirectoryDeletingService.java | Adds a unit test asserting one bucket per submitted purge transaction. |
| hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMDirectoriesPurgeRequestAndResponse.java | Adds a regression test validating light-parse equivalence for replicated bytes + hsync open-key handling. |
| hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMDirectoriesPurgeApplyPerf.java | New benchmark-tagged microbenchmark for purge apply-thread time. |
| hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/DirectoryDeletingService.java | Groups purge path submissions by bucket while preserving byte-limit batching per bucket. |
| hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMDirectoriesPurgeRequestWithFSO.java | Implements two-phase purge apply, adds light-parse helpers, memoizes bucket-info lookups, and batches quota/metric mutations. |
| hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMetrics.java | Adjusts internal delete metric increment to accept a count for bulk updates. |
| hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/service/TestOmMixedWorkloadUnderDeletionBench.java | New benchmark-tagged end-to-end mixed workload benchmark under deletion load (optional profiling support). |
| hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/utils/db/CodecBuffer.java | Adds disableLeakDetection() to revert leak-detecting buffer construction for perf-sensitive benchmarks. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Pre-PR vs post-PR: bucket lock behavior during FSO directory purgeTwo distinct lock-hold problems are easy to conflate because both show up as long OM apply times, but they have different causes and different fixes. 1. Large single-bucket batches (still a hotspot, but cheaper)Before this PR, a single
So large single-bucket batches already caused long write-lock holds on that bucket. This PR does not split those batches further (HDDS-16297), but it does move parse/precompute outside the lock and keeps only tombstones + batched quota work under the lock. Same batch size, shorter hold. 2. Multiple buckets in one transaction (fixed by this PR, AOS path)Terminology matters here:
Before this PR, On apply, Snapshot deep-clean was already single-bucket end-to-end: the deleted-dir supplier is scoped to one This PR groups DDS submissions by Summary
The remaining hotspot called out in review is the first and last rows: dense single-bucket work is cheaper now, but a very large purge path can still hold one bucket write lock for the full apply phase until HDDS-16297 lands. |
|
|
||
| // Group purge paths by their owning bucket so that every submitted purge transaction contains paths from a single | ||
| // bucket only. This keeps the apply side acquiring exactly one bucket write lock per transaction; combined with | ||
| // apply-side chunking within that lock, a large background directory purge cannot starve readers on other buckets. |
There was a problem hiding this comment.
Could we drop apply-side chunking here? This patch does not chunk a PurgePathRequest during apply; HDDS-16297 tracks DDS-side splitting.
What changes were proposed in this pull request?
DirectoryDeletingService(DDS) drains the FSO deleted-directory backlog bysubmitting
PurgeDirectoriestransactions to the OM. Each transaction isapplied on the single OM state-machine thread while holding the bucket write
lock. Two aspects of the old path let a background purge stall interactive
writes and reads:
Wide lock hold time.
OMDirectoriesPurgeRequestWithFSO.validateAndUpdateCachedid all of its work — protobuf field extraction, RocksDB key-string
construction, replicated block-size summation, hsync client-id lookup — while
holding the bucket write lock.
Cross-bucket lock stacking. A single purge transaction could carry paths
from multiple buckets, so applying it acquired multiple bucket write locks,
blocking read RPCs (
lookupKey,getFileStatus,listStatus) on bucketsunrelated to the deletion.
This PR addresses both:
Two-phase apply in
OMDirectoriesPurgeRequestWithFSOPurgePathRequestand precompute eachentry's delete key, path key, replicated byte size, hsync client id, and
prepared sub-dir/sub-file tombstones into a
PurgeApplyState.chain unchanged), then apply the precomputed cache tombstones, quota deltas,
and hsync open-key cleanup. Snapshot-namespace
copyObjectwork is moved outof the locked region.
Per-bucket grouping in
DirectoryDeletingServicesubmitPurgePathsWithBatchinggroupsPurgePathRequests by(volumeId, bucketId)before submission, so each submitted transactioncarries paths from exactly one bucket and the apply side takes exactly one
bucket write lock. Byte-limit batching is preserved within each bucket group.
Wire format is unchanged — no protobuf, RPC, or RocksDB-schema changes.
Generated-by: Claude Code (claude-opus-4-8)
What is the link to the Apache JIRA
https://issues.apache.org/jira/browse/HDDS-16220
How was this patch tested?
CI: https://github.com/yandrey321/ozone/actions/runs/32983050746/job/98250638151
TestOMDirectoriesPurgeRequestAndResponse#testLightParsePreservesReplicatedByteSumAndHsyncForSubFiles— proves the lock-free phase-1 light parse yields the exact same replicated
byte sum and hsync open-key handling as the original full
OmKeyInfoparse(bucket
usedBytesdecrements to precisely zero).TestDirectoryDeletingService#testPurgeDirectoriesGroupedByBucketPerTransaction— interleaves paths from two buckets and asserts one bucket per submitted
PurgeDirectoriestransaction, order-independently.Benchmark runs an interactive workload (create/mkdir/rename/filewrite plus reads) against
a live FSO directory purge and reports p50/p99 per operation, first with no
deletion running (control) and then during the drain (under
load).
Configuration: 180k-file delete backlog across 4 buckets, 14 client threads for mixed workload, 32 MB
Ratis appender byte limit. Baseline (pre-change) and fix measured back-to-back.
p99 latency, control (no purge running) — baseline vs fix:
p99 latency, under load (during the purge drain) — baseline vs fix:
Interactive ops completed during the drain window (effectively a fixed ~20 s
observation window):