Siyao Meng created HDDS-17368:
---------------------------------

             Summary: MappedBufferManager loses one mmap quota permit each time 
a chunk range is re-read after GC cleared its cached mapping
                 Key: HDDS-17368
                 URL: https://issues.apache.org/jira/browse/HDDS-17368
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: 
MC-1-return-quota-of-replaced-cleared-mapped-buffer.patch, 
TestBugMC1ReplacedClearedEntryLeaksQuota.java

h3. Mechanism
{{MappedBufferManager.computeIfAbsent}} treats an entry whose {{WeakReference}} 
has been cleared by GC like a missing entry: it maps the range again and stores 
the new reference with an unconditional {{mappedBuffers.put}}, discarding the 
cleared reference. The cleared entry still accounted for the permit taken when 
it was mapped, and only the asynchronous cleanup in 
{{MappedBufferManager.getQuota}} returns such permits, for references it 
removes itself with {{remove(key, ref)}}. After the {{put}} the cleared 
reference is no longer in the map, so its permit is never returned.

h3. Trigger
# A datanode with mmap reads enabled 
({{ozone.chunk.read.mapped.buffer.max.count}} > 0, default 0) serves a memory 
mapped read of a chunk range, and the response is released. Reads are memory 
mapped only when the read buffer capacity is above 
{{ozone.chunk.read.mapped.buffer.threshold}} (32KB), so data written with the 
default 16KB per checksum is never mapped; chunks written with checksum type 
NONE or with more than 32KB per checksum, and reads into a single buffer, are.
# GC clears the cached mapping. The entry stays in the map, because the cleanup 
only runs after {{getQuota}} fails.
# The same range (same file, offset and length) is read again.

For such data this is the ordinary pattern of repeated reads; no fault is 
needed.

h3. Impact
Each such re-read permanently lowers the mmap quota of the manager by one, 
until the datanode restarts. Each lost permit replaces one cache entry with 
another, so at least one entry is always left for the cleanup to reclaim, and 
repeated re-reads can wear the quota down to 1 of {{max.count}}. Reads that 
need more pieces than the remaining quota fall back to heap buffers. The 
reproduction below ended at 1 of 4. Data stays correct; the effect is that the 
configured mmap read capacity erodes.

h3. Reproduction
PASS on unmodified source, which means the defect is present. 
[^TestBugMC1ReplacedClearedEntryLeaksQuota.java] 
({{hadoop-hdds/container-service}}) reads one range through 
{{ChunkUtils.readData}} with a manager of capacity 4, lets GC clear the mapping 
and re-reads it three times. Output: quota 3, 2, 1, 0 after the re-reads, quota 
1 of 4 after the cleanup with no live mapping, and a two piece read then 
returns a heap buffer. With the patch the quota stays at 3 and the test fails 
at its first re-read assertion.

h3. Patch
[^MC-1-return-quota-of-replaced-cleared-mapped-buffer.patch], against 
5fed99dab3c3204cd7bf1516074969a38d5eb00d. It also applies to master at 
76b5c6947a2.

When the key still holds a cleared reference, the new reference is installed 
with {{mappedBuffers.replace(key, refer, newRef)}} and the cleared entry's 
permit is returned if the replace succeeds; otherwise the code falls back to 
{{put}} as before. This follows the {{remove(key, ref)}} approach of 
HDDS-15357: either the cleanup removes the cleared reference and returns its 
permit, or the replace does, never both. A failed mapping leaves the cleared 
entry to the cleanup as before.

Covered by {{TestMappedBufferManager#testRemapClearedBufferReturnsItsQuota}}, 
which fails without the change ({{expected: <1> but was: <0>}}). With it, 
{{TestMappedBufferManager}}, {{TestChunkUtils}}, {{TestFilePerBlockStrategy}}, 
{{TestFilePerChunkStrategy}}, {{TestChunkManagerDummyImpl}} and the 
{{TestKeyValueHandler}} suites pass (159 tests), checkstyle is clean.

Found by TLA+ model checking and code review of the datanode memory mapped 
chunk read path under HDDS-15926, on commit 
5fed99dab3c3204cd7bf1516074969a38d5eb00d. 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
(v10.3.26#10030026)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to