This is an automated email from the ASF dual-hosted git repository.

jt2594838 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new a7e508cf458 [Subscription] Index consensus ACK progress by writer 
(#18835)
a7e508cf458 is described below

commit a7e508cf458857cc4afa1e661220d099559b67f9
Author: Caideyipi <[email protected]>
AuthorDate: Wed Oct 14 11:20:48 2026 +0800

    [Subscription] Index consensus ACK progress by writer (#18835)
---
 .../ConsensusSubscriptionCommitManager.java        | 93 ++++++++++++++--------
 .../ConsensusSubscriptionCommitStateTest.java      | 20 +++++
 2 files changed, 79 insertions(+), 34 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionCommitManager.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionCommitManager.java
index 7d5edd25828..14e50ae44d0 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionCommitManager.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionCommitManager.java
@@ -67,13 +67,16 @@ import java.nio.file.StandardCopyOption;
 import java.nio.file.StandardOpenOption;
 import java.util.Base64;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.NavigableSet;
 import java.util.Objects;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutorService;
 
@@ -1617,8 +1620,14 @@ public class ConsensusSubscriptionCommitManager {
     /** Tracks dispatched but not-yet-committed events by writer-local slot. */
     private final Map<ProgressSlot, ProgressKey> outstandingKeys = new 
ConcurrentHashMap<>();
 
-    /** Tracks committed dispatched entries that cannot yet advance the 
frontier because of gaps. */
-    private final Map<ProgressSlot, ProgressKey> committedPendingKeys = new 
LinkedHashMap<>();
+    // Exact ACK lookup uses the slot map above. Successor lookup must not 
scan the entire backlog
+    // on every ACK or every step of a contiguous advance. Both indexes use 
ProgressKey's existing
+    // physical-time/node/sequence ordering and are guarded by the same state 
monitor as the maps.
+    private final Map<Integer, NavigableSet<ProgressKey>> 
outstandingKeysByWriter = new HashMap<>();
+
+    /** Tracks ACKed entries blocked by earlier outstanding progress from the 
same writer. */
+    private final Map<Integer, NavigableSet<ProgressKey>> 
committedPendingKeysByWriter =
+        new HashMap<>();
 
     public ConsensusSubscriptionCommitState(
         final String regionId, final SubscriptionConsensusProgress progress) {
@@ -1675,6 +1684,10 @@ public class ConsensusSubscriptionCommitManager {
       final ProgressSlot slot = ProgressSlot.from(key);
       synchronized (this) {
         final ProgressKey previous = outstandingKeys.put(slot, key);
+        if (Objects.nonNull(previous)) {
+          removeFromWriterIndex(outstandingKeysByWriter, previous);
+        }
+        addToWriterIndex(outstandingKeysByWriter, key);
         if (Objects.nonNull(previous) && !previous.equals(key)) {
           LOGGER.warn(
               DataNodePipeMessages
@@ -1720,7 +1733,7 @@ public class ConsensusSubscriptionCommitManager {
       final ProgressKey key = new ProgressKey(writerId, writerProgress);
 
       synchronized (this) {
-        final ProgressKey recordedKey = 
outstandingKeys.remove(ProgressSlot.from(key));
+        final ProgressKey recordedKey = removeOutstandingKey(key);
         if (recordedKey == null) {
           if (recentlyCommittedKeys.contains(key)) {
             LOGGER.debug(
@@ -1781,7 +1794,7 @@ public class ConsensusSubscriptionCommitManager {
           return CommitOperationResult.handledWithoutAdvance();
         }
 
-        final ProgressKey outstandingKey = 
outstandingKeys.remove(ProgressSlot.from(incomingKey));
+        final ProgressKey outstandingKey = removeOutstandingKey(incomingKey);
         if (Objects.isNull(outstandingKey)) {
           // Late or duplicate ACKs are reported by the queue or summarized by 
the receiver. Logging
           // every missing mapping at WARN would amplify one commit request by 
its context count.
@@ -1809,7 +1822,8 @@ public class ConsensusSubscriptionCommitManager {
     public void resetForSeek(final RegionProgress regionProgress) {
       synchronized (this) {
         outstandingKeys.clear();
-        committedPendingKeys.clear();
+        outstandingKeysByWriter.clear();
+        committedPendingKeysByWriter.clear();
         recentlyCommittedKeys.clear();
         committedWriterPositions.clear();
         if (Objects.nonNull(regionProgress)) {
@@ -1861,10 +1875,10 @@ public class ConsensusSubscriptionCommitManager {
     }
 
     private void stageCommittedAndAdvance(final ProgressKey key) {
-      committedPendingKeys.put(ProgressSlot.from(key), key);
+      addToWriterIndex(committedPendingKeysByWriter, key);
       final WriterId writerId = key.toWriterId(regionId);
       if (Objects.isNull(writerId)) {
-        committedPendingKeys.remove(ProgressSlot.from(key));
+        removeCommittedPendingKey(key);
         return;
       }
       ProgressKey current =
@@ -1878,7 +1892,7 @@ public class ConsensusSubscriptionCommitManager {
         if (Objects.nonNull(nextOutstanding) && 
nextOutstanding.compareTo(nextCommitted) < 0) {
           return;
         }
-        committedPendingKeys.remove(ProgressSlot.from(nextCommitted));
+        removeCommittedPendingKey(nextCommitted);
         advanceCommitted(nextCommitted);
         current = nextCommitted;
       }
@@ -1897,39 +1911,50 @@ public class ConsensusSubscriptionCommitManager {
     }
 
     private ProgressKey findNextCommittedKey(final WriterId writerId, final 
ProgressKey current) {
-      ProgressKey next = null;
-      for (final ProgressKey candidate : committedPendingKeys.values()) {
-        if (!sameWriter(writerId, candidate)) {
-          continue;
-        }
-        if (candidate.compareTo(current) <= 0) {
-          continue;
-        }
-        if (Objects.isNull(next) || candidate.compareTo(next) < 0) {
-          next = candidate;
-        }
-      }
-      return next;
+      return findNextKey(committedPendingKeysByWriter, writerId, current);
     }
 
     private ProgressKey findNextOutstandingKey(final WriterId writerId, final 
ProgressKey current) {
-      ProgressKey next = null;
-      for (final ProgressKey candidate : outstandingKeys.values()) {
-        if (!sameWriter(writerId, candidate)) {
-          continue;
-        }
-        if (candidate.compareTo(current) <= 0) {
-          continue;
-        }
-        if (Objects.isNull(next) || candidate.compareTo(next) < 0) {
-          next = candidate;
+      return findNextKey(outstandingKeysByWriter, writerId, current);
+    }
+
+    private ProgressKey removeOutstandingKey(final ProgressKey key) {
+      final ProgressKey removed = 
outstandingKeys.remove(ProgressSlot.from(key));
+      if (Objects.nonNull(removed)) {
+        removeFromWriterIndex(outstandingKeysByWriter, removed);
+      }
+      return removed;
+    }
+
+    private void removeCommittedPendingKey(final ProgressKey key) {
+      removeFromWriterIndex(committedPendingKeysByWriter, key);
+    }
+
+    private static void addToWriterIndex(
+        final Map<Integer, NavigableSet<ProgressKey>> index, final ProgressKey 
key) {
+      index.computeIfAbsent(key.writerNodeId, ignored -> new 
TreeSet<>()).add(key);
+    }
+
+    private static void removeFromWriterIndex(
+        final Map<Integer, NavigableSet<ProgressKey>> index, final ProgressKey 
key) {
+      final NavigableSet<ProgressKey> writerKeys = index.get(key.writerNodeId);
+      if (Objects.nonNull(writerKeys)) {
+        writerKeys.remove(key);
+        if (writerKeys.isEmpty()) {
+          index.remove(key.writerNodeId);
         }
       }
-      return next;
     }
 
-    private boolean sameWriter(final WriterId writerId, final ProgressKey key) 
{
-      return Objects.nonNull(writerId) && writerId.getNodeId() == 
key.writerNodeId;
+    private static ProgressKey findNextKey(
+        final Map<Integer, NavigableSet<ProgressKey>> index,
+        final WriterId writerId,
+        final ProgressKey current) {
+      if (Objects.isNull(writerId)) {
+        return null;
+      }
+      final NavigableSet<ProgressKey> writerKeys = 
index.get(writerId.getNodeId());
+      return Objects.nonNull(writerKeys) ? writerKeys.higher(current) : null;
     }
 
     private CommitOperationResult buildCommitOperationResult(
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionCommitStateTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionCommitStateTest.java
index 75aa3032307..24778e2c175 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionCommitStateTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionCommitStateTest.java
@@ -86,6 +86,26 @@ public class ConsensusSubscriptionCommitStateTest {
     assertEquals(7, state.getCommittedWriterNodeId());
   }
 
+  @Test
+  public void testReverseAckWithLargeBacklogAdvancesToLatestProgress() {
+    final WriterId writerId = new WriterId("1_2", 7);
+    final ConsensusSubscriptionCommitManager.ConsensusSubscriptionCommitState 
state =
+        new 
ConsensusSubscriptionCommitManager.ConsensusSubscriptionCommitState(
+            "1_2", new SubscriptionConsensusProgress());
+    final int backlogSize = 2048;
+
+    for (int localSeq = 0; localSeq < backlogSize; localSeq++) {
+      state.recordMapping(writerId, new WriterProgress(1_000L + localSeq, 
localSeq));
+    }
+    for (int localSeq = backlogSize - 1; localSeq >= 0; localSeq--) {
+      assertTrue(state.commit(writerId, new WriterProgress(1_000L + localSeq, 
localSeq)));
+    }
+
+    assertEquals(
+        new WriterProgress(1_000L + backlogSize - 1, backlogSize - 1L),
+        state.getCommittedRegionProgress().getWriterPositions().get(writerId));
+  }
+
   @Test
   public void testSerializeDeserializeWriterProgress() throws Exception {
     final ConsensusSubscriptionCommitManager.ConsensusSubscriptionCommitState 
state =

Reply via email to