Siyao Meng created HDDS-16796:
---------------------------------

             Summary: deletedTable entries inherit the committed flag from 
their key info, so the purge can release pending deletion usage that was never 
added
                 Key: HDDS-16796
                 URL: https://issues.apache.org/jira/browse/HDDS-16796
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: CR-3-set-committed-deleted-flag-authoritatively.patch

h3. Mechanism
A {{deletedTable}} entry carries a flag that records whether the replicated 
size of that key info and one namespace unit were added to the bucket's 
{{snapshotUsedBytes}} and {{snapshotUsedNamespace}} when the key left the key 
table ({{OmKeyInfo.isDeletedKeyCommitted}}, set with 
{{OmKeyInfo.withCommittedKeyDeletedFlag}}). When the entry is purged, 
{{KeyManagerImpl.getPendingDeletionKeys}}, {{KeyDeletingService}} and 
{{OMKeyPurgeRequest.updateBucketSize}} subtract the size and the namespace unit 
only for key infos that carry the flag (HDDS-13756).

The flag is part of the {{OmKeyInfo}} that each producer moves into 
{{deletedTable}}, and the producers do not all set it from their own intent. 
The shared mover {{OmUtils.prepareKeyForDelete}} keeps whatever the key info it 
is handed carries, and two sites that build the entry without that mover, 
{{S3MultipartUploadCompleteResponse}} for the key and the unused parts of a 
completed upload and {{OMKeyRequest.wrapUncommittedBlocksAsPseudoKey}} for the 
pseudo key of uncommitted blocks, do not clear it either. Only 
{{AbstractOMKeyDeleteResponse}} decides the flag for the entry it writes.

A producer that does not add a key's size to the pending deletion counters can 
therefore write an entry that the purge treats as charged, and the purge then 
subtracts a size and a namespace unit that were never added. The counters have 
no lower bound, so they go negative and {{OmBucketInfo.getTotalBucketSpace}} 
and {{getTotalBucketNamespace}}, which the quota checks in {{OMKeyRequest}} 
compare against, fall below the bucket's real usage. Nothing recomputes them 
automatically.

h3. Trigger
# A key info that already carries the committed flag value is moved into 
{{deletedTable}} by a producer that does not add its size to the bucket's 
pending deletion counters. Run through the shared mover and through 
{{S3MultipartUploadCompleteResponse}} (an unused part of a completed upload).
# {{KeyDeletingService}} purges the entry.

The other producers that route through the shared mover (multipart abort, an 
overwritten or aborted part, {{FSORepairTool}}) and the uncommitted blocks 
pseudo key reach the same state, from code reading, not run.

By history, not run on older code: the flag and the pending deletion counters 
arrived together with HDDS-13756, so the inheritance is present from the 2.1.0 
release tag onwards. 2.0.0 has neither.

h3. Impact
* After the purge the bucket's {{snapshotUsedBytes}} and 
{{snapshotUsedNamespace}} are lower by the size and the namespace unit of an 
entry that never added them, and go negative for a bucket with nothing else 
pending. In the regression run of the shared mover the entry comes back with 
the flag cleared, and the purge therefore leaves the counters alone.
* The charged usage the quota checks read is then below the bucket's real 
usage, so the bucket admits more than its space and namespace quota allow.
* No key and no data is affected, and the counters stay wrong until an operator 
starts a quota repair ({{ozone repair om quota start}}), which recomputes them 
(code reading, not run).

h3. Reproduction
The two regression tests in the attached patch cover it and fail on unmodified 
source:
* {{TestOmUtils.prepareKeyForDeleteSetsCommittedFlagAuthoritatively}} 
({{hadoop-ozone/common}}): the shared mover must decide the flag of the entry 
it builds, for the three argument form and for both values of the new argument. 
Without the change the three argument form and the {{false}} form both return a 
flagged entry.
* 
{{TestS3MultipartUploadCompleteResponse.testCompleteDoesNotInheritClientCommittedFlag}}
 ({{hadoop-ozone/ozone-manager}}, also in the inherited {{...WithFSO}}): an 
unused part of a completed upload must not produce a flagged entry. Without the 
change it fails with {{expected: <false> but was: <true>}}.

h3. Patch
[^CR-3-set-committed-deleted-flag-authoritatively.patch], against 
a6332fb59879b80615b9233e953d6813d03ae7a8. It also applies to master at 
fa7b58e3901.

{{OmUtils.prepareKeyForDelete}} takes the flag as a new argument and sets it on 
the entry it builds instead of keeping the value of the key info it is handed. 
The three argument form stays and passes {{false}}, so a producer that does not 
charge the counters needs no change. The producers that do charge them pass 
their intent: {{AbstractOMKeyDeleteResponse}} passes the value it already 
computed (both of its overloads), {{OMDirectoriesPurgeResponseWithFSO}} passes 
{{true}} for the sub files of a purged directory instead of flagging the key 
info beforehand. The two sites that build the entry without the mover clear the 
flag explicitly: {{S3MultipartUploadCompleteResponse}} for the key and the 
unused parts it moves, and {{OMKeyRequest.wrapUncommittedBlocksAsPseudoKey}} 
for the pseudo key. {{OMKeyCommitRequest}}, which flags the version it 
overwrites after the move, is unchanged.

So every {{deletedTable}} entry is flagged by the code that writes it, and an 
entry whose source key info carried a value is written with the flag the 
producer intends. Nothing changes for an entry the OM itself flagged, there is 
no wire or protocol change, and entries already in {{deletedTable}} are read as 
before.

With the patch the key delete and purge response suites, the directories purge 
and key purge request suites, the quota repair task suite, the key and part 
commit response suites, the multipart abort, commit part and complete response 
suites and {{TestKeyDeletingService}} pass, and checkstyle on 
{{hadoop-ozone/common}} and {{hadoop-ozone/ozone-manager}} is clean.

One coordination note with HDDS-16793, which is a different defect in the same 
accounting and whose patch is also open for review: that patch pre-sets the 
flag on the shared test fixture {{OMRequestTestUtils.deleteKey}}, which this 
change then overrides, so {{TestQuotaRepairTask}} has two failures with both 
applied. Whichever lands second carries the fixture line over to the four 
argument form of the mover. Verified by running both.

Found by code review of the OM key deletion service and key purge paths, as 
part of the TLA+ verification effort under HDDS-15926, on commit 
a6332fb59879b80615b9233e953d6813d03ae7a8. Checked against HDDS issues and 
apache/ozone pull requests for duplicates before filing. Related but not 
duplicates: HDDS-13756 (introduced the flag and the pending deletion counters), 
HDDS-16793 (quota repair counts entries the purge never releases), HDDS-16176 
(the size formula in the same release path). 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