Siyao Meng created HDDS-17282:
---------------------------------

             Summary: KeyDeletingService reclaims a deleted key record whose 
earlier version group the previous snapshot still references
                 Key: HDDS-17282
                 URL: https://issues.apache.org/jira/browse/HDDS-17282
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: CR-9-keep-deleted-versioned-key-held-by-snapshot.patch

h3. Mechanism
A key record can hold several location version groups. Records written while 
the bucket's {{isVersionEnabled}} flag is true do 
({{OMKeyRequest.prepareFileInfo}} calls {{OmKeyInfo.addNewVersion}} with 
keepOldVersions set), and such a record keeps its groups if the flag is turned 
off later. {{OMKeyDeleteRequest}} moves the whole record, with every group, to 
{{deletedTable}}.

{{ReclaimableKeyFilter.getPreviousSnapshotKeyInfo}} keeps a deleted record only 
if the previous snapshot's copy has the same object ID and 
{{SnapshotUtils.isBlockLocationInfoSame}} is true. That check returns false as 
soon as the two records have a different number of version groups. So for this 
state:
* a {{deletedTable}} entry for object X holds the groups [v0, v1],
* the previous snapshot's copy of X holds [v0], with the same version number 
and the same blocks,

the filter returns the entry as reclaimable, and 
{{KeyManagerImpl.getPendingDeletionKeys}} hands the blocks of every group to 
{{KeyDeletingService}}, including those of v0. By code reading the same happens 
when the previous snapshot holds [v0, v1] and is purged while the next older 
snapshot holds [v0]. The same filter is used for snapshot deep cleaning, 
{{DirectoryDeletingService}} and {{BucketDeletedDataCalculator}}. A record 
written while the flag is false has one version group and is not affected.

The version count check was added with the block comparison in HDDS-8686 
(1.4.0). Before that, a matching object ID was enough to keep the record.

HDDS-10051 was resolved as Won't Fix because object versioning is not intended 
to coexist with Ozone snapshots, and its resolving comment says that validation 
rejecting the combination should be tracked separately. HDDS-16060 proposes 
that exclusion for the new S3 versioning status, and also that reclamation fail 
closed when OM finds a pre existing mixed state. OM does not reject a snapshot 
of a bucket whose {{isVersionEnabled}} flag is true, and the pull request for 
HDDS-16060 checks only the new versioning status. In that state the filter 
deletes blocks that the snapshot still lists, which is data loss and not only 
an accounting error. This report does not ask to support the combination. The 
patch applies the fail closed rule that HDDS-16060 describes to the records 
that exist today.

h3. Impact
* The blocks of v0 are sent to SCM for deletion while the previous snapshot 
still lists them, so reads of X through that snapshot lose their data once the 
datanodes delete the blocks. Run on the OM side: the blocks are selected for 
deletion and sent to a testing SCM. Not run: SCM and datanode deletion and the 
failed snapshot read.

h3. Reproduction
The regression test in the patch, 
{{TestKeyDeletingService.testOverwrittenKeyBlocksReferencedBySnapshotNotReclaimed}},
 builds this state on one OM (deleted record [b0, b1], snapshot copy [b0]) with 
a testing SCM and evaluates the filter for the active object store. On the 
unmodified source the versioned case fails because both blocks are reclaimable 
while the snapshot exists:
{noformat}
TestKeyDeletingService$Normal.testOverwrittenKeyBlocksReferencedBySnapshotNotReclaimed:567
Expecting
  [945169052L, 945169400L]
not to contain
  [945169052L]
but found
  [945169052L]
{noformat}
The case with one version group per record passes with and without the change 
(the old record is a separate entry that matches the snapshot's copy and is 
kept, the newer one is reclaimable). Block IDs are time based and differ per 
run.

h3. Patch
[^CR-9-keep-deleted-versioned-key-held-by-snapshot.patch], against 
b10a7d427bdf83807a7e48a1be507d3b02d5a503. It also applies to master at 
26388a80b37.

{{ReclaimableKeyFilter.getPreviousSnapshotKeyInfo}} now also treats the record 
as present in the previous snapshot when it still holds one of the non empty 
version groups of the snapshot's copy (same version number, same blocks). The 
entry is then kept as a whole until no previous snapshot holds such a group, 
after which it is reclaimed as before. This fails closed: the newer groups of 
the entry wait for the snapshot as well, because the filter decides per key 
record and not per version group. For a record with one version group the 
decision is unchanged. {{SnapshotUtils.isBlockLocationInfoSame}} is not 
changed, because snapshot diff uses it to report a modified key. The same 
lookup feeds the exclusive size: for such an entry the previous snapshot is now 
credited with the size of its copy, which is still a whole key approximation.

With the change the regression test passes, and after the snapshot is deleted 
both blocks are reclaimed. The key deleting, directory deleting and snapshot 
deleting service suites, the reclaimable filter suites, {{TestSnapshotUtils}}, 
{{TestSnapshotDiffManager}} and {{TestBucketDeletedDataCalculator}} pass, and 
on a mini cluster {{TestSnapshotDeletingServiceIntegrationTest}} and 
{{TestKeyPurging}} pass. Checkstyle is clean. Blocks already deleted before the 
patch are not recovered.

Found by code review of the S3 gateway object put and key commit paths, as part 
of the TLA+ verification effort under HDDS-15926, on commit 
b10a7d427bdf83807a7e48a1be507d3b02d5a503. 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