Siyao Meng created HDDS-17013:
---------------------------------

             Summary: Tarball and untar directory of a container import 
interrupted by a datanode crash are never removed from the volume
                 Key: HDDS-17013
                 URL: https://issues.apache.org/jira/browse/HDDS-17013
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: 
CR-16-remove-interrupted-import-leftovers-at-startup.patch, 
TestBugCR16StaleImportTarball.java

h3. Mechanism
A pushed container is written by {{SendContainerRequestHandler}} to 
{{<volume>/tmp/container-copy/container-<id>-<random UUID>.tar}}, then untarred 
into {{container-copy/<id>}} by {{TarContainerPacker.unpackContainerData}}, 
called from {{KeyValueContainer.importContainerData}}. The tarball is deleted 
only in process ({{onError}}, the failure path of {{onCompleted}}, the 
{{finally}} of {{ContainerImporter.importContainer}}); the untar directory is 
deleted by the failure path of {{KeyValueContainer.importContainerData}} or 
moved into place on success. Nothing else lists or cleans {{container-copy}}: 
the volume initialization ({{HddsVolume.createTmpDirs}}) cleans only 
directories under {{hdds/<clusterID>/tmp}}, container loading at startup does 
not look at it, and a graceful shutdown does not either. A later import of the 
same container to the same volume replaces the untar directory, but because the 
tarball name contains a random UUID it never replaces the old tarball.

Released versions are affected as well (checked 2.2.1): there the pull 
replication path also writes {{container-<id>-<UUID>.tar}} into the same 
directory.

h3. Trigger
# SCM has a container pushed to a datanode, and the datanode has received part 
of it (or is untarring it).
# The datanode process dies before the import finishes (kill, OOM kill or exit 
on OOM, power loss).
# The datanode restarts. The leftovers stay.

h3. Impact
Each import in flight at a crash leaves up to about the size of the container 
on that volume (more if the untar had started) until an operator deletes it by 
hand. The loss accumulates: a datanode that crashes repeatedly while receiving 
re-replication, for example during a decommission or after another datanode 
failed, adds more on each crash, and nothing in Ozone reports where the space 
went. The space is outside the container accounting, so it lowers the free 
space the datanode reports (argued from the code, not measured). No data loss.

h3. Reproduction
PASS, deterministic, unmodified source. [^TestBugCR16StaleImportTarball.java] 
({{hadoop-hdds/container-service}}) models a datanode lifetime by what startup 
does per data volume ({{StorageVolumeUtil.checkVolume}}, then 
{{ContainerReader}} as in {{OzoneContainer.buildContainerSet}}). It pushes 
three 1MB parts of a container through the real {{SendContainerRequestHandler}} 
and abandons the handler without {{onError}} or {{onCompleted}}, as after a 
kill. The tarball is still there after a restart, after a failed push of the 
same container, and after a volume set shutdown and a second restart. A passing 
test means the defect is present.

h3. Patch
[^CR-16-remove-interrupted-import-leftovers-at-startup.patch], against 
cce1ca28944886218132f01c0e271c1fd0700078. It also applies to master at 
2536fe9cbc3.

{{ContainerReader}} empties the volume's {{container-copy}} directory before it 
loads the containers of the volume at datanode startup (the {{shouldDelete}} 
path; the debug CLI does not use it). Nothing can be importing at that point, 
since the replication server is started later in {{OzoneContainer.start}}. The 
cleanup is not done in {{HddsVolume.createTmpDirs}}, because that runs on every 
version handshake with SCM, including after a reregister, when an import may be 
in progress. An exception during the cleanup only logs a warning and does not 
stop the volume from loading. The cleanup also removes files left in that 
directory by older versions after an upgrade.

Covered by {{testLeftoverContainerImportFilesCleared}} in the existing 
{{TestContainerReader}}, which also checks that a run without {{shouldDelete}} 
leaves the files alone. Without the change it fails for all container test 
versions with the directory not empty; with it the replication and container 
loading suites ({{TestSendContainerRequestHandler}}, {{TestContainerImporter}}, 
{{TestGrpcReplicationService}}, {{TestContainerReader}}, 
{{TestOzoneContainer}}, 139 tests) pass and checkstyle is clean. With the patch 
the reproduction fails because the tarball is gone after the first restart.

HDDS-8162 (under HDDS-8161) would move {{container-copy}} under 
{{hdds/<clusterID>/tmp}}, which HDDS-8161 proposes to clean on restart; this 
patch does not move the directory and does not conflict with that.

Found by code review of the datanode volume and per-disk RocksDB lifecycle 
paths, as part of the TLA+ verification effort under HDDS-15926, on commit 
cce1ca28944886218132f01c0e271c1fd0700078. 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