Siyao Meng created HDDS-17025:
---------------------------------

             Summary: Container push with a negative size reserves a negative 
amount on the receiving volume that is never released
                 Key: HDDS-17025
                 URL: https://issues.apache.org/jira/browse/HDDS-17025
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: 
CR-12-reserve-default-space-for-negative-import-size.patch, 
TestBugCR12NegativeImportSizeReservation.java

h3. Mechanism
On the first message of a push, {{SendContainerRequestHandler.onNext}} reserves 
{{ContainerImporter.getSpaceToReserve(size)}}, which is {{2 * size}} for any 
size the sender gives. For a negative size this is a negative reservation: 
{{AvailableSpaceFilter}} accepts any volume for it and the volume's committed 
bytes go down. Both releases (in {{onError}} and {{onCompleted}}) only release 
a positive reservation, so the negative amount stays when the stream ends, 
whether the import succeeds, fails or the sender aborts.

h3. Trigger
The source sends {{getBytesUsed()}} of its container as {{size}} 
({{GrpcContainerUploader.startUpload}}, since HDDS-12998 in 2.1.0). On 2.1.0 to 
2.1.2 that value is negative as soon as block deletion drives the in memory 
counter below zero, the case HDDS-15110 addresses; its in memory clamp is only 
in 2.2.0 and later. On 2.2.x and master the persisted {{CONTAINER_BYTES_USED}} 
counter is still written as {{bytes - releasedBytes}} without a lower bound in 
{{KeyValueContainerData.updateAndCommitDBCounters}} and loaded back without 
one, so after a restart the source sends a negative size again. HDDS-15110's 
{{TestContainerReplication.pushSucceedsWhenSourceBytesUsedIsNegative}} pushes 
such a size and only checks that the import succeeds. I did not drive a real 
counter negative.

h3. Impact
Each such push lowers the receiving volume's committed bytes by {{2 * |size|}} 
until restart. The volume then reports less committed space than it holds, also 
to SCM, and admits containers and imports it has no room for (argued from 
{{AvailableSpaceFilter}}, not run here).

h3. Reproduction
[^TestBugCR12NegativeImportSizeReservation.java] 
({{hadoop-hdds/container-service}}) feeds two streams with {{size = 
-1234567890}} into {{SendContainerRequestHandler}}, the observer the 
replication service hands each message to, with a real {{ContainerImporter}}, 
{{MutableVolumeSet}} and volume choosing policy. After the first stream (failed 
import) {{committed=-2469135780}}, after the second (aborted by the sender) 
{{committed=-4938271560 reported to SCM=-4938271560}}. A passing test means the 
defect is present. The negative size is supplied directly, not produced by a 
real source.

h3. Patch
[^CR-12-reserve-default-space-for-negative-import-size.patch], against 
cce1ca28944886218132f01c0e271c1fd0700078. It also applies to master at 
2536fe9cbc3. Released 2.1.0 to 2.2.1 are affected.

{{ContainerImporter.getSpaceToReserve}} uses the given size only when it is not 
negative and {{2 * size}} does not overflow, and otherwise reserves the default 
replication space, as for a source that sends no size. The reservation is then 
never negative and the existing releases free it. Rejecting the push instead 
would make the container permanently unreplicable from that source, since its 
counter stays wrong until repaired, so the receiver falls back to the 
reservation it already uses when no size is sent. A push with a negative size 
can now be refused with {{DISK_OUT_OF_SPACE}} on a volume without the default 
free space, where it is always accepted today. Covered by the new 
{{TestSendContainerRequestHandler.testInvalidSizeReservesDefaultSpace}} for a 
negative size and for {{Long.MAX_VALUE}}, which fails without the change (for 
example {{expected: <10737418240> but was: <-2469135780>}}). With it the 
replication package passes (133 tests) and checkstyle is clean. Clamping the 
persisted counter on the source is a separate change and is not included.

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