Siyao Meng created HDDS-16770:
---------------------------------

             Summary: FSO rename response serializes the live cached 
OmBucketInfo, corrupting bucket usage after crash and replay
                 Key: HDDS-16770
                 URL: https://issues.apache.org/jira/browse/HDDS-16770
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: CR-2-copy-bucket-info-into-fso-rename-response.patch, 
TestCR2RenameBucketAlias.java

h3. Mechanism
{{OMKeyRenameRequestWithFSO.setModificationTime}} puts a new {{OmBucketInfo}} 
into the bucket table cache, and {{renameKey}} hands the same instance to 
{{OMKeyRenameResponseWithFSO}} ({{:353}}), which serializes it on the double 
buffer flush thread. Later requests mutate that cached instance in place 
({{OMKeyRequest.getBucketInfo}} returns the cached reference) before the 
rename's batch is written.

The bucket row is therefore committed in a batch whose {{TransactionInfo}} is 
still below the later request's index, but already contains that request's 
{{usedNamespace}} / {{usedBytes}} delta.

h3. Trigger
A rename whose source or destination parent is the bucket root (a rename 
between directories carries no bucket in the response), followed by a request 
that changes bucket usage, such as a key commit, applied while the flush thread 
is still working through the batch that holds the rename, then an OM crash 
before the later request's batch is committed.

h3. Impact
On restart Ratis replays the later request on top of a bucket row that already 
includes it. With a tight namespace quota an acknowledged commit is rejected 
with {{QUOTA_EXCEEDED}} on replay and the file is missing on that OM. Otherwise 
bucket usage is double counted until a quota repair is run.

h3. Reproduction
PASS at component level: [^TestCR2RenameBucketAlias.java] 
({{hadoop-ozone/ozone-manager}}) uses real RocksDB, table caches, the double 
buffer flush thread and the request classes, with the flush timing controlled. 
The crash and the replay are driven by the test, not by a real Ratis restart.

h3. Suggested fix
Give the response its own snapshot: pass {{omBucketInfo.copyObject()}} (when 
non null) to the response, matching the other FSO requests. HDDS-16125 fixed 
the same aliasing in {{OMQuotaRepairRequest}}.

h3. Patch
[^CR-2-copy-bucket-info-into-fso-rename-response.patch], against 
20e6a1c0f6039248373efd3eb1d7f2afcf7f1535. It also applies to master at 
c99d291e863.

One line: {{renameKey}} passes {{omBucketInfo.copyObject()}} (or null when the 
rename does not touch the bucket) to the response. The copy is taken under the 
bucket lock, so no later request can have touched the object yet. This is what 
the other FSO key requests already do.

Covered by {{testFlushedBucketExcludesLaterUsageChange}} in the existing 
{{TestOMKeyRenameRequestWithFSO}}: it applies a root level rename, changes 
{{usedNamespace}} on the cached bucket as a later request would, flushes the 
rename response and reads the bucket row from RocksDB. Without the change it 
fails with "expected: 0L but was: 1L". The rename and double buffer unit suites 
pass (44 tests), checkstyle is clean. A bucket that already double counted 
needs the existing quota repair.

Found by TLA+ model checking and code review of the OM FSO namespace mutation 
paths under HDDS-15926, on commit 20e6a1c0f6039248373efd3eb1d7f2afcf7f1535. 
Checked against HDDS issues and apache/ozone pull requests for duplicates 
before filing. The attached patch is a proposal for review. Generated with 
Specula (Claude Opus 5.5).



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to