This is an automated email from the ASF dual-hosted git repository.
yujun777 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 1c5e867d849 [refactor](stream) Consolidate OLAP table stream partition
state (#70242)
1c5e867d849 is described below
commit 1c5e867d84958f2299d3d20fb2b7b5efbd8bc160
Author: yujun <[email protected]>
AuthorDate: Thu Nov 5 16:40:29 2026 +0800
[refactor](stream) Consolidate OLAP table stream partition state (#70242)
OLAP table streams track consumption separately for each base-table
partition. Each partition can carry a consumed offset, its last
consumption time, and a historical snapshot TSO until initial rows are
consumed.
---
.../doris/catalog/stream/OlapTableStream.java | 120 ++++++++++-----------
.../catalog/stream/OlapTableStreamUpdate.java | 30 +++---
.../stream/OlapTableStreamConsumptionTest.java | 4 -
.../stream/OlapTableStreamSnapshotReadTest.java | 6 +-
.../catalog/stream/OlapTableStreamUpdateTest.java | 77 +++++++++++--
.../stream/TableStreamManagerCleanupTest.java | 54 ++++------
.../InsertIntoTableCommandTableStreamTest.java | 30 ++----
.../TableStreamOffsetTransactionTest.java | 18 ++--
8 files changed, 186 insertions(+), 153 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/catalog/stream/OlapTableStream.java
b/fe/fe-core/src/main/java/org/apache/doris/catalog/stream/OlapTableStream.java
index 48c3ca5a438..9c3deb00e71 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/catalog/stream/OlapTableStream.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/catalog/stream/OlapTableStream.java
@@ -47,14 +47,37 @@ import java.util.function.Predicate;
public class OlapTableStream extends BaseTableStream {
- @SerializedName("po")
- private Map<Long, Long> partitionOffset;
+ @SerializedName("ps")
+ private Map<Long, PartitionStreamState> partitionStates = new HashMap<>();
+
+ // Nullable offset and historicalTso distinguish an unconsumed snapshot
from an incremental baseline.
+ // State is replaced as a whole when a consumption commits.
+ public static class PartitionStreamState {
+ @SerializedName("o")
+ private final Long offset;
+ @SerializedName("ct")
+ private final long consumptionTime;
+ @SerializedName("ht")
+ private final Long historicalTso;
+
+ public PartitionStreamState(Long offset, long consumptionTime, Long
historicalTso) {
+ this.offset = offset;
+ this.consumptionTime = consumptionTime;
+ this.historicalTso = historicalTso;
+ }
- @SerializedName("pct")
- private Map<Long, Long> partitionConsumptionTime;
+ public Long getOffset() {
+ return offset;
+ }
- @SerializedName("hpt")
- private Map<Long, Long> historicalPartitionTSO;
+ public long getConsumptionTime() {
+ return consumptionTime;
+ }
+
+ public Long getHistoricalTso() {
+ return historicalTso;
+ }
+ }
// for persist
public OlapTableStream() {
@@ -64,9 +87,6 @@ public class OlapTableStream extends BaseTableStream {
public OlapTableStream(long id, String streamName, TableIf baseTable) {
super(id, streamName, baseTable);
Preconditions.checkArgument(baseTable instanceof OlapTable);
- this.partitionOffset = new HashMap<>();
- this.partitionConsumptionTime = new HashMap<>();
- this.historicalPartitionTSO = new HashMap<>();
}
public OlapTableStream(String streamName, TableIf baseTable) {
@@ -137,15 +157,13 @@ public class OlapTableStream extends BaseTableStream {
if (!showInitialRows) {
// set partition offset
baseTable.getPartitions()
- .forEach(p -> partitionOffset.put(p.getId(), p.getTso()));
+ .forEach(p -> partitionStates.put(p.getId(), new
PartitionStreamState(p.getTso(), -1, null)));
} else {
baseTable.getPartitions()
.stream()
.filter(p -> p.getVisibleVersion() >
Partition.PARTITION_INIT_VERSION)
- .forEach(p -> {
- historicalPartitionTSO.put(p.getId(),
p.getTso());
- }
- );
+ .forEach(p -> partitionStates.put(p.getId(),
+ new PartitionStreamState(null, -1, p.getTso())));
}
}
@@ -167,13 +185,7 @@ public class OlapTableStream extends BaseTableStream {
if (table.readLockIfExist()) {
try {
for (Partition partition : table.getPartitions()) {
- long partitionId = partition.getId();
- boolean hasOffset =
partitionOffset.containsKey(partitionId);
- appendConsumptionRow(dataBatch, qualifiedDbName, name, id,
partition.getName(), hasOffset,
- hasOffset ? partitionOffset.get(partitionId) : 0,
- hasOffset ? partition.getTso() : 0,
- !hasOffset && partition.hasData(),
- partitionConsumptionTime.getOrDefault(partitionId,
-1L));
+ snapshotPartition(partition).appendRow(dataBatch,
qualifiedDbName, name, id);
}
} finally {
table.readUnlock();
@@ -211,13 +223,14 @@ public class OlapTableStream extends BaseTableStream {
private StreamConsumptionUnitSnapshot snapshotPartition(Partition
partition) {
long partitionId = partition.getId();
- boolean hasOffset = partitionOffset.containsKey(partitionId);
+ PartitionStreamState state = partitionStates.get(partitionId);
+ boolean hasOffset = state != null && state.getOffset() != null;
return new StreamConsumptionUnitSnapshot(
partition.getName(), hasOffset,
- hasOffset ? partitionOffset.get(partitionId) : 0,
+ hasOffset ? state.getOffset() : 0,
hasOffset ? partition.getTso() : 0,
!hasOffset && partition.hasData(),
- partitionConsumptionTime.getOrDefault(partitionId, -1L));
+ hasOffset ? state.getConsumptionTime() : -1);
}
private static void appendConsumptionRow(List<TRow> dataBatch, String
dbName, String streamName,
@@ -270,33 +283,29 @@ public class OlapTableStream extends BaseTableStream {
public boolean hasData(Partition partition) {
// if all available visible data has been consumed, return false
- return (!partitionOffset.containsKey(partition.getId())
- ||
!partitionOffset.get(partition.getId()).equals(partition.getTso()))
+ PartitionStreamState state = partitionStates.get(partition.getId());
+ return (state == null || state.getOffset() == null ||
!state.getOffset().equals(partition.getTso()))
&& partition.hasData();
}
public boolean hasHistoricalData(long partitionId) {
- return historicalPartitionTSO.containsKey(partitionId);
+ PartitionStreamState state = partitionStates.get(partitionId);
+ return state != null && state.getHistoricalTso() != null;
}
public boolean hasConsumedData(long partitionId) {
- // A partition that was empty at stream creation is recorded with the
sentinel offset -1
- // (see initializeLocalOffsets); a real committed TSO is always
positive (its physical part
- // is non-zero). So only a positive recorded offset counts as a real
consumption baseline.
- // This keeps empty partitions out of the snapshot scan instead of
letting them fall back to
- // the live partition TSO and leak post-snapshot rows.
- Long offset = partitionOffset.get(partitionId);
- return offset != null && offset > 0;
+ // An empty partition has the sentinel offset -1; only a positive
committed TSO
+ // counts as a consumption baseline for snapshot reads.
+ PartitionStreamState state = partitionStates.get(partitionId);
+ return state != null && state.getOffset() != null && state.getOffset()
> 0;
}
public Pair<Long, Long> getStreamUpdate(Long partitionId) {
- // if partition has historical data, return <historical tso, current
tso>
- // otherwise, return <current consumed tso, current tso>
- Long left = partitionOffset.get(partitionId);
- if (historicalPartitionTSO.containsKey(partitionId)) {
- left = historicalPartitionTSO.get(partitionId);
- }
- return Pair.of(left,
getBaseTableNullable().getPartition(partitionId).getTso());
+ // Historical data takes precedence over the consumed offset until it
is consumed.
+ PartitionStreamState state = partitionStates.get(partitionId);
+ Long startTso = state == null ? null
+ : state.getHistoricalTso() != null ? state.getHistoricalTso()
: state.getOffset();
+ return Pair.of(startTso,
getBaseTableNullable().getPartition(partitionId).getTso());
}
@Override
@@ -304,19 +313,14 @@ public class OlapTableStream extends BaseTableStream {
throws UserException {
Preconditions.checkArgument(update instanceof OlapTableStreamUpdate);
// check valid
- ((OlapTableStreamUpdate) update).checkPartitionOffset(getDBName(),
getName(), historicalPartitionTSO,
- partitionOffset);
+ ((OlapTableStreamUpdate) update).checkPartitionOffset(getDBName(),
getName(), partitionStates);
}
@Override
public void unprotectedUpdateStreamUpdate(AbstractTableStreamUpdate
update, Long ts) {
Map<Long, Long> next = ((OlapTableStreamUpdate) update).getNext();
for (Map.Entry<Long, Long> entry : next.entrySet()) {
- if (historicalPartitionTSO.containsKey(entry.getKey())) {
- historicalPartitionTSO.remove(entry.getKey());
- }
- partitionOffset.put(entry.getKey(), entry.getValue());
- partitionConsumptionTime.put(entry.getKey(), ts);
+ partitionStates.put(entry.getKey(), new
PartitionStreamState(entry.getValue(), ts, null));
}
}
@@ -324,23 +328,11 @@ public class OlapTableStream extends BaseTableStream {
Preconditions.checkState(isWriteLockHeldByCurrentThread(),
"unprotectedCollectStalePartitionOffsetIds must be called with
write lock held");
Set<Long> stalePartitionIds = new HashSet<>();
- for (Long partitionId : partitionOffset.keySet()) {
+ for (Long partitionId : partitionStates.keySet()) {
if (!validPartitionIds.contains(partitionId)) {
stalePartitionIds.add(partitionId);
}
}
- for (Long partitionId : partitionConsumptionTime.keySet()) {
- if (!validPartitionIds.contains(partitionId)) {
- stalePartitionIds.add(partitionId);
- }
- }
- if (historicalPartitionTSO != null) {
- for (Long partitionId : historicalPartitionTSO.keySet()) {
- if (!validPartitionIds.contains(partitionId)) {
- stalePartitionIds.add(partitionId);
- }
- }
- }
return stalePartitionIds;
}
@@ -348,11 +340,7 @@ public class OlapTableStream extends BaseTableStream {
Preconditions.checkState(isWriteLockHeldByCurrentThread(),
"unprotectedPrunePartitionOffsets must be called with write
lock held");
for (Long partitionId : partitionIds) {
- partitionOffset.remove(partitionId);
- partitionConsumptionTime.remove(partitionId);
- if (historicalPartitionTSO != null) {
- historicalPartitionTSO.remove(partitionId);
- }
+ partitionStates.remove(partitionId);
}
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/catalog/stream/OlapTableStreamUpdate.java
b/fe/fe-core/src/main/java/org/apache/doris/catalog/stream/OlapTableStreamUpdate.java
index 2357ae4b6e3..677def19e89 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/catalog/stream/OlapTableStreamUpdate.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/catalog/stream/OlapTableStreamUpdate.java
@@ -17,6 +17,7 @@
package org.apache.doris.catalog.stream;
+import org.apache.doris.catalog.stream.OlapTableStream.PartitionStreamState;
import org.apache.doris.common.UserException;
import org.apache.doris.transaction.TransactionCommitFailedException;
@@ -59,32 +60,29 @@ public class OlapTableStreamUpdate extends
AbstractTableStreamUpdate {
this.prev.putAll(((OlapTableStreamUpdate) other).getPrev());
}
- public void checkPartitionOffset(String dbName, String streamName,
Map<Long, Long> historicalPartitionTSO,
- Map<Long, Long> partitionOffset)
+ public void checkPartitionOffset(String dbName, String streamName,
Map<Long, PartitionStreamState> partitionStates)
throws UserException {
for (Map.Entry<Long, Long> entry : prev.entrySet()) {
- if (historicalPartitionTSO.containsKey(entry.getKey())) {
- if
(!historicalPartitionTSO.get(entry.getKey()).equals(-entry.getValue())) {
+ PartitionStreamState state = partitionStates.get(entry.getKey());
+ if (state != null && state.getHistoricalTso() != null) {
+ if (!state.getHistoricalTso().equals(-entry.getValue())) {
throw new TransactionCommitFailedException("history offset
not consumed: "
+ dbName + '-' + streamName + '-' + entry.getKey()
+ '-' + entry.getValue()
- + " vs " +
historicalPartitionTSO.get(entry.getKey()));
+ + " vs " + state.getHistoricalTso());
}
- } else if (partitionOffset.containsKey(entry.getKey())
- &&
!partitionOffset.get(entry.getKey()).equals(entry.getValue())) {
+ } else if (state != null && state.getOffset() != null
+ && !state.getOffset().equals(entry.getValue())) {
throw new TransactionCommitFailedException("target offset
already consumed: "
+ dbName + '-' + streamName + '-' + entry.getKey() +
'-' + entry.getValue()
- + " vs " + partitionOffset.get(entry.getKey()));
+ + " vs " + state.getOffset());
}
}
for (Map.Entry<Long, Long> entry : next.entrySet()) {
- if (partitionOffset.containsKey(entry.getKey())) {
- if (!prev.containsKey(entry.getKey())) {
- throw new TransactionCommitFailedException(
- "previous version missing for partition=" +
entry.getKey() + ", db=" + dbName
- + ", stream=" + streamName);
- }
- } else {
- // the new partition id may not be updated in time, so key in
table stream not exist is also valid
+ PartitionStreamState state = partitionStates.get(entry.getKey());
+ if (state != null && state.getOffset() != null &&
!prev.containsKey(entry.getKey())) {
+ throw new TransactionCommitFailedException(
+ "previous version missing for partition=" +
entry.getKey() + ", db=" + dbName
+ + ", stream=" + streamName);
}
}
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/OlapTableStreamConsumptionTest.java
b/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/OlapTableStreamConsumptionTest.java
index 5b34e8854ba..f27d8e4cc63 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/OlapTableStreamConsumptionTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/OlapTableStreamConsumptionTest.java
@@ -19,7 +19,6 @@ package org.apache.doris.catalog.stream;
import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.Partition;
-import org.apache.doris.common.jmockit.Deencapsulation;
import org.apache.doris.thrift.TRow;
import org.junit.jupiter.api.Assertions;
@@ -27,7 +26,6 @@ import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -55,8 +53,6 @@ public class OlapTableStreamConsumptionTest {
OlapTableStream stream = Mockito.spy(new OlapTableStream());
Mockito.doReturn(table).when(stream).getBaseTableNullable();
- Deencapsulation.setField(stream, "partitionOffset", new HashMap<Long,
Long>());
- Deencapsulation.setField(stream, "partitionConsumptionTime", new
HashMap<Long, Long>());
List<TRow> rows = new ArrayList<>();
stream.fillTableStreamConsumptionInfo(rows, unit -> {
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/OlapTableStreamSnapshotReadTest.java
b/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/OlapTableStreamSnapshotReadTest.java
index 5fa29bb37c2..1abb876b918 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/OlapTableStreamSnapshotReadTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/OlapTableStreamSnapshotReadTest.java
@@ -20,6 +20,7 @@ package org.apache.doris.catalog.stream;
import org.apache.doris.catalog.KeysType;
import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.Partition;
+import org.apache.doris.catalog.stream.OlapTableStream.PartitionStreamState;
import org.apache.doris.common.jmockit.Deencapsulation;
import com.google.common.collect.Lists;
@@ -124,8 +125,9 @@ public class OlapTableStreamSnapshotReadTest {
Mockito.doReturn(table).when(stream).getBaseTableNullable();
// The offsets have to be in place before the wrapper is built: it
reads them into the map the read of
// a snapshot is built from.
- Deencapsulation.setField(stream, "partitionOffset", new
HashMap<>(offsets));
- Deencapsulation.setField(stream, "historicalPartitionTSO", new
HashMap<Long, Long>());
+ Map<Long, PartitionStreamState> partitionStates = new HashMap<>();
+ offsets.forEach((pid, offset) -> partitionStates.put(pid, new
PartitionStreamState(offset, -1, null)));
+ Deencapsulation.setField(stream, "partitionStates", partitionStates);
return new Fixture(stream, new OlapTableStreamWrapper(stream, table,
Lists.newArrayList(byId.keySet())));
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/OlapTableStreamUpdateTest.java
b/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/OlapTableStreamUpdateTest.java
index ae6ba8d52a3..3fa8a78f767 100755
---
a/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/OlapTableStreamUpdateTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/OlapTableStreamUpdateTest.java
@@ -17,34 +17,41 @@
package org.apache.doris.catalog.stream;
+import org.apache.doris.catalog.stream.OlapTableStream.PartitionStreamState;
+import org.apache.doris.common.jmockit.Deencapsulation;
import org.apache.doris.transaction.TransactionCommitFailedException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
public class OlapTableStreamUpdateTest {
@Test
public void testCheckPartitionOffsetForHistoricalConsumeOk() throws
Exception {
- Map<Long, Long> historicalPartitionTSO = Collections.singletonMap(1L,
100L);
- Map<Long, Long> partitionOffset = Collections.emptyMap();
+ Map<Long, PartitionStreamState> partitionStates =
+ Collections.singletonMap(1L, new PartitionStreamState(null,
-1, 100L));
// history partition prev offset is encoded as negative (see
StreamConsumptionInfoExtractor)
Map<Long, Long> prev = Collections.singletonMap(1L, -100L);
Map<Long, Long> next = Collections.emptyMap();
OlapTableStreamUpdate update = new OlapTableStreamUpdate(prev, next);
- update.checkPartitionOffset("test_db", "s1", historicalPartitionTSO,
partitionOffset);
+ update.checkPartitionOffset("test_db", "s1", partitionStates);
}
@Test
public void testCheckPartitionOffsetForHistoricalConsumeConflict() {
- Map<Long, Long> historicalPartitionTSO = Collections.singletonMap(1L,
99L);
- Map<Long, Long> partitionOffset = Collections.emptyMap();
+ Map<Long, PartitionStreamState> partitionStates =
+ Collections.singletonMap(1L, new PartitionStreamState(null,
-1, 99L));
// history partition prev offset is encoded as negative; -(-100)=100
!= 99 -> conflict
Map<Long, Long> prev = Collections.singletonMap(1L, -100L);
@@ -52,7 +59,65 @@ public class OlapTableStreamUpdateTest {
OlapTableStreamUpdate update = new OlapTableStreamUpdate(prev, next);
TransactionCommitFailedException exception =
Assertions.assertThrows(TransactionCommitFailedException.class,
- () -> update.checkPartitionOffset("test_db", "s1",
historicalPartitionTSO, partitionOffset));
+ () -> update.checkPartitionOffset("test_db", "s1",
partitionStates));
Assertions.assertTrue(exception.getMessage().contains("history offset
not consumed"));
}
+
+ @Test
+ public void testIncrementalOffsetValidation() throws Exception {
+ Map<Long, PartitionStreamState> states = Map.of(1L, new
PartitionStreamState(100L, 10, null));
+ new OlapTableStreamUpdate(Map.of(1L, 100L), Map.of(1L, 200L))
+ .checkPartitionOffset("test_db", "s1", states);
+ Assertions.assertThrows(TransactionCommitFailedException.class,
+ () -> new OlapTableStreamUpdate(Map.of(1L, 99L), Map.of(1L,
200L))
+ .checkPartitionOffset("test_db", "s1", states));
+ Assertions.assertThrows(TransactionCommitFailedException.class,
+ () -> new OlapTableStreamUpdate(Collections.emptyMap(),
Map.of(1L, 200L))
+ .checkPartitionOffset("test_db", "s1", states));
+ // A newly added partition has no recorded offset and can be consumed
without prev.
+ new OlapTableStreamUpdate(Collections.emptyMap(), Map.of(2L, 200L))
+ .checkPartitionOffset("test_db", "s1", states);
+ // A history-only state must not be mistaken for an incremental offset.
+ new OlapTableStreamUpdate(Collections.emptyMap(), Map.of(2L, 200L))
+ .checkPartitionOffset("test_db", "s1", Map.of(2L, new
PartitionStreamState(null, -1, 100L)));
+ }
+
+ @Test
+ public void testPartitionStatePersistenceAndConsumption() throws Exception
{
+ OlapTableStream stream = new OlapTableStream();
+ Map<Long, PartitionStreamState> states = new HashMap<>();
+ states.put(1L, new PartitionStreamState(null, -1, 100L));
+ states.put(2L, new PartitionStreamState(200L, 20, null));
+ states.put(3L, new PartitionStreamState(-1L, -1, null));
+ Deencapsulation.setField(stream, "partitionStates", states);
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ stream.write(new DataOutputStream(bytes));
+ OlapTableStream restored = OlapTableStream.read(new DataInputStream(
+ new ByteArrayInputStream(bytes.toByteArray())));
+ Assertions.assertTrue(restored.hasHistoricalData(1L));
+ Assertions.assertFalse(restored.hasConsumedData(1L));
+ Assertions.assertTrue(restored.hasConsumedData(2L));
+ Assertions.assertFalse(restored.hasConsumedData(3L));
+ Assertions.assertFalse(restored.hasConsumedData(4L));
+
+ Map<Long, PartitionStreamState> restoredStates =
Deencapsulation.getField(restored, "partitionStates");
+ Assertions.assertNull(restoredStates.get(1L).getOffset());
+ Assertions.assertEquals(100L,
restoredStates.get(1L).getHistoricalTso());
+ Assertions.assertEquals(20,
restoredStates.get(2L).getConsumptionTime());
+ restored.writeLock();
+ try {
+ OlapTableStreamUpdate update = new
OlapTableStreamUpdate(Map.of(1L, -100L), Map.of(1L, 150L));
+ update.checkPartitionOffset("test_db", "s1", restoredStates);
+ restored.unprotectedUpdateStreamUpdate(update, 30L);
+ Assertions.assertFalse(restored.hasHistoricalData(1L));
+ Assertions.assertEquals(150L, restoredStates.get(1L).getOffset());
+ Assertions.assertEquals(30,
restoredStates.get(1L).getConsumptionTime());
+ Assertions.assertEquals(Set.of(1L, 3L),
+
restored.unprotectedCollectStalePartitionOffsetIds(Set.of(2L)));
+ restored.unprotectedPrunePartitionOffsets(Set.of(1L, 3L));
+ Assertions.assertEquals(Set.of(2L), restoredStates.keySet());
+ } finally {
+ restored.writeUnlock();
+ }
+ }
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/TableStreamManagerCleanupTest.java
b/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/TableStreamManagerCleanupTest.java
index 511374e9cc2..a0c24c3ac7e 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/TableStreamManagerCleanupTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/catalog/stream/TableStreamManagerCleanupTest.java
@@ -20,6 +20,7 @@ package org.apache.doris.catalog.stream;
import org.apache.doris.catalog.Database;
import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.OlapTable;
+import org.apache.doris.catalog.stream.OlapTableStream.PartitionStreamState;
import org.apache.doris.cloud.proto.Cloud;
import org.apache.doris.common.Config;
import org.apache.doris.common.FeConstants;
@@ -32,6 +33,7 @@ import org.apache.doris.persist.DropInfo;
import org.apache.doris.persist.EditLog;
import org.apache.doris.persist.RecoverInfo;
import org.apache.doris.persist.TableStreamCleanupInfo;
+import org.apache.doris.persist.gson.GsonUtils;
import org.apache.doris.utframe.TestWithFeService;
import org.junit.jupiter.api.Assertions;
@@ -174,8 +176,8 @@ public class TableStreamManagerCleanupTest extends
TestWithFeService {
Assertions.assertTrue(executor.awaitTermination(10,
TimeUnit.SECONDS));
}
- Map<Long, Long> leaderOffsets = new HashMap<>(
- Deencapsulation.getField(context.stream, "partitionOffset"));
+ String leaderStates = GsonUtils.GSON.toJson(
+ Deencapsulation.<Map<Long,
PartitionStreamState>>getField(context.stream, "partitionStates"));
setPartitionState(context.stream, keptPartitionId, removedPartitionId);
for (Object journal : journalOrder) {
if (journal instanceof OlapTableStreamUpdate) {
@@ -186,8 +188,9 @@ public class TableStreamManagerCleanupTest extends
TestWithFeService {
}
}
- Map<Long, Long> replayedOffsets =
Deencapsulation.getField(context.stream, "partitionOffset");
- Assertions.assertEquals(leaderOffsets, replayedOffsets);
+ Map<Long, PartitionStreamState> replayedStates =
+ Deencapsulation.getField(context.stream, "partitionStates");
+ Assertions.assertEquals(leaderStates,
GsonUtils.GSON.toJson(replayedStates));
}
@Test
@@ -249,8 +252,8 @@ public class TableStreamManagerCleanupTest extends
TestWithFeService {
}
}
- Map<Long, Long> leaderOffsets = new HashMap<>(
- Deencapsulation.getField(context.stream, "partitionOffset"));
+ String leaderStates = GsonUtils.GSON.toJson(
+ Deencapsulation.<Map<Long,
PartitionStreamState>>getField(context.stream, "partitionStates"));
setPartitionState(context.stream, keptPartitionId, removedPartitionId);
Database db = (Database)
Env.getCurrentInternalCatalog().getDbOrMetaException("test_stream_cleanup");
for (Object journal : journalOrder) {
@@ -266,9 +269,10 @@ public class TableStreamManagerCleanupTest extends
TestWithFeService {
}
}
- Map<Long, Long> replayedOffsets =
Deencapsulation.getField(context.stream, "partitionOffset");
- Assertions.assertEquals(leaderOffsets, replayedOffsets);
- Assertions.assertTrue(replayedOffsets.containsKey(removedPartitionId));
+ Map<Long, PartitionStreamState> replayedStates =
+ Deencapsulation.getField(context.stream, "partitionStates");
+ Assertions.assertEquals(leaderStates,
GsonUtils.GSON.toJson(replayedStates));
+ Assertions.assertTrue(replayedStates.containsKey(removedPartitionId));
}
@Test
@@ -389,32 +393,20 @@ public class TableStreamManagerCleanupTest extends
TestWithFeService {
}
private void setPartitionState(OlapTableStream stream, long
keptPartitionId, long removedPartitionId) {
- Map<Long, Long> partitionOffset = new HashMap<>();
- partitionOffset.put(keptPartitionId, 11L);
- partitionOffset.put(removedPartitionId, 22L);
- Map<Long, Long> partitionConsumptionTime = new HashMap<>();
- partitionConsumptionTime.put(keptPartitionId, 111L);
- partitionConsumptionTime.put(removedPartitionId, 222L);
- Map<Long, Long> historicalPartitionTSO = new HashMap<>();
- historicalPartitionTSO.put(keptPartitionId, 1001L);
- historicalPartitionTSO.put(removedPartitionId, 2002L);
- Deencapsulation.setField(stream, "partitionOffset", partitionOffset);
- Deencapsulation.setField(stream, "partitionConsumptionTime",
partitionConsumptionTime);
- Deencapsulation.setField(stream, "historicalPartitionTSO",
historicalPartitionTSO);
+ Map<Long, PartitionStreamState> partitionStates = new HashMap<>();
+ partitionStates.put(keptPartitionId, new PartitionStreamState(11L,
111L, 1001L));
+ partitionStates.put(removedPartitionId, new PartitionStreamState(22L,
222L, 2002L));
+ Deencapsulation.setField(stream, "partitionStates", partitionStates);
}
private void assertPartitionState(OlapTableStream stream, long
keptPartitionId, long removedPartitionId,
boolean removedExpected) {
- Map<Long, Long> partitionOffset = Deencapsulation.getField(stream,
"partitionOffset");
- Map<Long, Long> partitionConsumptionTime =
Deencapsulation.getField(stream, "partitionConsumptionTime");
- Map<Long, Long> historicalPartitionTSO =
Deencapsulation.getField(stream, "historicalPartitionTSO");
-
- Assertions.assertTrue(partitionOffset.containsKey(keptPartitionId));
-
Assertions.assertTrue(partitionConsumptionTime.containsKey(keptPartitionId));
-
Assertions.assertTrue(historicalPartitionTSO.containsKey(keptPartitionId));
- Assertions.assertEquals(!removedExpected,
partitionOffset.containsKey(removedPartitionId));
- Assertions.assertEquals(!removedExpected,
partitionConsumptionTime.containsKey(removedPartitionId));
- Assertions.assertEquals(!removedExpected,
historicalPartitionTSO.containsKey(removedPartitionId));
+ Map<Long, PartitionStreamState> partitionStates =
Deencapsulation.getField(stream, "partitionStates");
+ PartitionStreamState kept = partitionStates.get(keptPartitionId);
+ Assertions.assertEquals(11L, kept.getOffset());
+ Assertions.assertEquals(111L, kept.getConsumptionTime());
+ Assertions.assertEquals(1001L, kept.getHistoricalTso());
+ Assertions.assertEquals(!removedExpected,
partitionStates.containsKey(removedPartitionId));
}
private static void await(BooleanSupplier condition) throws
InterruptedException {
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertIntoTableCommandTableStreamTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertIntoTableCommandTableStreamTest.java
index 725107dcf67..3e8a4d05156 100755
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertIntoTableCommandTableStreamTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertIntoTableCommandTableStreamTest.java
@@ -30,6 +30,7 @@ import org.apache.doris.catalog.Tablet;
import org.apache.doris.catalog.stream.AbstractTableStreamUpdate;
import org.apache.doris.catalog.stream.CloudOlapTableStreamUpdate;
import org.apache.doris.catalog.stream.OlapTableStream;
+import org.apache.doris.catalog.stream.OlapTableStream.PartitionStreamState;
import org.apache.doris.catalog.stream.OlapTableStreamUpdate;
import org.apache.doris.catalog.stream.OlapTableStreamWrapper;
import org.apache.doris.catalog.stream.TableStreamUpdateInfo;
@@ -549,12 +550,9 @@ public class InsertIntoTableCommandTableStreamTest extends
TestWithFeService {
Assertions.assertFalse(stream.hasHistoricalData(pid),
"no historical offset should be present after advance
without prior history");
}
- // partitionConsumptionTime is private; verify via reflection on the
same field name.
- @SuppressWarnings("unchecked")
- Map<Long, Long> consumptionTime = (Map<Long, Long>)
Deencapsulation.getField(stream,
- "partitionConsumptionTime");
+ Map<Long, PartitionStreamState> partitionStates =
Deencapsulation.getField(stream, "partitionStates");
for (Long pid : next.keySet()) {
- Assertions.assertEquals(Long.valueOf(ts), consumptionTime.get(pid),
+ Assertions.assertEquals(Long.valueOf(ts),
partitionStates.get(pid).getConsumptionTime(),
"partitionConsumptionTime must be recorded with the commit
ts");
}
}
@@ -571,9 +569,7 @@ public class InsertIntoTableCommandTableStreamTest extends
TestWithFeService {
+ "properties('show_initial_rows' = 'false')");
OlapTableStream stream = (OlapTableStream)
db.getTableOrMetaException("s_history");
- @SuppressWarnings("unchecked")
- Map<Long, Long> historicalPartitionTSO = (Map<Long, Long>)
Deencapsulation.getField(stream,
- "historicalPartitionTSO");
+ Map<Long, PartitionStreamState> partitionStates =
Deencapsulation.getField(stream, "partitionStates");
Map<Long, Long> prev = new HashMap<>();
Map<Long, Long> next = new HashMap<>();
@@ -582,7 +578,8 @@ public class InsertIntoTableCommandTableStreamTest extends
TestWithFeService {
long pid = partition.getId();
long histTso = 8000L + seed;
long nextTso = 9999L + seed;
- historicalPartitionTSO.put(pid, histTso);
+ partitionStates.put(pid, new PartitionStreamState(
+ partitionStates.get(pid).getOffset(), -1, histTso));
prev.put(pid, histTso);
next.put(pid, nextTso);
seed++;
@@ -597,18 +594,15 @@ public class InsertIntoTableCommandTableStreamTest
extends TestWithFeService {
baseTable.writeUnlock();
}
- @SuppressWarnings("unchecked")
- Map<Long, Long> consumptionTime = (Map<Long, Long>)
Deencapsulation.getField(stream,
- "partitionConsumptionTime");
for (Map.Entry<Long, Long> entry : next.entrySet()) {
long pid = entry.getKey();
Assertions.assertFalse(stream.hasHistoricalData(pid),
"historicalPartitionTSO must be cleared after commit");
- Assertions.assertFalse(historicalPartitionTSO.containsKey(pid),
- "historicalPartitionTSO must be cleared after commit");
+ Assertions.assertNull(partitionStates.get(pid).getHistoricalTso(),
+ "historical TSO must be cleared after commit");
Assertions.assertEquals(entry.getValue(),
stream.getStreamUpdate(pid).first,
"partitionOffset must be advanced to update.next after
history consumed");
- Assertions.assertEquals(Long.valueOf(ts), consumptionTime.get(pid),
+ Assertions.assertEquals(Long.valueOf(ts),
partitionStates.get(pid).getConsumptionTime(),
"partitionConsumptionTime must be recorded with the commit
ts");
}
}
@@ -625,9 +619,7 @@ public class InsertIntoTableCommandTableStreamTest extends
TestWithFeService {
// s1 was created with show_initial_rows=true. Seed
historicalPartitionTSO for one
// partition to also cover the history branch.
OlapTableStream stream = (OlapTableStream)
db.getTableOrMetaException("s1");
- @SuppressWarnings("unchecked")
- Map<Long, Long> historicalPartitionTSO = (Map<Long, Long>)
Deencapsulation.getField(stream,
- "historicalPartitionTSO");
+ Map<Long, PartitionStreamState> partitionStates =
Deencapsulation.getField(stream, "partitionStates");
// Bump partition versions so partition.getTso() differs from any
initial seed and
// is a meaningful upper bound. Pass tso explicitly (third arg) to
simulate a
@@ -650,7 +642,7 @@ public class InsertIntoTableCommandTableStreamTest extends
TestWithFeService {
Partition historyPartition =
baseTable.getPartitions().iterator().next();
long historyPid = historyPartition.getId();
long historyTso = 6789L;
- historicalPartitionTSO.put(historyPid, historyTso);
+ partitionStates.put(historyPid, new PartitionStreamState(null, -1,
historyTso));
String sql = "insert into test_stream.tbl_target select * from
test_stream.s1";
LogicalPlan logicalPlan = parser.parseSingle(sql);
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/transaction/TableStreamOffsetTransactionTest.java
b/fe/fe-core/src/test/java/org/apache/doris/transaction/TableStreamOffsetTransactionTest.java
index 320b49ff9f3..b11d930f331 100755
---
a/fe/fe-core/src/test/java/org/apache/doris/transaction/TableStreamOffsetTransactionTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/transaction/TableStreamOffsetTransactionTest.java
@@ -21,6 +21,7 @@ import org.apache.doris.catalog.Database;
import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.stream.OlapTableStream;
+import org.apache.doris.catalog.stream.OlapTableStream.PartitionStreamState;
import org.apache.doris.catalog.stream.OlapTableStreamUpdate;
import org.apache.doris.catalog.stream.TableStreamUpdateInfo;
import org.apache.doris.common.Config;
@@ -92,8 +93,10 @@ public class TableStreamOffsetTransactionTest extends
TestWithFeService {
}
// committed offset must be advanced to the historical TSO snapshot
captured at creation
Map<Long, Long> expectedPartitionOffset = new
HashMap<>(historicalPartitionTSO);
- Deencapsulation.setField(stream, "historicalPartitionTSO",
historicalPartitionTSO);
- Deencapsulation.setField(stream, "partitionOffset", new HashMap<Long,
Long>());
+ Map<Long, PartitionStreamState> partitionStates = new HashMap<>();
+ historicalPartitionTSO.forEach((pid, tso) ->
+ partitionStates.put(pid, new PartitionStreamState(null, -1,
tso)));
+ Deencapsulation.setField(stream, "partitionStates", partitionStates);
// historical consume: prev is the historical TSO snapshot encoded as
negative
// (see StreamConsumptionInfoExtractor), next points to the historical
TSO snapshot
@@ -120,14 +123,11 @@ public class TableStreamOffsetTransactionTest extends
TestWithFeService {
long commitTime = System.currentTimeMillis();
Deencapsulation.invoke(dbTxnMgr, "updateStreamOffset",
transactionState, commitTime);
- Map<Long, Long> updatedHistoricalPartitionTSO =
Deencapsulation.getField(stream, "historicalPartitionTSO");
- Map<Long, Long> updatedPartitionOffset =
Deencapsulation.getField(stream, "partitionOffset");
- Map<Long, Long> partitionConsumptionTime =
Deencapsulation.getField(stream, "partitionConsumptionTime");
-
for (Long pid : partitionIds) {
-
Assertions.assertFalse(updatedHistoricalPartitionTSO.containsKey(pid));
- Assertions.assertEquals(expectedPartitionOffset.get(pid),
updatedPartitionOffset.get(pid));
- Assertions.assertEquals(commitTime,
partitionConsumptionTime.get(pid));
+ PartitionStreamState state = partitionStates.get(pid);
+ Assertions.assertNull(state.getHistoricalTso());
+ Assertions.assertEquals(expectedPartitionOffset.get(pid),
state.getOffset());
+ Assertions.assertEquals(commitTime, state.getConsumptionTime());
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]