Chi-Hsuan Huang created HDDS-16177:
--------------------------------------

             Summary: MPU overwrite on a versioned bucket leaks the previous 
key's blocks
                 Key: HDDS-16177
                 URL: https://issues.apache.org/jira/browse/HDDS-16177
             Project: Apache Ozone
          Issue Type: Bug
          Components: OM
            Reporter: Chi-Hsuan Huang


h3. Problem
Completing a multipart upload that overwrites an existing key on a 
versioning\-enabled bucket drops the previous key's blocks from OM metadata 
without queueing them for deletion. The blocks stay on the datanodes and are 
never reclaimed. The previous key's space is also never credited back to the 
bucket.

h3. Root cause
In {{S3MultipartUploadCompleteRequest.java:331\-343}}, the previous key\-table 
entry is only cleaned up when versioning is off:

{code}
OmKeyInfo keyToDelete = 
omMetadataManager.getKeyTable\(getBucketLayout\(\)\).get\(dbOzoneKey\);
boolean isNamespaceUpdate = false;
if \(keyToDelete \!= null && \!omBucketInfo.getIsVersionEnabled\(\)\) {
  RepeatedOmKeyInfo oldKeyVersionsToDelete = getOldVersionsToCleanUp\(
      keyToDelete, omBucketInfo.getObjectID\(\), trxnLogIndex\);
  allKeyInfoToRemove.addAll\(oldKeyVersionsToDelete.getOmKeyInfoList\(\)\);
  usedBytesDiff \-= keyToDelete.getReplicatedSize\(\);
} else {
  checkBucketQuotaInNamespace\(omBucketInfo, 1L\);
  omBucketInfo.incrUsedNamespace\(1L\);
  isNamespaceUpdate = true;
}
{code}

{{keyToDelete}} is referenced nowhere else in the request, so on a 
versioning\-enabled bucket its blocks never reach {{allKeyInfoToRemove}}, which 
is what the response writes to the deleted table.

At the same time the blocks are removed from the metadata. {{getOmKeyInfo}} 
starts from the existing key\-table entry and calls 
{{omKeyInfo.updateLocationInfoList\(partLocationInfos, true, true\)}}, and 
{{OmKeyInfo.updateLocationInfoList}} \({{OmKeyInfo.java:307\-335}}\) performs 
{{removeBlocks\(latestVersion\)}} followed by {{addAll\(latestVersion, 
updatedBlockLocations\)}}, replacing the latest version group's blocks with the 
new parts. MPU completion never calls {{addNewVersion}}, as the comment at 
{{S3MultipartUploadCompleteRequest.java:513\-517}} records \("Already a version 
exists, so we should add it as a new version. But now as versioning is not 
supported, just following the commit key approach"\), so the previous content 
is not retained as an older version either.

The result is that the previous key's blocks are referenced by neither the key 
table nor the deleted table.

h3. Impact
* Storage on the datanodes is leaked and cannot be reclaimed, since no deletion 
is ever scheduled for those blocks.
* The bucket's {{usedBytes}} keeps the previous key's charge, so the counter 
stays above what the key table describes.
* Quota repair recounts from the active key table and cannot see the leaked 
blocks, so it cannot detect or correct the discrepancy.
* {{usedNamespace}} is also incremented on every such overwrite without a 
matching key being added.

h3. Reproduction sketch
On a bucket created with versioning enabled, write a key through a multipart 
upload, then overwrite the same key name through a second multipart upload. The 
first upload's blocks remain on the datanodes with no metadata referencing them.

h3. Notes
Established by tracing the request path; not yet reproduced end to end. An 
integration test using the real client would confirm the blocks are neither in 
the deleted table nor referenced by the key table after the second upload.

Found while scoping HDDS\-16127. Pinned source commit 4766aa8609. Analysis 
assisted by AI tooling \(Claude Code, Opus 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