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

Siyao Meng updated HDDS-16755:
------------------------------
    Status: Patch Available  (was: Open)

> Uncommitted block cleanup can delete a block that a snapshot of an hsync'ed 
> key still references
> ------------------------------------------------------------------------------------------------
>
>                 Key: HDDS-16755
>                 URL: https://issues.apache.org/jira/browse/HDDS-16755
>             Project: Apache Ozone
>          Issue Type: Bug
>          Components: Ozone Manager, Snapshot
>            Reporter: Siyao Meng
>            Assignee: Siyao Meng
>            Priority: Major
>              Labels: pull-request-available
>
> h3. Mechanism
> When a key commit leaves out a block that was allocated to the open key, 
> {{OMKeyRequest.wrapUncommittedBlocksAsPseudoKey}} 
> ({{OMKeyRequest.java:1227}}) wraps the block in a pseudo key with object ID 
> {{OBJECT_ID_RECLAIM_BLOCKS}} (0) and the commit puts it in {{deletedTable}}. 
> The marker means "reclaim unconditionally", on the assumption that such a 
> block was never visible to a snapshot.
> That assumption does not hold for an hsync'ed key. An hsync publishes the 
> block list to {{keyTable}}, so a snapshot taken after the hsync lists those 
> blocks. If a later hsync or the final commit leaves one of them out, it is 
> released as an uncommitted block when the key is closed.
> {{ReclaimableKeyFilter.getPreviousSnapshotKeyInfo}} 
> ({{ReclaimableKeyFilter.java:166}}) treats an object ID mismatch with the 
> previous snapshot's key as "not the same key" and returns reclaimable. The 
> pseudo key always mismatches, so {{KeyDeletingService}} sends the block to 
> SCM for deletion while a snapshot still references it.
> h3. Trigger
> # A writer hsyncs key {{k}} with blocks {{[A, B]}}.
> # Snapshot {{S1}} is created. {{S1}} lists {{k}} with {{[A, B]}}.
> # The writer allocates block {{C}} and commits {{k}} with {{[A, C]}}.
> # The commit finds {{B}} uncommitted and writes a {{deletedTable}} row for it 
> with object ID 0.
> # {{KeyDeletingService}} reclaims the row. {{B}} is deleted from datanodes 
> and reads of {{k}} through {{S1}} fail.
> Variant with two snapshots: between steps 2 and 3 the writer hsyncs {{[A, 
> C]}} and snapshot {{S2}} is created. The previous snapshot {{S2}} then lists 
> {{[A, C]}} and shares no block with the pseudo key, while {{S1}} still 
> references {{B}}.
> The same helper is used by {{OMKeyCommitRequestWithFSO}} ({{:314}}) and 
> {{S3MultipartUploadCommitPartRequest}} ({{:305}}).
> h3. Impact
> Data loss for snapshot reads. The active key is not affected.
> Reproduced on master with a {{TestKeyDeletingService}} case that issues 
> {{openKey}}, {{allocateBlock}}, {{hsyncKey}}, {{createSnapshot}} and 
> {{commitKey}} against OM directly. Whether an unmodified Ozone client can 
> drop a block it has already hsync'ed has not been established, so this is 
> currently an OM side hazard: OM accepts the commit and deletes snapshot data.
> h3. Proposed fix
> The pseudo key must keep both the object ID and the hsync metadata of the key.
> * Object ID: stop overwriting it with 0, so the filter finds the key's 
> version in the previous snapshot.
> * Hsync metadata: the pseudo key is already built before {{HSYNC_CLIENT_ID}} 
> is removed ({{OMKeyCommitRequest.java:380}} and {{:394}}, 
> {{OMKeyCommitRequestWithFSO.java:314}} and {{:338}}). That ordering must be 
> kept and documented at the helper.
> With both, {{SnapshotUtils.isBlockLocationInfoSame}} 
> ({{SnapshotUtils.java:303}}) retains the row while the previous snapshot 
> holds an hsync'ed version of the key with the same object ID. This also 
> covers the two snapshot variant, where the previous snapshot does not list 
> the block itself. The row becomes reclaimable once no previous snapshot holds 
> an hsync'ed version of the key.
> The {{deletedTable}} row name is derived from the transaction index, not the 
> object ID, so rows do not collide.
> Known side effects:
> * While the previous snapshot holds an hsync'ed version of the key, the whole 
> pseudo row is retained, including blocks no snapshot references.
> * A retained pseudo row adds the snapshot key's size to that snapshot's 
> exclusive size even though the active key still shares blocks with it.
> The then unused {{OBJECT_ID_RECLAIM_BLOCKS}} constant and its exemption in 
> {{WithObjectID.validate}} can be removed in a follow up.
> h3. Test
> * New 
> {{TestKeyDeletingService.testUncommittedBlockOfHsyncedKeyRetainedBySnapshot}} 
> runs the two snapshot variant. The {{deletedTable}} row for {{B}} must stay 
> while {{S2}} is the previous snapshot and after {{S2}} is deleted, and must 
> be purged only after {{S1}} is deleted.
> * 
> {{TestOMKeyCommitRequest.testValidateAndUpdateCacheOnOverwriteWithUncommittedBlocks}}
>  asserts that the pseudo key keeps a non zero object ID. It runs for OBS and 
> FSO.
> Both fail on master without the fix. The new {{TestKeyDeletingService}} case 
> also fails with the object ID kept but the hsync metadata removed from the 
> pseudo key.
> Line numbers are from master at e62603cf3ca.



--
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