Siyao Meng created HDDS-16764:
---------------------------------

             Summary: DeleteOpenKeys removes a key hsync'ed after the cleanup 
scan and queues its blocks for deletion, and the next overwrite commit 
terminates the OM
                 Key: HDDS-16764
                 URL: https://issues.apache.org/jira/browse/HDDS-16764
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: MC-7-keep-open-key-hsynced-after-cleanup-selection.patch, 
TestBugMC7ExpiredOpenKeyHsyncRace.java

h3. Mechanism
{{OmMetadataManagerImpl.getExpiredOpenKeys}} reads hsync state from RocksDB 
only, and {{OMOpenKeysDeleteRequest.updateOpenKeyTableCache}} rechecks only 
existence and {{updateID}}. A first hsync that lands between the cleanup scan 
and the {{DeleteOpenKeys}} request therefore loses its open entry while the 
{{fileTable}} entry keeps {{HSYNC_CLIENT_ID}}.

{{OMOpenKeysDeleteResponse}} also moves every block of the deleted open key to 
the deleted table, including the blocks the hsync'ed key table entry references.

The commit requests ({{OMKeyCommitRequestWithFSO.java:206}}, 
{{OMKeyCommitRequest.java:284}}) then dereference the missing open entry 
({{openKeyToDelete.toBuilder()}}) without a null check when another client 
commits over that key. {{OMOpenKeysDeleteRequest}} is shared by all bucket 
layouts, so this is not limited to FSO.

h3. Trigger
# c1 creates a file and keeps it open past 
{{ozone.om.open.key.expire.threshold}} without hsync.
# c2 opens the same path (overwrite of an hsync'ed file is allowed).
# The cleanup scan selects c1's open key.
# c1's first hsync is applied before {{DeleteOpenKeys}}.
# c2 commits.

h3. Impact
c1's hsync is acknowledged but its open entry is deleted and the blocks of the 
hsync'ed data are queued for deletion, with no second writer needed (verified 
up to the deleted table entry; the physical block deletion was not run). c2's 
commit then throws {{NullPointerException}} in apply and 
{{OzoneManagerStateMachine.runCommand}} terminates the OM. The entry is already 
in the Raft log, so the other OMs and a restart would hit it again (argued from 
the deterministic apply path, not run).

h3. Reproduction
PASS, 3 of 3 runs. [^TestBugMC7ExpiredOpenKeyHsyncRace.java] 
({{hadoop-ozone/ozone-manager}}) uses only client calls against one real OM 
with its real {{OpenKeyCleanupService}}, with the service interval and expiry 
threshold shortened by configuration. With defaults the window is milliseconds, 
once per service run, on a file open for seven days.

h3. Suggested fix
* Close the race in {{OMOpenKeysDeleteRequest.updateOpenKeyTableCache}}: never 
delete an open key that a committed key still points to through 
{{HSYNC_CLIENT_ID}}.
* Null check {{openKeyToDelete}} in both commit requests. This removes the 
termination and lets a cluster already in this state recover.

HDDS-16732 reports the same cleanup site enqueueing a live key's blocks on a 
versioned bucket; different trigger, same missing key table check.

h3. Patch
[^MC-7-keep-open-key-hsynced-after-cleanup-selection.patch], against 
20e6a1c0f6039248373efd3eb1d7f2afcf7f1535. It also applies to master at 
9c6bee807c8.

Two changes:
* {{OMOpenKeysDeleteRequest}}: under the bucket lock, an open key whose own 
client ID is in its {{HSYNC_CLIENT_ID}} is looked up in the key table and 
skipped when the committed key still carries that client ID. A deleted key has 
no entry and an overwritten key carries another writer's ID or none, so both 
are still deleted. The extra read only happens for open keys that are hsync'ed, 
which the scan normally never submits.
* {{OMKeyCommitRequest}} and {{OMKeyCommitRequestWithFSO}}: the 
{{OVERWRITTEN_HSYNC_KEY}} marking is skipped when the open entry is missing, so 
an OM that already holds such a key no longer terminates on the next overwrite 
commit.

Covered by {{testDeleteHsyncedKeys}} in the existing 
{{TestOMOpenKeysDeleteRequest}} (both layouts) and 
{{testOverwriteHsyncKeyWithoutOpenKey}} in {{TestOMKeyCommitRequest}} 
(inherited by the FSO variant). Without the change the first fails because the 
hsync'ed open key is gone, and the second with the {{NullPointerException}} on 
{{openKeyToDelete}}. The open key delete, key commit and cleanup service unit 
suites (82 tests) and {{TestHSync}} (42 tests) pass, checkstyle is clean. The 
cross OM and restart behaviour is argued from the apply path, not run.

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