Caideyipi opened a new pull request, #18402:
URL: https://github.com/apache/iotdb/pull/18402

   ## Description
   
   This PR removes two coupled bottlenecks in consensus subscription 
prefetching:
   
   1. Reuse the subscription WAL iterator across prefetch rounds instead of 
reopening it at the current search index on every round.
   2. Let late/duplicate ACK handling share the queue read lock with 
prefetching instead of waiting for the queue write lock.
   
   ### Bottleneck shown by the flame graphs
   
   The four CPU/wall profiles show one server-side bottleneck and its 
client-side amplification.
   
   On the DataNode CPU profile:
   
   - Consensus subscription prefetch accounts for about 79.3% of CPU.
   - The WAL input path under DataInputStream.readFully accounts for about 
60.4%.
   - LZ4_decompress_safe self time is about 32.3%.
   - openReaderAtIndex is about 33.5% cumulative and skipEntries is about 16.2%.
   - Actual low-level disk reads are only a few percent.
   
   This is repeated replay work rather than raw disk throughput. 
tryCatchUpFromWAL() previously reset the iterator before every batch. A 
preceding hasNext() could already have parsed and cached nextReady, but the 
next round closed that iterator, discarded the cached request, reopened the WAL 
at nextExpectedSearchIndex, and repeated reader lookup, skipping, reads, and 
LZ4 decompression.
   
   On the DataNode wall profile, the same expensive prefetch round holds the 
queue read lock. A late/missing ACK then waits for the queue write lock for 
roughly 86% of its RPC wall time.
   
   On the consumer wall profile, the auto-commit RPC waits for that ACK 
response while holding the consumer SynchronizedHandler monitor. A concurrent 
business thread calling commitSync waits for the same monitor for roughly 48% 
of consumer wall time. Thus enabling auto-commit together with manual 
commitSync amplifies the server stall, but the server-side WAL replay and ACK 
lock contention are the root causes addressed here.
   
   The consumer CPU profile also shows Tablet.deserialize at about 49% and 
Tablet.readValuesFromBuffer at about 40%. This PR does not change tablet 
deserialization; the wall profiles show that the larger end-to-end stall is the 
ACK/WAL contention chain above.
   
   ### Design
   
   - Keep ProgressWALIterator and its hasNext() cache alive across normal WAL 
catch-up rounds. refresh() discovers newly visible WAL files without rewinding 
already consumed data.
   - Request an explicit deferred iterator realignment when the realtime 
pending path advances the local cursor independently. Seek, gap recovery, 
memory retry, close, and lifecycle transitions retain their explicit reset 
behavior.
   - While a reset is pending, scheduling reports immediate work without 
calling hasNext() on the stale iterator. The next prefetch round applies the 
reset under the queue lock.
   - Handle late/missing ACKs under the queue read lock. The touched queue/map 
indexes are concurrent containers, event cleanup is idempotently protected by 
the event monitor, and commit-state updates are serialized by the commit-state 
monitor. The read lock still fences seek/close and other lifecycle transitions 
that require the queue write lock.
   
   ### Performance test
   
   ProgressWALIteratorTest#testIteratorReusePerformance is disabled by default 
and can be enabled manually:
   
   ~~~bash
   mvn test -pl iotdb-core/datanode \
     -Dtest=ProgressWALIteratorTest#testIteratorReusePerformance \
     -Diotdb.test.subscription.performance=true
   ~~~
   
   Optional parameters:
   
   - -Diotdb.test.subscription.performance.entries=<count>
   - -Diotdb.test.subscription.performance.batch-size=<count>
   
   With 4,096 entries and batches of 64 on the same generated WAL:
   
   ~~~text
   reopen=647.872 ms, reuse=18.445 ms, speedup=35.12x
   ~~~
   
   A final repeat after recompilation produced:
   
   ~~~text
   reopen=606.675 ms, reuse=16.032 ms, speedup=37.84x
   ~~~
   
   The benchmark isolates the repeated reopen/skip/decompress behavior from 
consumer RPC and tablet deserialization.
   
   ### Tests
   
   - ConsensusPrefetchingQueueTest#testLateAckDoesNotWaitForPrefetchReadLock
   - ConsensusPrefetchingQueueTest#testWalCatchUpReusesIteratorAcrossRounds
   - 
ConsensusPrefetchingQueueTest#testPendingCursorAdvanceDefersWalIteratorRealignment
   - Full ConsensusPrefetchingQueueTest and ProgressWALIteratorTest: 31 tests, 
0 failures, 0 errors, 1 performance test skipped by default
   - Spotless and Checkstyle passed
   
   <hr>
   
   This PR has:
   
   - [x] been self-reviewed.
     - [x] concurrent read
     - [x] concurrent write
     - [x] concurrent read and write
   - [x] added comments explaining the why and the intent of the code wherever 
it would not be obvious for an unfamiliar reader.
   - [x] added unit tests to cover the new code paths.
   
   <hr>
   
   ##### Key changed/added classes
   
   - ConsensusPrefetchingQueue
   - ConsensusPrefetchingQueueTest
   - ProgressWALIteratorTest


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to