Siyao Meng created HDDS-16791:
---------------------------------
Summary: PurgeKeys requests that only carry keysToUpdate are
rejected, so a partly reclaimable deleted key is not rewritten and its blocks
are resent to SCM
Key: HDDS-16791
URL: https://issues.apache.org/jira/browse/HDDS-16791
Project: Apache Ozone
Issue Type: Bug
Reporter: Siyao Meng
Attachments:
MC-5-accept-purge-keys-request-with-only-keys-to-update.patch,
TestBugMC5UpdateOnlyPurgeRejected.java
h3. Mechanism
When a {{deletedTable}} entry holds one version that can be reclaimed and one
that a snapshot still needs, {{KeyDeletingService}} sends the reclaimable
blocks to SCM and then puts the entry into {{keysToUpdate}} of a PurgeKeys
request, so that OM rewrites the entry with only the retained version. If
nothing else in that store can be purged, the request has no deleted keys and
no renamed keys. {{OMKeyPurgeRequest.validateAndUpdateCache}} rejects such a
request with {{KEY_DELETION_ERROR}} ("No keys found to be purged or renamed in
the request.") without looking at {{keysToUpdate}}. The entry is not rewritten
and the next run finds the same blocks again.
The check was added by HDDS-11411 for deleted keys and extended to renamed keys
by HDDS-13026. No comment, commit message or test says that a request with only
{{keysToUpdate}} should be refused, and {{OMKeyPurgeResponse}} applies
{{keysToUpdate}} whenever another key shares the request. HDDS-11411 is first
contained in release 2.0.0 (by history, not run on older code).
h3. Trigger
# Create a key and take a snapshot of the bucket.
# Overwrite the key with a commit that leaves an allocated block uncommitted.
The {{deletedTable}} entry now holds the old version, which the snapshot still
references, and the uncommitted blocks, which are reclaimable.
# {{KeyDeletingService}} runs while that entry is the only reclaimable work of
the active object store.
Default settings, no fault. For the active object store the condition ends when
another purgeable key shares the request. Snapshot variant: a second snapshot
is taken after step 2 while the entry still holds both versions. The entry then
sits in the deleted table of the second snapshot and, if that deleted table
holds no fully reclaimable key and no reclaimable rename entry, the request for
that snapshot is rejected in every run for as long as both snapshots exist.
h3. Impact
* The same block is sent to SCM for deletion in every run of the service
(default interval 1m). Run: 3 of 3 runs in both variants, on the OM side with a
recording SCM block client. By code reading SCM creates a new delete
transaction per call and duplicates are detected by transaction ID, not by
block, so every resend is a new SCM transaction and datanode command for a
block that is already deleted. That part was not run against a real SCM or
datanode.
* The updates that follow a successful purge are skipped. In the active store
variant the exclusive size of the snapshot stayed 0 during the rejected runs
and became 268435456 in the first accepted run. In the snapshot variant the
second snapshot is never marked as deep cleaned, so it is scanned again in
every run.
* {{keysReclaimedInInterval}} counts the same block again in every run (1, 2, 3
over three runs).
* No data is lost and no client call fails.
h3. Reproduction
PASS, deterministic, unmodified source.
[^TestBugMC5UpdateOnlyPurgeRejected.java] ({{hadoop-ozone/ozone-manager}})
drives one real OM per test through {{OzoneManagerProtocol}}, with a recording
SCM block client and no datanodes, and calls the service task directly three
times. The first test covers the active object store, including the run in
which an unrelated deleted key makes the request acceptable. The second covers
the snapshot variant. Both print the requests, their responses, the blocks sent
to SCM and the content of the entry for every run. A passing test means the
defect is present.
h3. Patch
[^MC-5-accept-purge-keys-request-with-only-keys-to-update.patch], against
a6332fb59879b80615b9233e953d6813d03ae7a8. It also applies to master at
3a22acfaa21.
{{OMKeyPurgeRequest.validateAndUpdateCache}} rejects a request only when the
deleted keys, the renamed keys and {{keysToUpdate}} are all empty.
{{OMKeyPurgeResponse}} already applies {{keysToUpdate}}, so no other code
change is needed. An accepted request also records the purge transaction, like
any other PurgeKeys request. The change is not gated by a layout feature, like
HDDS-13026 on the same condition. Not tested with OMs of different versions
applying the same log. By code reading, an OM without the change still rejects
such a request, so the entry (and for a snapshot the recorded purge
transaction) can differ between OMs until the entry is purged or its snapshot
is deleted. What remains on the older OM is a reference to blocks that are
already deleted.
Covered by {{testRequestWithOnlyKeysToUpdate}} in the existing
{{TestOMKeyPurgeRequestAndResponse}}, which also checks that a request with
nothing to purge, update or rename is still rejected. Without the change it
fails with "expected: <OK> but was: <KEY_DELETION_ERROR>". With it
{{TestOMKeyPurgeRequestAndResponse}},
{{TestOMDirectoriesPurgeRequestAndResponse}},
{{TestOMSnapshotPurgeRequestAndResponse}}, {{TestKeyDeletingService}} and
{{TestKeyManagerImpl}} pass (98 tests) and checkstyle is clean.
{{TestKeyPurging}} and {{TestSnapshotDeletingServiceIntegrationTest}} on a mini
cluster (9 tests) pass in a run that had this patch and a second, unrelated
change to {{KeyDeletingService}} applied together. With the patch both tests of
the reproduction fail at their first assertion on the response status, and the
output of the first run shows the request accepted, the entry rewritten, the
exclusive size set and the second snapshot deep cleaned. In a variant of the
reproduction without assertions, the second and third run send no block and no
PurgeKeys request.
Not part of the patch: the reclaim metrics are updated whether or not a purge
request was applied. This change removes the case that inflated them in every
run. Other failed purges are still counted and then counted again by the run
that retries them. That is the same kind of issue as HDDS-16437.
Found by TLA+ model checking and code review of the OM key deletion service and
key purge paths under HDDS-15926, on commit
a6332fb59879b80615b9233e953d6813d03ae7a8. Checked against HDDS issues and
apache/ozone pull requests for duplicates before filing. Related but not
duplicates: HDDS-16464 (deep clean flag of a snapshot set too early),
HDDS-16755 (a different defect on the same kind of entry), HDDS-16437. 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]