Siyao Meng created HDDS-17022:
---------------------------------

             Summary: RECOVERING container (EC reconstruction target) keeps its 
space reservation after it is closed, deleted or marked unhealthy
                 Key: HDDS-17022
                 URL: https://issues.apache.org/jira/browse/HDDS-17022
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng


h3. Mechanism
Since HDDS-12810 every CreateContainer reserves the container size in 
{{VolumeChoosingPolicy.chooseVolume}}, and {{KeyValueContainer.create}} marks 
the container as holding a reservation, whatever its state. A RECOVERING 
container is writable and its writes consume the reservation through 
{{ContainerData.incrWriteBytes}}, so holding one is expected. But 
{{ContainerData.setState}} gives the reservation back only when the old state 
is OPEN. A RECOVERING container therefore keeps the unwritten part of its 
reservation when it moves to CLOSING and CLOSED, when it is force deleted 
({{markContainerForDelete}} sets DELETED) and when the stale recovering 
scrubber marks it UNHEALTHY. Nothing recomputes {{HddsVolume.committedBytes}} 
while the datanode runs. This is a regression from HDDS-12810 (2.1.0): before 
it, only OPEN containers reserved (in {{ContainerSet.addContainer}} through 
{{commitSpace}}), so RECOVERING containers held nothing to release.

h3. Trigger
Normal EC reconstruction, no fault: {{ECReconstructionCoordinator}} creates 
RECOVERING containers on the targets and then closes them, or deletes them with 
force after a failed reconstruction.

h3. Impact
Each reconstruction leaves the unwritten part of a container size committed on 
the target volume until restart; a failed one, deleted early, leaves nearly all 
of it. On a datanode that receives many reconstructions (for example after the 
loss of a datanode or disk holding many EC replicas, or repeated reconstruction 
failures), volumes end up refusing new containers and imports while empty, and 
the inflated committed value is reported in the storage report.

h3. Reproduction
[^TestBugMC6RecoveringReservationLeak.java] ({{hadoop-hdds/container-service}}) 
uses a real {{HddsVolume}} with a mocked 1000 MB usage source, 300 MB 
containers, the real {{RoundRobinVolumeChoosingPolicy}} and 
{{KeyValueHandler}}, and sends the CreateContainer (state RECOVERING), 
CloseContainer and forced DeleteContainer requests the EC coordinator sends. A 
closed RECOVERING container keeps {{committed=314572800}}, also after it is 
deleted, while an OPEN control container returns to 0. After three create and 
delete cycles the empty volume has {{committed=943718400}} and a new OPEN 
container gets {{DISK_OUT_OF_SPACE}}. A passing test means the defect is 
present. Not exercised: a real reconstruction on a cluster, and the SCM side.

h3. Patch
[^MC-6-release-reservation-when-leaving-recovering.patch], against 
cce1ca28944886218132f01c0e271c1fd0700078. It also applies to master at 
2536fe9cbc3. Released 2.1.0 and later are affected.

{{ContainerData.setState}} releases when the old state is OPEN or RECOVERING 
({{HddsUtils.isOpenToWriteState}}), and {{ContainerData.commitSpace}} accepts 
the same states, so both sides agree on which containers hold a reservation. 
The release is guarded by the reservation flag, so containers that never 
reserved (imports, containers loaded at restart, DiskBalancer temporary data) 
are not affected, and one change covers close, delete and the scrubber. The 
{{commitSpace}} change has no effect at load ({{ContainerReader}} returns 
before it for RECOVERING containers); it lets a RECOVERING container reserve 
again, for example when a failed state change is rolled back. Covered by the 
new {{TestKeyValueHandler.testRecoveringContainerReleasesReservation}} for the 
three exit paths, which also checks that a RECOVERING container can reserve 
again. It fails without the change, and also with only the {{commitSpace}} half 
(5368709120 still committed after the exit). With it the handler, container, 
reader, scrubber, DiskBalancer, persistence, close command and importer suites 
pass (760 tests) and checkstyle is clean.

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