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

Siyao Meng updated HDDS-17018:
------------------------------
    Attachment: MC-2-reject-container-on-failed-volume.patch

> Container imported or created while its datanode volume fails stays 
> registered on the failed volume and is reported to SCM as live
> ----------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: HDDS-17018
>                 URL: https://issues.apache.org/jira/browse/HDDS-17018
>             Project: Apache Ozone
>          Issue Type: Bug
>            Reporter: Siyao Meng
>            Priority: Major
>         Attachments: MC-2-reject-container-on-failed-volume.patch, 
> TestBugMC2ContainerOnFailedVolume.java
>
>
> h3. Mechanism
> When a data volume fails, {{MutableVolumeSet.handleVolumeFailures}} marks it 
> FAILED and, after releasing its lock, the failed volume listener 
> {{ContainerSet.handleVolumeFailures}} removes the volume's containers in a 
> single pass and sends a full container report. Neither the push import path 
> ({{ContainerImporter.importContainer}}) nor the create path 
> ({{KeyValueHandler.handleCreateContainer}}) checks the volume state before 
> {{ContainerSet.addContainer}}. A container added after that pass stays in the 
> container set on the FAILED volume and is never removed.
> h3. Trigger
> # A push replication of a container reaches the datanode and the target 
> volume is chosen on the first chunk, or a CreateContainer is processed and 
> {{KeyValueContainer.create}} has picked its volume.
> # Before the container is added to the set, the volume checker fails that 
> volume and the listener sweep runs.
> # The import or create then adds the container.
> No timing aid is needed for the import path when the volume fails while the 
> tarball is still streaming. That holds while the per disk DB of a failed 
> volume can be reopened (HDDS-17017): with that fixed, an import whose volume 
> fails while streaming is refused when it loads the container metadata into 
> the DB, and the window left for import is between that load and 
> {{addContainer}}. The create route keeps its window between 
> {{KeyValueContainer.create}} and {{addContainer}}; the create test of the 
> reproduction still passes with only that other fix applied.
> h3. Impact
> * The datanode keeps the container on a volume it has declared failed and 
> includes it in its container reports, so SCM counts it as a live replica.
> * It stays until the datanode restarts. Delete (exercised in the 
> reproduction) and unhealthy handling (argued from the code) skip containers 
> on failed volumes.
> h3. Reproduction
> PASS, deterministic, unmodified source. 
> [^TestBugMC2ContainerOnFailedVolume.java] ({{hadoop-hdds/container-service}}) 
> uses two schema V3 data volumes, the real {{handleVolumeFailures}} and the 
> real container sweep. The first test streams a push import through 
> {{SendContainerRequestHandler}} and fails the target volume between the first 
> and the last chunk: the imported container is in the set and in the container 
> report, while the container that was on the same volume before the failure 
> was swept. The second test sends a CreateContainer through the handler and 
> fails the volume while a spy holds {{addContainer}}: the create returns 
> SUCCESS and the container is in the set and in the report. A passing test 
> means the defect is present.
> h3. Patch
> [^MC-2-reject-container-on-failed-volume.patch], against 
> cce1ca28944886218132f01c0e271c1fd0700078. It also applies to master at 
> 2536fe9cbc3.
> {{ContainerSet.addContainer}} checks the volume state right after the 
> container is put into the map. If the volume has failed it removes that entry 
> again and throws {{StorageContainerException}} with 
> {{CONTAINER_INTERNAL_ERROR}}, before the container is counted on the volume. 
> The check has to come after the put: the volume is marked FAILED before the 
> sweep, so either the sweep sees the container or this check sees the failure. 
> One check in the shared method covers import, create and 
> {{addContainerByOverwriteMissingContainer}}, and their callers already handle 
> an exception from it. For a rejected create {{KeyValueHandler}} returns 
> {{CONTAINER_INTERNAL_ERROR}}; an explicit CreateContainer sent through 
> {{HddsDispatcher.dispatch}} then ends in the dispatcher's existing 
> {{NullPointerException}} for a failed create, as for any other failed 
> explicit create (from the code, not run).
> Covered by {{testAddContainerOnFailedVolumeIsRejected}} in the existing 
> {{TestContainerSet}}. Without the change it fails because the container on 
> the failed volume is added. {{TestKeyValueHandler.testDeleteContainer}} added 
> a container after failing its volume and now adds it first. With the change 
> the container set, handler, dispatcher, import, container reader, disk 
> balancer and replication suites pass (one replication metrics test also fails 
> intermittently without the change), 
> {{TestDatanodeHddsVolumeFailureDetection}} and 
> {{TestDatanodeHddsVolumeFailureToleration}} on a mini cluster pass, and 
> checkstyle is clean. With the patch both tests of the reproduction fail 
> because the container is not registered; {{KeyValueHandler}} returns 
> {{CONTAINER_INTERNAL_ERROR}} for the create.
> On rejection the container directory stays on the failed disk, as for any 
> other failure of {{addContainer}}.
> Not covered, left for a follow up: the disk balancer moves a container with 
> {{ContainerSet.updateContainer}}, which has no volume check, and then marks 
> the source replica for delete. If the destination volume fails after its 
> sweep and before that swap, the moved container is registered on the failed 
> volume and the healthy source copy is marked deleted (from the code, not 
> run). A check there has to restore the old mapping and interacts with a 
> concurrent sweep, so it is not a one line addition.
> Found by TLA+ model checking and code review of the datanode volume and 
> per-disk RocksDB lifecycle paths 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