Skip to content

Commit 4516870

Browse files
committed
Fix subscription AT_END replay cursor boundary
1 parent eb0e3f1 commit 4516870

2 files changed

Lines changed: 59 additions & 5 deletions

File tree

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusPrefetchingQueue.java‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,11 +1103,17 @@ && compareWriterProgress(requestProgress, storedWriterProgress) == 0) {
11031103
"resolved first uncovered replayable WAL record");
11041104
}
11051105
return ReplayLocateDecision.atEnd(
1106-
consensusReqReader.getCurrentSearchIndex(),
1106+
nextSearchIndexAfterCurrent(),
11071107
effectiveRecoveryRegionProgress,
11081108
"all locally replayable WAL records are already covered");
11091109
}
11101110

1111+
private long nextSearchIndexAfterCurrent() {
1112+
// The reader reports the last local WAL index, while the subscription cursor is the next
1113+
// index to read. Starting at the last index would replay it and report one spurious WAL gap.
1114+
return consensusReqReader.getCurrentSearchIndex() + 1L;
1115+
}
1116+
11111117
protected ReplayLocateDecision locateReplayStartForRegionProgress(
11121118
final RegionProgress regionProgress, final boolean seekAfter) {
11131119
if (!(consensusReqReader instanceof WALNode)) {
@@ -3079,8 +3085,7 @@ public void seekToBeginning() {
30793085
* Seeks to the current WAL write position. After this, only newly written data will be consumed.
30803086
*/
30813087
public void seekToEnd() {
3082-
seekToResolvedPosition(
3083-
consensusReqReader.getCurrentSearchIndex(), computeTailRegionProgress(), "end");
3088+
seekToResolvedPosition(nextSearchIndexAfterCurrent(), computeTailRegionProgress(), "end");
30843089
}
30853090

30863091
public void seekToRegionProgress(final RegionProgress regionProgress) {

‎iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusPrefetchingQueueTest.java‎

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -612,12 +612,13 @@ public void testReplayStartPreservesUncoveredFollowerEntries() throws Exception
612612
reader.currentSearchIndex = 5L;
613613
final ConsensusPrefetchingQueue.ReplayLocateDecision tailDecision =
614614
queue.scanReplayStartForRequests(
615-
Collections.singletonList(createRequest(-1L, 11L, 101L, 8)).iterator(),
615+
Arrays.asList(createRequest(5L, 10L, 100L, 8), createRequest(-1L, 11L, 101L, 8))
616+
.iterator(),
616617
regionProgress,
617618
true);
618619

619620
assertEquals(ConsensusPrefetchingQueue.ReplayLocateStatus.AT_END, tailDecision.getStatus());
620-
assertEquals(5L, tailDecision.getStartSearchIndex());
621+
assertEquals(6L, tailDecision.getStartSearchIndex());
621622
assertEquals(
622623
committedProgress,
623624
tailDecision.getRecoveryRegionProgress().getWriterPositions().get(formerLeader));
@@ -1129,6 +1130,7 @@ public void testAtEndReplayLookupPreservesRequestedWriterFrontier() throws Excep
11291130
queue.scanReplayStartForRequests(Collections.emptyIterator(), requestedProgress, true);
11301131

11311132
assertEquals(ConsensusPrefetchingQueue.ReplayLocateStatus.AT_END, decision.getStatus());
1133+
assertEquals(6L, decision.getStartSearchIndex());
11321134
assertEquals(requestedProgress, decision.getRecoveryRegionProgress());
11331135
} finally {
11341136
if (queue != null) {
@@ -1138,6 +1140,53 @@ public void testAtEndReplayLookupPreservesRequestedWriterFrontier() throws Excep
11381140
}
11391141
}
11401142

1143+
@Test
1144+
public void testAtEndInitializationHasNoTailGap() throws Exception {
1145+
final String originalSystemDir = IoTDBDescriptor.getInstance().getConfig().getSystemDir();
1146+
final File systemDir = temporaryFolder.newFolder("atEndNoTailGap");
1147+
final File walDirectory = temporaryFolder.newFolder("atEndNoTailGapWal");
1148+
ConsensusPrefetchingQueue queue = null;
1149+
try {
1150+
final DataRegionId regionId = new DataRegionId(7);
1151+
final WALNode walNode = mock(WALNode.class);
1152+
when(walNode.getLogDirectory()).thenReturn(walDirectory);
1153+
when(walNode.getCurrentSearchIndex()).thenReturn(5L);
1154+
final IoTConsensusServerImpl serverImpl = mock(IoTConsensusServerImpl.class);
1155+
when(serverImpl.getConsensusReqReader()).thenReturn(walNode);
1156+
when(serverImpl.getWriterSafeFrontierTracker()).thenReturn(new WriterSafeFrontierTracker());
1157+
final RegionProgress committedProgress =
1158+
new RegionProgress(
1159+
Collections.singletonMap(
1160+
new WriterId(regionId.toString(), 7), new WriterProgress(1000L, 5L)));
1161+
queue =
1162+
new ConsensusPrefetchingQueue(
1163+
"consumerGroup",
1164+
"topic",
1165+
TopicConstant.ORDER_MODE_LEADER_ONLY_VALUE,
1166+
regionId,
1167+
serverImpl,
1168+
new SubscriptionWalRetentionPolicy(
1169+
"topic",
1170+
SubscriptionWalRetentionPolicy.UNBOUNDED,
1171+
SubscriptionWalRetentionPolicy.UNBOUNDED),
1172+
mock(ConsensusLogToTabletConverter.class),
1173+
newCommitManager(systemDir),
1174+
committedProgress,
1175+
6L,
1176+
1L,
1177+
true);
1178+
1179+
assertNull(queue.poll("consumer"));
1180+
assertEquals(6L, queue.getCurrentReadSearchIndex());
1181+
assertEquals(0L, queue.getRawWalGap());
1182+
} finally {
1183+
if (queue != null) {
1184+
queue.close();
1185+
}
1186+
IoTDBDescriptor.getInstance().getConfig().setSystemDir(originalSystemDir);
1187+
}
1188+
}
1189+
11411190
@Test
11421191
public void testAtEndInitializationPreservesPendingQueueRegistrationBoundary() throws Exception {
11431192
final String originalSystemDir = IoTDBDescriptor.getInstance().getConfig().getSystemDir();

0 commit comments

Comments
 (0)