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

Siyao Meng reassigned HDDS-17469:
---------------------------------

    Assignee: Siyao Meng

> OMKeysRenameResponse writes renames whose sources and destinations overlap in 
> map order, so the committed key table differs from the cache
> ------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: HDDS-17469
>                 URL: https://issues.apache.org/jira/browse/HDDS-17469
>             Project: Apache Ozone
>          Issue Type: Bug
>    Affects Versions: 1.1.0, 1.2.0, 1.3.0, 1.2.1, 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: CR-10-skip-overlapping-pairs-in-rename-keys.patch
>
>
> h3. Mechanism
> {{OMKeysRenameRequest.validateAndUpdateCache}} checks each pair against the 
> key table, whose cache already holds the result of the earlier pairs of the 
> same transaction, and stores the accepted pairs in a {{HashMap}} from source 
> name to the new {{OmKeyInfo}}. {{OMKeysRenameResponse.addToDBBatch}} iterates 
> that map and writes, per entry, a delete of the source and a put of the 
> destination. When the pairs of one response share key names, the batch 
> therefore depends on the map's iteration order, and a map keyed by source 
> name cannot hold two pairs with the same source at all. The committed key 
> table then differs from the cache that the request updated:
> * A destination that a later pair renames away (source {{key}} to {{key_b}}, 
> source {{key_b}} to {{key_c}}): when the second entry is iterated first, the 
> batch is delete {{key_b}}, put {{key_c}}, delete {{key}}, put {{key_b}}. The 
> table commits both {{key_b}} and {{key_c}} with the same object ID and block 
> list, while the cache holds only {{key_c}}.
> * A destination that an earlier pair used as its source ({{key}} to 
> {{key_b}}, {{other}} to {{key}}): when the second entry is iterated first, 
> the batch is delete {{other}}, put {{key}}, delete {{key}}, put {{key_b}}. 
> The table commits no {{key}}, while the cache holds {{key}} with the object 
> of {{other}}.
> * A source that appears in two pairs ({{a}} to {{b}}, {{c}} to {{a}}, {{a}} 
> to {{d}}): the map keeps only the last pair for {{a}}, so {{b}} is never 
> written and {{a}} and {{d}} both commit the object of {{c}}.
> Responses whose pairs share no key name are not affected, since their deletes 
> and puts touch disjoint keys. The FSO layout has no multi key rename 
> response, and {{OMKeyRenameResponse}} carries one pair per response, so 
> neither is affected.
> h3. Trigger
> An {{OMKeysRenameResponse}} whose pairs share a key name, as described above. 
> {{validateAndUpdateCache}} builds such a response whenever a pair names a key 
> that an earlier accepted pair of the same transaction renamed from or to, 
> because the existence checks of the later pair read the cache entries written 
> by the earlier one. No timing or fault is needed. OMs running the same 
> version apply the transaction with the same map order, so they commit the 
> same state.
> h3. Impact
> The cache entries of the transaction are evicted shortly after the double 
> buffer flush, and on restart, so the committed state becomes what every read 
> sees:
> * In the first case {{key_b}} reappears as a second key on the blocks of 
> {{key_c}}. When one of the two is deleted, its blocks are added to 
> {{deletedTable}} and sent to SCM for deletion while the other key still lists 
> them, so that key loses its data once the blocks are deleted.
> * In the second and third cases a key that the OM reported as renamed 
> disappears ({{key}}, respectively {{b}}). No key refers to its object any 
> more and its blocks never reach {{deletedTable}}, so they leak.
> These effects were observed on a single real OM (single node Ratis, no 
> datanodes, blocks sent for deletion recorded by a test SCM); actual block 
> deletion on datanodes was not run.
> h3. Reproduction
> The attached patch carries the reproduction as a regression test: 
> {{TestOMKeysRenameRequest.testCommittedKeyTableMatchesCache}} runs a 
> transaction with the first and the second case through {{preExecute}} and 
> {{validateAndUpdateCache}}, commits its response, and compares each key in 
> the DB with the cache; a third case with disjoint pairs is the control. 
> Without the check in {{preExecute}} it fails for both cases ({{key_b}}: 
> present in the DB but not in the cache; {{key}}: present in the cache but not 
> in the DB).
> h3. Patch
> [^CR-10-skip-overlapping-pairs-in-rename-keys.patch], against 
> a5814528cdc8d3c1b27f259e0db2256b585cb825. It also applies to master at 
> 88119243f99.
> {{OMKeysRenameRequest.preExecute}} rejects the transaction with 
> {{INVALID_REQUEST}} when a pair names a key that an earlier pair of the same 
> transaction uses as source or destination (pairs with an empty name are 
> skipped, as {{validateAndUpdateCache}} never applies them). The check needs 
> only the key names, so it runs on the leader before the transaction is 
> replicated, and {{validateAndUpdateCache}} and the response are unchanged: 
> entries already in the Raft log are applied as before, and OMs of different 
> versions applying the same entry stay consistent. A leader without the patch 
> still replicates such a transaction, and patched followers then apply it the 
> old way, consistent but unfixed.
> Behaviour change: a transaction with pairs that share a key is now rejected 
> as a whole, nothing in it is renamed, and the caller gets 
> {{INVALID_REQUEST}}. Today the committed result of such a transaction depends 
> on the key names: for some names it matches the cache (for example {{k}} to 
> {{k_b}}, then {{k_b}} to {{k_c}}), for others keys are lost or duplicated. 
> Those that work today by chance are rejected too. Transactions whose pairs 
> share no key behave as before.
> Keeping the pairs in their original order in the response was considered and 
> not taken: the map keyed by source name loses the earlier of two pairs with 
> one source (third case), so it would need an ordered list of pairs passed 
> from the request to the response, a larger change of both.
> With the patch {{TestOMKeysRenameRequest}}, {{TestOMKeysRenameResponse}}, 
> {{TestOMKeyRenameRequest}}, {{TestOMKeyRenameRequestWithFSO}}, 
> {{TestOMKeyRenameResponse}} and {{TestOMKeyRenameResponseWithFSO}} (36 tests) 
> pass, as do {{testKeysRename}} and {{testKeysRenameFail}} of the mini cluster 
> RPC suite and {{TestOzoneManagerRestart.testRestartOMWithKeyOperation}} on a 
> mini cluster, and checkstyle is clean.
> Found by code review of the OM table cache, double buffer flush and startup 
> paths, as part of the TLA+ verification effort under HDDS-15926, on commit 
> a5814528cdc8d3c1b27f259e0db2256b585cb825. 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
(v10.3.26#10030026)

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

Reply via email to