[ 
https://issues.apache.org/jira/browse/HDDS-17468?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Siyao Meng reassigned HDDS-17468:
---------------------------------

    Assignee: Siyao Meng

> Rename responses miss an earlier snapshotRenamedTable put in the same flush 
> batch, so blocks a snapshot still holds can be reclaimed
> ------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: HDDS-17468
>                 URL: https://issues.apache.org/jira/browse/HDDS-17468
>             Project: Apache Ozone
>          Issue Type: Bug
>    Affects Versions: 1.4.0, 2.0.0, 1.4.1, 2.1.0, 2.2.0, 2.1.1, 2.1.2, 2.2.1
>            Reporter: Siyao Meng
>            Assignee: Siyao Meng
>            Priority: Critical
>         Attachments: MC-2-record-first-rename-once-per-flush-batch.patch
>
>
> h3. Mechanism
> In a bucket that has a snapshot, {{snapshotRenamedTable}} maps 
> {{/volume/bucket/objectID}} to the name the object has in the latest 
> snapshot, and only the first rename after the snapshot may write it. 
> {{OMKeyRenameResponse.addToDBBatch}}, {{OMKeysRenameResponse.addToDBBatch}} 
> and {{OMKeyRenameResponseWithFSO.addToDBBatch}} enforce that on the double 
> buffer flush thread: each reads the entry with {{snapshotRenamedTable.get}} 
> and puts its source name only if the read returns null.
> Nothing adds cache entries to {{snapshotRenamedTable}}, so that read only 
> returns data from batches that are already committed. A put that an earlier 
> response added to the current {{BatchOperation}} is not visible to it. When 
> two rename responses for the same object are in one flush batch, the second 
> one also reads null and its put replaces the first one's. The entry then 
> holds the intermediate name, which the object never had in the snapshot, 
> instead of its name in the snapshot.
> Which value is stored depends on where the batch boundaries fall, so two OMs 
> that apply the same log can store different values for the same entry.
> h3. Trigger
> # A bucket has a snapshot, and an object in it has no 
> {{snapshotRenamedTable}} entry yet.
> # Two rename responses for that object are flushed in the same double buffer 
> batch, the second one renaming from the name the first one produced.
> # The object's key is deleted and {{KeyDeletingService}} processes its 
> {{deletedTable}} entry.
> h3. Impact
> * {{KeyManagerImpl.getPreviousSnapshotOzonePathInfo}} looks the deleted key 
> up in the snapshot under the stored intermediate name and finds nothing. 
> {{ReclaimableKeyFilter.isReclaimable}} returns true, and 
> {{KeyDeletingService}} purges the entry and sends its blocks to SCM for 
> deletion, although the snapshot still holds the key under its original name 
> with the same object ID and blocks. The object ID check in 
> {{ReclaimableKeyFilter.getPreviousSnapshotKeyInfo}} runs only when the lookup 
> finds a key, so it does not catch this. Once the datanodes delete those 
> blocks, the key can no longer be read from the snapshot. That last step was 
> not run: the test setup has no datanodes.
> * Nothing corrects the wrong entry later. The next snapshot of the bucket 
> copies it through its checkpoint, so the snapshot deep clean path reads it as 
> well. This was found by reading the code, not run.
> h3. Reproduction
> The regression tests in the attached patch cover it, and the ones marked 
> below fail on unmodified source. All are in {{hadoop-ozone/ozone-manager}}:
> * {{TestOMKeyRenameResponse.testSecondRenameKeepsSnapshotName}}, also run 
> through {{TestOMKeyRenameResponseWithFSO}}: two rename responses for one key 
> in a snapshotted bucket. With both in one batch it fails without the change 
> (the entry holds the intermediate name). With a commit between them it 
> passes, as a control.
> * {{TestOMKeysRenameResponse.testSecondRenameInSameBatchKeepsSnapshotName}}: 
> two {{OMKeysRenameResponse}} instances for one key in one batch. Fails 
> without the change.
> * 
> {{TestOMKeyRenameResponseWithFSO.testSecondDirectoryRenameInSameBatchKeepsSnapshotName}}:
>  two directory rename responses in one batch. Fails without the change.
> * 
> {{TestOMKeyRenameResponse.testRenameOfObjectNotCommittedAtSourceRecordsNothing}},
>  also run through {{TestOMKeyRenameResponseWithFSO}}: the committed source 
> name holds a different object, so no entry may be written. Without the change 
> an entry is written.
> There is no standalone reproduction.
> h3. Patch
> [^MC-2-record-first-rename-once-per-flush-batch.patch], against 
> a5814528cdc8d3c1b27f259e0db2256b585cb825. It also applies to master at 
> 88119243f99.
> The decision stays on the flush thread, and all three responses get one more 
> condition: the committed DB must still hold the same object (same object ID) 
> at the rename's source key. The check reads with {{getSkipCache}} from the 
> key table, the file table, or for an FSO directory rename the directory table 
> (helper {{OMKeyRenameResponse.isCommittedAt}}). If an earlier rename of the 
> object is in the same batch, the object got its current name inside that 
> batch. The committed table then does not hold it at the source, and the later 
> rename leaves the entry that the first rename wrote. So for renames of the 
> same object, the stored value no longer depends on where the batch boundaries 
> fall.
> The decision is deliberately not moved to apply time. Snapshot create clears 
> the bucket's {{snapshotRenamedTable}} range only when its own batch is 
> flushed, and it adds no cache entries. A rename applied after a snapshot 
> create but before that flush would read the stale entry, write nothing, and 
> leave the object without an entry once the range is cleared. That loses 
> blocks the same way. On the flush thread, snapshot create is already a batch 
> of its own, so a rename after it always reads the cleared range.
> A separate, older defect is not addressed here: an 
> {{OMSnapshotMoveTableKeysResponse}} that moves entries into the active DB, 
> followed in the same batch by a rename of one of those objects, still 
> overwrites the moved entry. That is HDDS-17467, whose patch is independent of 
> this one.
> The patch adds no request or response field and no proto change. Its cost is 
> one extra point read per rename, only in a bucket that has a snapshot and 
> only while the object has no entry. An object that is created and renamed 
> within one batch now gets no entry. It is in no snapshot, so that entry was 
> never used. Entries already stored with a wrong name are not repaired.
> With the patch, these pass: the rename, key purge, directory purge, snapshot 
> create and snapshot move table keys response and request suites, 
> {{TestKeyManagerImpl}}, {{TestKeyDeletingService}}, 
> {{TestSnapshotDeletingService}}, {{TestReclaimableKeyFilter}} and 
> {{TestReclaimableRenameEntryFilter}}. Checkstyle is clean. Integration tests 
> were not run.
> Found by TLA+ model checking and code review of the OM table cache, double 
> buffer flush and startup paths under HDDS-15926, on commit 
> a5814528cdc8d3c1b27f259e0db2256b585cb825. Checked against HDDS issues and 
> apache/ozone pull requests for duplicates before filing. Related but not 
> duplicates: HDDS-8189 and HDDS-8435 (introduced the check), HDDS-17173 (table 
> iterators do not return unflushed entries), HDDS-15125 (proposed redesign of 
> snapshot reclaimability). The attached patch is a proposal for review. 
> Generated with Specula (Claude Opus 5.5).



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

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

Reply via email to