[ 
https://issues.apache.org/jira/browse/HDDS-17013?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Andy Huang reassigned HDDS-17013:
---------------------------------

    Assignee: Andy Huang

> 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
>            Assignee: Andy Huang
>            Priority: Major
>         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