Siyao Meng created HDDS-17023:
---------------------------------

             Summary: Failed container push releases the receiver's space 
reservation twice when the sender's half close arrives after the error
                 Key: HDDS-17023
                 URL: https://issues.apache.org/jira/browse/HDDS-17023
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: MC-7-release-push-import-reservation-once.patch, 
TestBugMC7PushImportDoubleRelease.java

h3. Mechanism
{{SendContainerRequestHandler}} reserves space on the first message of a push 
and releases it in the {{finally}} of {{onError}} and of {{onCompleted}} (both 
added by HDDS-12235 in 2.1.0). {{onError}} closes the output and sets it to 
null but keeps {{volume}} and {{spaceToReserve}}. When the sender's half close 
is delivered after that, {{onCompleted}} returns early ("Received container 
without any parts") and its {{finally}} releases the same reservation again. 
gRPC still delivers a half close it has already received after the server 
called {{onError}}: the reproduction below, with a real Netty server and the 
production sender, logs {{onError}} and then {{onCompleted}} on the same call 
and ends with the reservation released twice. A later {{onNext}} that fails on 
the closed output would release again (argued, not observed: gRPC dropped 
queued messages in the runs).

h3. Trigger
An I/O error on the receiving datanode while it handles the last message of a 
push, for example when opening or writing the tarball in the untar directory: a 
container whose tarball fits in one chunk, or an error such as ENOSPC on the 
last chunk of a large one. {{GrpcOutputStream.close}} sends the last chunk and 
the half close without waiting. A failure on an earlier chunk releases once: 
the sender sees the error before it reaches the half close.

h3. Impact
Each such failure lowers the volume's committed bytes by one extra reservation, 
which is {{2 x bytesUsed}} of the pushed container as sent by the source (the 
default {{2 x ozone.scm.container.size}} if the source sends no size), until 
restart. The value can go below zero and is reported to SCM. 
{{AvailableSpaceFilter}} then overstates the free space by that amount (argued 
from the arithmetic, not run).

h3. Reproduction
[^TestBugMC7PushImportDoubleRelease.java] ({{hadoop-hdds/container-service}}) 
pushes with the production {{GrpcContainerUploader}} to a real Netty 
{{ReplicationServer}} with a real {{ContainerImporter}} and 
{{MutableVolumeSet}}. The fault is a real filesystem error: the receiver's 
untar directory is read only. The sender has no local container, so no size is 
sent and the default reservation applies (2 MB with a 1 MB container size). It 
waits for the two handler log lines, then reads {{committed after failed 
push=-2097152 reported to SCM=-2097152}}. Passed in 3 of 3 runs. A passing test 
means the defect is present. Not exercised: a failure on the last chunk of a 
large container, a real EIO or ENOSPC.

h3. Patch
[^MC-7-release-push-import-reservation-once.patch], against 
cce1ca28944886218132f01c0e271c1fd0700078. It also applies to master at 
2536fe9cbc3. Released 2.1.0 to 2.2.1 are affected.

Both {{finally}} blocks call one helper that releases the reservation and sets 
{{spaceToReserve}} to 0, so later callbacks of the same stream release nothing. 
Covered by the existing 
{{TestSendContainerRequestHandler.testSpaceReservedAndReleasedWhenOnNextFails}},
 which now also delivers {{onCompleted}} after the error; it fails without the 
change (for example {{expected: 0L but was: -10737418240L}}). With it the 
replication package passes (131 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