Skip to content

Commit f6ad80c

Browse files
committed
[BugFix] Carry forward untouched indexes into the file-bundling bundle on compaction publish (#76105)
Signed-off-by: sevev <qiangzh95@gmail.com> (cherry picked from commit 35c68c6)
1 parent d575d92 commit f6ad80c

2 files changed

Lines changed: 239 additions & 2 deletions

File tree

fe/fe-core/src/main/java/com/starrocks/transaction/PublishVersionDaemon.java

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import com.starrocks.catalog.Tablet;
4545
import com.starrocks.common.Config;
4646
import com.starrocks.common.ErrorCode;
47+
import com.starrocks.common.NoAliveBackendException;
4748
import com.starrocks.common.StarRocksException;
4849
import com.starrocks.common.ThreadPoolManager;
4950
import com.starrocks.common.util.FrontendDaemon;
@@ -55,12 +56,15 @@
5556
import com.starrocks.lake.Utils;
5657
import com.starrocks.lake.compaction.Quantiles;
5758
import com.starrocks.metric.MetricRepo;
59+
import com.starrocks.proto.AggregatePublishVersionRequest;
5860
import com.starrocks.proto.DeleteTxnLogRequest;
5961
import com.starrocks.proto.DeleteTxnLogResponse;
6062
import com.starrocks.proto.TabletStatPB;
6163
import com.starrocks.proto.TxnInfoPB;
64+
import com.starrocks.proto.TxnTypePB;
6265
import com.starrocks.rpc.BrpcProxy;
6366
import com.starrocks.rpc.LakeService;
67+
import com.starrocks.rpc.RpcException;
6468
import com.starrocks.server.GlobalStateMgr;
6569
import com.starrocks.server.RunMode;
6670
import com.starrocks.server.WarehouseManager;
@@ -597,6 +601,7 @@ public boolean publishPartitionBatch(Database db, long tableId, PartitionPublish
597601
// The mapping is shadow index id -> ShadowIndexTxnBatch
598602
Map<Long, ShadowIndexTxnBatch> shadowIndexTxnBatches = null;
599603
Set<Tablet> normalTablets = null;
604+
List<Tablet> carryForwardTablets = null;
600605

601606
Locker locker = new Locker();
602607
locker.lockTablesWithIntensiveDbLock(db.getId(), Lists.newArrayList(tableId), LockType.READ);
@@ -626,6 +631,7 @@ public boolean publishPartitionBatch(Database db, long tableId, PartitionPublish
626631
}
627632

628633
useAggregatePublish = table.isFileBundling();
634+
Set<Long> publishedNormalIndexIds = Sets.newHashSet();
629635
for (int i = 0; i < transactionStates.size(); i++) {
630636
TransactionState txnState = transactionStates.get(i);
631637
computeResource = txnState.getComputeResource();
@@ -650,9 +656,21 @@ public boolean publishPartitionBatch(Database db, long tableId, PartitionPublish
650656
} else {
651657
normalTablets = (normalTablets == null) ? Sets.newHashSet() : normalTablets;
652658
normalTablets.addAll(index.getTablets());
659+
publishedNormalIndexIds.add(index.getId());
653660
}
654661
}
655662
}
663+
// File bundling stores the metadata of ALL tablets of a partition version in a single bundle file.
664+
// The batched transactions' index views are point-in-time (for lake compaction snapshotted at txn
665+
// begin); if a rollup/MV index became visible after they began, none of them touch it, so publishing
666+
// the new version would write a bundle WITHOUT that index's tablets and permanently wedge the
667+
// partition. Carry forward every currently-visible NORMAL index none of these transactions touched
668+
// so the new bundle stays a complete whole-partition snapshot. See collectFileBundlingCarryForwardTablets.
669+
if (useAggregatePublish) {
670+
carryForwardTablets = collectFileBundlingCarryForwardTablets(
671+
partition.getLatestMaterializedIndices(MaterializedIndex.IndexExtState.VISIBLE),
672+
publishedNormalIndexIds);
673+
}
656674
} finally {
657675
locker.unLockTablesWithIntensiveDbLock(db.getId(), Lists.newArrayList(tableId), LockType.READ);
658676
}
@@ -696,6 +714,10 @@ public boolean publishPartitionBatch(Database db, long tableId, PartitionPublish
696714
Utils.publishVersionBatch(publishTablets, txnInfos,
697715
startVersion - 1, endVersion, compactionScores, nodeToTablets,
698716
computeResource, tabletStats);
717+
} else if (CollectionUtils.isNotEmpty(carryForwardTablets)) {
718+
aggregatePublishWithCarryForward(publishTablets, txnInfos, carryForwardTablets,
719+
startVersion - 1, endVersion, nodeToTablets, computeResource, compactionScores,
720+
tabletStats);
699721
} else {
700722
Utils.aggregatePublishVersion(publishTablets, txnInfos, startVersion - 1, endVersion,
701723
compactionScores, nodeToTablets, computeResource, tabletStats);
@@ -1038,6 +1060,7 @@ private boolean publishPartition(@NotNull Database db, @NotNull TableCommitInfo
10381060
ComputeResource computeResource = txnState.getComputeResource();
10391061
List<Tablet> normalTablets = null;
10401062
List<Tablet> shadowTablets = null;
1063+
List<Tablet> carryForwardTablets = null;
10411064

10421065
Locker locker = new Locker();
10431066
locker.lockTablesWithIntensiveDbLock(db.getId(), Lists.newArrayList(tableId), LockType.READ);
@@ -1064,6 +1087,7 @@ private boolean publishPartition(@NotNull Database db, @NotNull TableCommitInfo
10641087
}
10651088
baseVersion = partition.getVisibleVersion();
10661089
List<MaterializedIndex> indexes = txnState.getPartitionLoadedIndexes(table.getId(), partition);
1090+
Set<Long> publishedNormalIndexIds = Sets.newHashSet();
10671091
for (MaterializedIndex index : indexes) {
10681092
if (!index.visibleForTransaction(txnId)) {
10691093
LOG.info("Ignored index {} for transaction {}", table.getIndexNameByMetaId(index.getMetaId()), txnId);
@@ -1075,8 +1099,23 @@ private boolean publishPartition(@NotNull Database db, @NotNull TableCommitInfo
10751099
} else {
10761100
normalTablets = (normalTablets == null) ? Lists.newArrayList() : normalTablets;
10771101
normalTablets.addAll(index.getTablets());
1102+
publishedNormalIndexIds.add(index.getId());
10781103
}
10791104
}
1105+
// File bundling stores the metadata of ALL tablets of a partition version in a single bundle
1106+
// file (meta/0_<version>.meta). The index set above comes from the transaction's point-in-time
1107+
// view (for lake compaction it is snapshotted at txn begin). If a materialized-view/rollup
1108+
// index became visible after this transaction began, that stale view omits it, so publishing
1109+
// the new version would write a bundle WITHOUT the rollup index's tablets, dropping them from
1110+
// this version and permanently wedging the partition's publish queue (a later publish reading
1111+
// those tablets at this version gets a 404). Carry forward every currently-visible NORMAL index
1112+
// this transaction did not touch, so the new bundle remains a complete whole-partition snapshot.
1113+
// Only needed for file bundling; without it each tablet keeps its own per-version metadata file.
1114+
if (useAggregatePublish) {
1115+
carryForwardTablets = collectFileBundlingCarryForwardTablets(
1116+
partition.getLatestMaterializedIndices(MaterializedIndex.IndexExtState.VISIBLE),
1117+
publishedNormalIndexIds);
1118+
}
10801119
} finally {
10811120
locker.unLockTablesWithIntensiveDbLock(db.getId(), Lists.newArrayList(tableId), LockType.READ);
10821121
}
@@ -1092,8 +1131,13 @@ private boolean publishPartition(@NotNull Database db, @NotNull TableCommitInfo
10921131
// Per-tablet stats from the publish response: first-load row counts for statistics
10931132
// collection, and range-distribution tablet sizes for real-time reshard triggering.
10941133
Map<Long, TabletStatPB> tabletStats = new HashMap<>();
1095-
Utils.publishVersion(normalTablets, txnInfo, baseVersion, txnVersion, compactionScores,
1096-
computeResource, tabletStats, useAggregatePublish);
1134+
if (useAggregatePublish && CollectionUtils.isNotEmpty(carryForwardTablets)) {
1135+
aggregatePublishWithCarryForward(normalTablets, Lists.newArrayList(txnInfo), carryForwardTablets,
1136+
baseVersion, txnVersion, null, computeResource, compactionScores, tabletStats);
1137+
} else {
1138+
Utils.publishVersion(normalTablets, txnInfo, baseVersion, txnVersion, compactionScores,
1139+
computeResource, tabletStats, useAggregatePublish);
1140+
}
10971141

10981142
Quantiles quantiles = Quantiles.compute(compactionScores.values());
10991143
partitionCommitInfo.setCompactionScore(quantiles);
@@ -1120,6 +1164,67 @@ private boolean publishPartition(@NotNull Database db, @NotNull TableCommitInfo
11201164
}
11211165
}
11221166

1167+
// For file bundling only: from the partition's currently-visible indexes, return the tablets of every
1168+
// NORMAL index that this transaction did not touch (i.e. whose index id is not in publishedNormalIndexIds).
1169+
// These must be carried forward (empty version bump) into the new version's bundle so it stays a complete
1170+
// whole-partition snapshot; otherwise a transaction with a stale index view (notably lake compaction
1171+
// whose loaded-index set is snapshotted at txn begin, before a rollup/MV index became visible) would
1172+
// publish a bundle missing that index and permanently wedge the partition. Returns null when nothing
1173+
// needs carrying forward (e.g. a normal load already covers every visible index). Caller must hold the
1174+
// table read lock while obtaining visibleIndexes.
1175+
static List<Tablet> collectFileBundlingCarryForwardTablets(List<MaterializedIndex> visibleIndexes,
1176+
Set<Long> publishedNormalIndexIds) {
1177+
List<Tablet> carryForwardTablets = null;
1178+
for (MaterializedIndex index : visibleIndexes) {
1179+
if (index.getState() == MaterializedIndex.IndexState.SHADOW
1180+
|| publishedNormalIndexIds.contains(index.getId())) {
1181+
continue;
1182+
}
1183+
carryForwardTablets = (carryForwardTablets == null) ? Lists.newArrayList() : carryForwardTablets;
1184+
carryForwardTablets.addAll(index.getTablets());
1185+
}
1186+
return carryForwardTablets;
1187+
}
1188+
1189+
// File bundling only. Publish the touched tablets (this publish's real transactions) together with the
1190+
// carry-forward tablets (untouched but currently-visible indexes) in ONE aggregate request, so the whole
1191+
// partition's metadata for the new version lands in a single bundle file (meta/0_<newVersion>.meta). Two
1192+
// separate aggregate publishes would each truncate-write that same bundle and lose one of the two sets.
1193+
//
1194+
// The carry-forward tablets have no txn log for these transactions, so for every real transaction we emit a
1195+
// matching no-op empty transaction (no_op_publish=true): the BE bypasses log loading and advances the tablet
1196+
// version without any data change. The count must match the real transactions because the BE requires
1197+
// new_version == base_version + txns.size() for a multi-transaction batch (a batch of pre-rollup compactions
1198+
// can span several versions).
1199+
static void aggregatePublishWithCarryForward(List<Tablet> touchedTablets, List<TxnInfoPB> txnInfos,
1200+
List<Tablet> carryForwardTablets, long baseVersion,
1201+
long newVersion, Map<ComputeNode, List<Long>> nodeToTablets,
1202+
ComputeResource computeResource,
1203+
Map<Long, Double> compactionScores,
1204+
Map<Long, TabletStatPB> tabletStats)
1205+
throws NoAliveBackendException, RpcException {
1206+
AggregatePublishVersionRequest request = new AggregatePublishVersionRequest();
1207+
Utils.createSubRequestForAggregatePublish(touchedTablets, txnInfos, baseVersion, newVersion,
1208+
nodeToTablets, computeResource, request);
1209+
1210+
List<TxnInfoPB> carryForwardTxnInfos = Lists.newArrayListWithCapacity(txnInfos.size());
1211+
for (TxnInfoPB txnInfo : txnInfos) {
1212+
TxnInfoPB emptyTxnInfo = new TxnInfoPB();
1213+
emptyTxnInfo.txnId = -1L;
1214+
emptyTxnInfo.combinedTxnLog = false;
1215+
emptyTxnInfo.commitTime = txnInfo.commitTime;
1216+
emptyTxnInfo.txnType = TxnTypePB.TXN_EMPTY;
1217+
emptyTxnInfo.gtid = txnInfo.gtid;
1218+
emptyTxnInfo.noOpPublish = true;
1219+
carryForwardTxnInfos.add(emptyTxnInfo);
1220+
}
1221+
// The carry-forward tablets have no txn log to delete on success, so do not thread nodeToTablets here.
1222+
Utils.createSubRequestForAggregatePublish(carryForwardTablets, carryForwardTxnInfos, baseVersion, newVersion,
1223+
null, computeResource, request);
1224+
Utils.sendAggregatePublishVersionRequest(request, baseVersion, computeResource, compactionScores,
1225+
tabletStats);
1226+
}
1227+
11231228
// Per-partition publishPartition phase breakdown for slow outliers.
11241229
// total = executor_queue + db_lock_wait + fe_prep + rpc, where:
11251230
// executor_queue = CompletableFuture submit -> lambda entry

0 commit comments

Comments
 (0)