Skip to content

Commit dfdc0a4

Browse files
Youngwbclaude
authored andcommitted
[Enhancement] Record IVM source version/timestamp ranges in MV task run EXTRA_MESSAGE (StarRocks#74605)
Signed-off-by: Youngwb <yangwenbo_mailbox@163.com> Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: Oliver Layer <o.layer@celonis.com>
1 parent 88eea3e commit dfdc0a4

15 files changed

Lines changed: 466 additions & 0 deletions

File tree

fe/fe-core/src/main/java/com/starrocks/connector/CatalogConnectorMetadata.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,16 @@ public TvrVersionRange getTableVersionRange(String dbName, Table table,
212212
return metadata.getTableVersionRange(dbName, table, startVersion, endVersion);
213213
}
214214

215+
@Override
216+
public Optional<Long> getVersionCommitTimeMillis(String dbName, Table table, long version) {
217+
ConnectorMetadata metadata = metadataOfTable(table);
218+
if (metadata == null) {
219+
metadata = metadataOfDb(dbName);
220+
}
221+
222+
return metadata.getVersionCommitTimeMillis(dbName, table, version);
223+
}
224+
215225
@Override
216226
public boolean tableExists(ConnectContext context, String dbName, String tblName) {
217227
ConnectorMetadata metadata = metadataOfDb(dbName);

fe/fe-core/src/main/java/com/starrocks/connector/ConnectorMetadata.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,15 @@ default List<TvrTableDeltaTrait> listTableDeltaTraits(String dbName, Table table
194194
return Lists.newArrayList();
195195
}
196196

197+
/**
198+
* Commit time of {@code version} (the table's own version space, e.g. an Iceberg snapshot id)
199+
* in epoch millis, or empty when it cannot be resolved (unknown/expired version, or a format
200+
* with no per-version commit time).
201+
*/
202+
default Optional<Long> getVersionCommitTimeMillis(String dbName, Table table, long version) {
203+
return Optional.empty();
204+
}
205+
197206
default boolean tableExists(ConnectContext context, String dbName, String tblName) {
198207
return listTableNames(context, dbName).contains(tblName);
199208
}

fe/fe-core/src/main/java/com/starrocks/connector/iceberg/IcebergMetadata.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,12 @@ public List<TvrTableDeltaTrait> listTableDeltaTraits(String dbName, Table table,
857857
return tvrDeltaTraits;
858858
}
859859

860+
@Override
861+
public Optional<Long> getVersionCommitTimeMillis(String dbName, Table table, long version) {
862+
Snapshot snapshot = ((IcebergTable) table).getNativeTable().snapshot(version);
863+
return snapshot == null ? Optional.empty() : Optional.of(snapshot.timestampMillis());
864+
}
865+
860866
@Override
861867
public TvrVersionRange getTableVersionRange(String dbName, Table table,
862868
Optional<ConnectorTableVersion> startVersion,

fe/fe-core/src/main/java/com/starrocks/connector/unified/UnifiedMetadata.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ public TvrVersionRange getTableVersionRange(String dbName, Table table,
164164
return metadata.getTableVersionRange(dbName, table, startVersion, endVersion);
165165
}
166166

167+
@Override
168+
public Optional<Long> getVersionCommitTimeMillis(String dbName, Table table, long version) {
169+
ConnectorMetadata metadata = metadataOfTable(table);
170+
return metadata.getVersionCommitTimeMillis(dbName, table, version);
171+
}
172+
167173
@Override
168174
public List<String> listDbNames(ConnectContext context) {
169175
return hiveMetadata.listDbNames(context);

fe/fe-core/src/main/java/com/starrocks/scheduler/mv/ivm/MVIVMRefreshProcessor.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import com.google.common.annotations.VisibleForTesting;
1818
import com.google.common.base.Joiner;
19+
import com.google.common.collect.ImmutableMap;
1920
import com.google.common.collect.Maps;
2021
import com.google.common.collect.Multimap;
2122
import com.starrocks.catalog.BaseTableInfo;
@@ -50,7 +51,9 @@
5051
import com.starrocks.scheduler.mv.BaseTableSnapshotInfo;
5152
import com.starrocks.scheduler.mv.MVRefreshExecutor;
5253
import com.starrocks.scheduler.mv.MVRefreshProcessor;
54+
import com.starrocks.scheduler.persist.MVTaskRunExtraMessage;
5355
import com.starrocks.server.GlobalStateMgr;
56+
import com.starrocks.server.MetadataMgr;
5457
import com.starrocks.sql.StatementPlanner;
5558
import com.starrocks.sql.analyzer.Analyzer;
5659
import com.starrocks.sql.analyzer.AnalyzerUtils;
@@ -156,9 +159,50 @@ public ProcessExecPlan getProcessExecPlan(TaskRunContext taskRunContext) throws
156159
try (Timer ignored = Tracers.watchScope("MVRefreshPrepareRefreshPlan")) {
157160
insertStmt = prepareRefreshPlan();
158161
}
162+
recordImvSourceRangesOnTaskRun();
159163
return new ProcessExecPlan(Constants.TaskRunState.SUCCESS, mvContext.getExecPlan(), insertStmt);
160164
}
161165

166+
/**
167+
* Record the staged TVR version range and snapshot commit times per base table on the task
168+
* run's extra message, surfaced via information_schema.task_runs.EXTRA_MESSAGE.
169+
* Must stay after prepareRefreshPlan(): recording earlier leaves stale ranges on the task
170+
* run when the hybrid processor falls back to PCT on an IVM planning failure.
171+
*/
172+
private void recordImvSourceRangesOnTaskRun() {
173+
updateTaskRunStatus(status -> {
174+
Map<String, Map<String, String>> versionRanges = Maps.newHashMap();
175+
Map<String, Map<String, String>> timestampRanges = Maps.newHashMap();
176+
for (BaseTableSnapshotInfo snapshotInfo : snapshotBaseTables.values()) {
177+
TvrVersionRange delta = ((TvrTableSnapshotInfo) snapshotInfo).getTvrSnapshot();
178+
if (delta == null) {
179+
continue;
180+
}
181+
BaseTableInfo baseTableInfo = snapshotInfo.getBaseTableInfo();
182+
String tableFullName = baseTableInfo.getReadableString();
183+
// TvrVersion.toString() renders the MIN/MAX sentinels as "MIN"/"MAX"
184+
versionRanges.put(tableFullName, ImmutableMap.of(
185+
"start", delta.from().toString(),
186+
"end", delta.to().toString()));
187+
timestampRanges.put(tableFullName,
188+
resolveCommitTimeRange(baseTableInfo.getDbName(), snapshotInfo.getBaseTable(), delta));
189+
}
190+
MVTaskRunExtraMessage extraMessage = status.getMvTaskRunExtraMessage();
191+
extraMessage.setImvSourceVersionRange(versionRanges);
192+
extraMessage.setImvSourceTimestampRange(timestampRanges);
193+
});
194+
}
195+
196+
private static Map<String, String> resolveCommitTimeRange(String dbName, Table table, TvrVersionRange delta) {
197+
Map<String, String> commitTimes = Maps.newLinkedHashMap();
198+
MetadataMgr metadataMgr = GlobalStateMgr.getCurrentState().getMetadataMgr();
199+
delta.start().flatMap(version -> metadataMgr.getVersionCommitTimeMillis(dbName, table, version))
200+
.ifPresent(time -> commitTimes.put("start", String.valueOf(time)));
201+
delta.end().flatMap(version -> metadataMgr.getVersionCommitTimeMillis(dbName, table, version))
202+
.ifPresent(time -> commitTimes.put("end", String.valueOf(time)));
203+
return commitTimes;
204+
}
205+
162206
@Override
163207
public Constants.TaskRunState execProcessExecPlan(TaskRunContext taskRunContext,
164208
ProcessExecPlan processExecPlan,

fe/fe-core/src/main/java/com/starrocks/scheduler/persist/MVTaskRunExtraMessage.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ public class MVTaskRunExtraMessage implements Writable {
8282
@SerializedName("pinnedSnapshotIdMap")
8383
private Map<String, Long> pinnedSnapshotIdMap = Maps.newHashMap();
8484

85+
// For IVM refreshes: per base table ("catalog.db.tbl"), the consumed TVR version range
86+
// {start, end} and the matching snapshot commit times in epoch millis (empty when unresolvable).
87+
@SerializedName("imvSourceVersionRange")
88+
private Map<String, Map<String, String>> imvSourceVersionRange = Maps.newHashMap();
89+
@SerializedName("imvSourceTimestampRange")
90+
private Map<String, Map<String, String>> imvSourceTimestampRange = Maps.newHashMap();
91+
8592
public MVTaskRunExtraMessage() {
8693
}
8794

@@ -222,6 +229,24 @@ public void setPinnedSnapshotIdMap(Map<String, Long> pinnedSnapshotIdMap) {
222229
Config.max_mv_task_run_meta_message_values_length);
223230
}
224231

232+
public Map<String, Map<String, String>> getImvSourceVersionRange() {
233+
return imvSourceVersionRange;
234+
}
235+
236+
public void setImvSourceVersionRange(Map<String, Map<String, String>> imvSourceVersionRange) {
237+
this.imvSourceVersionRange = MvUtils.shrinkToSize(imvSourceVersionRange,
238+
Config.max_mv_task_run_meta_message_values_length);
239+
}
240+
241+
public Map<String, Map<String, String>> getImvSourceTimestampRange() {
242+
return imvSourceTimestampRange;
243+
}
244+
245+
public void setImvSourceTimestampRange(Map<String, Map<String, String>> imvSourceTimestampRange) {
246+
this.imvSourceTimestampRange = MvUtils.shrinkToSize(imvSourceTimestampRange,
247+
Config.max_mv_task_run_meta_message_values_length);
248+
}
249+
225250
@Override
226251
public String toString() {
227252
return GsonUtils.GSON.toJson(this);

fe/fe-core/src/main/java/com/starrocks/server/MetadataMgr.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,11 @@ public TvrVersionRange getTableVersionRange(String dbName, Table table,
573573
.orElse(TvrTableSnapshot.empty());
574574
}
575575

576+
public Optional<Long> getVersionCommitTimeMillis(String dbName, Table table, long version) {
577+
Optional<ConnectorMetadata> connectorMetadata = getOptionalMetadata(table.getCatalogName());
578+
return connectorMetadata.flatMap(metadata -> metadata.getVersionCommitTimeMillis(dbName, table, version));
579+
}
580+
576581
public Optional<Database> getDatabase(ConnectContext context, BaseTableInfo baseTableInfo) {
577582
if (baseTableInfo.isInternalCatalog()) {
578583
return Optional.ofNullable(getDb(baseTableInfo.getDbId()));

fe/fe-core/src/test/java/com/starrocks/connector/CatalogConnectorMetadataTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.util.HashMap;
3535
import java.util.List;
3636
import java.util.Map;
37+
import java.util.Optional;
3738

3839
import static org.junit.jupiter.api.Assertions.assertEquals;
3940
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -290,4 +291,25 @@ void testAcquireTvrSnapshotDelegatesToChild(@Mocked ConnectorMetadata connectorM
290291
TvrTableSnapshot actual = catalogConnectorMetadata.acquireTvrSnapshot("test_db", table, mvId);
291292
assertEquals(expected, actual);
292293
}
294+
295+
@Test
296+
void testGetVersionCommitTimeMillisDelegatesToChild(@Mocked ConnectorMetadata connectorMetadata,
297+
@Mocked Table table) {
298+
Optional<Long> expected = Optional.of(1781000000000L);
299+
new Expectations() {
300+
{
301+
connectorMetadata.getVersionCommitTimeMillis("test_db", table, 42L);
302+
result = expected;
303+
times = 1;
304+
}
305+
};
306+
307+
CatalogConnectorMetadata catalogConnectorMetadata = new CatalogConnectorMetadata(
308+
connectorMetadata,
309+
informationSchemaMetadata,
310+
metaMetadata
311+
);
312+
313+
assertEquals(expected, catalogConnectorMetadata.getVersionCommitTimeMillis("test_db", table, 42L));
314+
}
293315
}

fe/fe-core/src/test/java/com/starrocks/connector/iceberg/IcebergMetadataTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,23 @@ public void testListDatabaseNames(@Mocked IcebergCatalog icebergCatalog) {
244244
Assertions.assertEquals(expectResult, metadata.listDbNames(connectContext));
245245
}
246246

247+
@Test
248+
public void testGetVersionCommitTimeMillis() {
249+
IcebergHiveCatalog icebergHiveCatalog = new IcebergHiveCatalog(CATALOG_NAME, new Configuration(), DEFAULT_CONFIG);
250+
IcebergMetadata metadata = new IcebergMetadata(CATALOG_NAME, HDFS_ENVIRONMENT, icebergHiveCatalog,
251+
Executors.newSingleThreadExecutor(), Executors.newSingleThreadExecutor(), null);
252+
mockedNativeTableA.newAppend().appendFile(FILE_A).commit();
253+
mockedNativeTableA.refresh();
254+
Snapshot snapshot = mockedNativeTableA.currentSnapshot();
255+
IcebergTable table = new IcebergTable(1, "tableA", CATALOG_NAME, CATALOG_NAME, "iceberg_db",
256+
"tableA", "", Lists.newArrayList(), mockedNativeTableA, Maps.newHashMap());
257+
258+
Assertions.assertEquals(Optional.of(snapshot.timestampMillis()),
259+
metadata.getVersionCommitTimeMillis("iceberg_db", table, snapshot.snapshotId()));
260+
Assertions.assertEquals(Optional.empty(),
261+
metadata.getVersionCommitTimeMillis("iceberg_db", table, snapshot.snapshotId() + 1));
262+
}
263+
247264
@Test
248265
public void testGetDB(@Mocked IcebergHiveCatalog icebergHiveCatalog) {
249266
String db = "db";

fe/fe-core/src/test/java/com/starrocks/connector/iceberg/MockIcebergMetadata.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import org.apache.iceberg.DataFiles;
5454
import org.apache.iceberg.PartitionSpec;
5555
import org.apache.iceberg.Schema;
56+
import org.apache.iceberg.Snapshot;
5657
import org.apache.iceberg.Table;
5758
import org.apache.iceberg.TableMetadata;
5859
import org.apache.iceberg.types.Types;
@@ -643,6 +644,12 @@ public TvrTableSnapshot getCurrentTvrSnapshot(String dbName, com.starrocks.catal
643644
return TvrTableSnapshot.of(TvrVersion.of(1L));
644645
}
645646

647+
@Override
648+
public Optional<Long> getVersionCommitTimeMillis(String dbName, com.starrocks.catalog.Table table, long version) {
649+
Snapshot snapshot = ((IcebergTable) table).getNativeTable().snapshot(version);
650+
return snapshot == null ? Optional.empty() : Optional.of(snapshot.timestampMillis());
651+
}
652+
646653
// ConnectorMetadata's default returns TvrTableSnapshot.empty(), which leaves the planned scan
647654
// pinned to the MIN snapshot (0 partitions, no data) -- low-cardinality dict collection and the
648655
// group-by min/max rule both then no-op. Mirror production's IcebergMetadata.getTableVersionRange:

0 commit comments

Comments
 (0)