Siyao Meng created HDDS-17018:
---------------------------------

             Summary: 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
         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