4444import com .starrocks .catalog .Tablet ;
4545import com .starrocks .common .Config ;
4646import com .starrocks .common .ErrorCode ;
47+ import com .starrocks .common .NoAliveBackendException ;
4748import com .starrocks .common .StarRocksException ;
4849import com .starrocks .common .ThreadPoolManager ;
4950import com .starrocks .common .util .FrontendDaemon ;
5556import com .starrocks .lake .Utils ;
5657import com .starrocks .lake .compaction .Quantiles ;
5758import com .starrocks .metric .MetricRepo ;
59+ import com .starrocks .proto .AggregatePublishVersionRequest ;
5860import com .starrocks .proto .DeleteTxnLogRequest ;
5961import com .starrocks .proto .DeleteTxnLogResponse ;
6062import com .starrocks .proto .TabletStatPB ;
6163import com .starrocks .proto .TxnInfoPB ;
64+ <<<<<<< HEAD
65+ =======
66+ import com .starrocks .proto .TxnTypePB ;
67+ import com .starrocks .proto .VectorIndexBuildInfoPB ;
68+ >>>>>>> 35 c68c6305 ( [BugFix ] Carry forward untouched indexes into the file -bundling bundle on compaction publish (#76105 ))
6269import com .starrocks .rpc .BrpcProxy ;
6370import com .starrocks .rpc .LakeService ;
71+ import com .starrocks .rpc .RpcException ;
6472import com .starrocks .server .GlobalStateMgr ;
6573import com .starrocks .server .RunMode ;
6674import com .starrocks .server .WarehouseManager ;
@@ -597,6 +605,7 @@ public boolean publishPartitionBatch(Database db, long tableId, PartitionPublish
597605 // The mapping is shadow index id -> ShadowIndexTxnBatch
598606 Map <Long , ShadowIndexTxnBatch > shadowIndexTxnBatches = null ;
599607 Set <Tablet > normalTablets = null ;
608+ List <Tablet > carryForwardTablets = null ;
600609
601610 Locker locker = new Locker ();
602611 locker .lockTablesWithIntensiveDbLock (db .getId (), Lists .newArrayList (tableId ), LockType .READ );
@@ -626,6 +635,7 @@ public boolean publishPartitionBatch(Database db, long tableId, PartitionPublish
626635 }
627636
628637 useAggregatePublish = table .isFileBundling ();
638+ Set <Long > publishedNormalIndexIds = Sets .newHashSet ();
629639 for (int i = 0 ; i < transactionStates .size (); i ++) {
630640 TransactionState txnState = transactionStates .get (i );
631641 computeResource = txnState .getComputeResource ();
@@ -650,9 +660,21 @@ public boolean publishPartitionBatch(Database db, long tableId, PartitionPublish
650660 } else {
651661 normalTablets = (normalTablets == null ) ? Sets .newHashSet () : normalTablets ;
652662 normalTablets .addAll (index .getTablets ());
663+ publishedNormalIndexIds .add (index .getId ());
653664 }
654665 }
655666 }
667+ // File bundling stores the metadata of ALL tablets of a partition version in a single bundle file.
668+ // The batched transactions' index views are point-in-time (for lake compaction snapshotted at txn
669+ // begin); if a rollup/MV index became visible after they began, none of them touch it, so publishing
670+ // the new version would write a bundle WITHOUT that index's tablets and permanently wedge the
671+ // partition. Carry forward every currently-visible NORMAL index none of these transactions touched
672+ // so the new bundle stays a complete whole-partition snapshot. See collectFileBundlingCarryForwardTablets.
673+ if (useAggregatePublish ) {
674+ carryForwardTablets = collectFileBundlingCarryForwardTablets (
675+ partition .getLatestMaterializedIndices (MaterializedIndex .IndexExtState .VISIBLE ),
676+ publishedNormalIndexIds );
677+ }
656678 } finally {
657679 locker .unLockTablesWithIntensiveDbLock (db .getId (), Lists .newArrayList (tableId ), LockType .READ );
658680 }
@@ -695,7 +717,15 @@ public boolean publishPartitionBatch(Database db, long tableId, PartitionPublish
695717 if (!useAggregatePublish ) {
696718 Utils .publishVersionBatch (publishTablets , txnInfos ,
697719 startVersion - 1 , endVersion , compactionScores , nodeToTablets ,
720+ <<<<<<< HEAD
698721 computeResource , tabletStats );
722+ =======
723+ computeResource , tabletStats , vectorIndexBuildInfos );
724+ } else if (CollectionUtils .isNotEmpty (carryForwardTablets )) {
725+ aggregatePublishWithCarryForward (publishTablets , txnInfos , carryForwardTablets ,
726+ startVersion - 1 , endVersion , nodeToTablets , computeResource , compactionScores ,
727+ tabletStats , vectorIndexBuildInfos );
728+ >>>>>>> 35 c68c6305 ( [BugFix ] Carry forward untouched indexes into the file -bundling bundle on compaction publish (#76105 ))
699729 } else {
700730 Utils .aggregatePublishVersion (publishTablets , txnInfos , startVersion - 1 , endVersion ,
701731 compactionScores , nodeToTablets , computeResource , tabletStats );
@@ -1038,6 +1068,7 @@ private boolean publishPartition(@NotNull Database db, @NotNull TableCommitInfo
10381068 ComputeResource computeResource = txnState .getComputeResource ();
10391069 List <Tablet > normalTablets = null ;
10401070 List <Tablet > shadowTablets = null ;
1071+ List <Tablet > carryForwardTablets = null ;
10411072
10421073 Locker locker = new Locker ();
10431074 locker .lockTablesWithIntensiveDbLock (db .getId (), Lists .newArrayList (tableId ), LockType .READ );
@@ -1064,6 +1095,7 @@ private boolean publishPartition(@NotNull Database db, @NotNull TableCommitInfo
10641095 }
10651096 baseVersion = partition .getVisibleVersion ();
10661097 List <MaterializedIndex > indexes = txnState .getPartitionLoadedIndexes (table .getId (), partition );
1098+ Set <Long > publishedNormalIndexIds = Sets .newHashSet ();
10671099 for (MaterializedIndex index : indexes ) {
10681100 if (!index .visibleForTransaction (txnId )) {
10691101 LOG .info ("Ignored index {} for transaction {}" , table .getIndexNameByMetaId (index .getMetaId ()), txnId );
@@ -1075,8 +1107,23 @@ private boolean publishPartition(@NotNull Database db, @NotNull TableCommitInfo
10751107 } else {
10761108 normalTablets = (normalTablets == null ) ? Lists .newArrayList () : normalTablets ;
10771109 normalTablets .addAll (index .getTablets ());
1110+ publishedNormalIndexIds .add (index .getId ());
10781111 }
10791112 }
1113+ // File bundling stores the metadata of ALL tablets of a partition version in a single bundle
1114+ // file (meta/0_<version>.meta). The index set above comes from the transaction's point-in-time
1115+ // view (for lake compaction it is snapshotted at txn begin). If a materialized-view/rollup
1116+ // index became visible after this transaction began, that stale view omits it, so publishing
1117+ // the new version would write a bundle WITHOUT the rollup index's tablets, dropping them from
1118+ // this version and permanently wedging the partition's publish queue (a later publish reading
1119+ // those tablets at this version gets a 404). Carry forward every currently-visible NORMAL index
1120+ // this transaction did not touch, so the new bundle remains a complete whole-partition snapshot.
1121+ // Only needed for file bundling; without it each tablet keeps its own per-version metadata file.
1122+ if (useAggregatePublish ) {
1123+ carryForwardTablets = collectFileBundlingCarryForwardTablets (
1124+ partition .getLatestMaterializedIndices (MaterializedIndex .IndexExtState .VISIBLE ),
1125+ publishedNormalIndexIds );
1126+ }
10801127 } finally {
10811128 locker .unLockTablesWithIntensiveDbLock (db .getId (), Lists .newArrayList (tableId ), LockType .READ );
10821129 }
@@ -1092,8 +1139,20 @@ private boolean publishPartition(@NotNull Database db, @NotNull TableCommitInfo
10921139 // Per-tablet stats from the publish response: first-load row counts for statistics
10931140 // collection, and range-distribution tablet sizes for real-time reshard triggering.
10941141 Map <Long , TabletStatPB > tabletStats = new HashMap <>();
1142+ <<<<<<< HEAD
10951143 Utils .publishVersion (normalTablets , txnInfo , baseVersion , txnVersion , compactionScores ,
10961144 computeResource , tabletStats , useAggregatePublish );
1145+ =======
1146+ List <VectorIndexBuildInfoPB > vectorIndexBuildInfos = new ArrayList <>();
1147+ if (useAggregatePublish && CollectionUtils .isNotEmpty (carryForwardTablets )) {
1148+ aggregatePublishWithCarryForward (normalTablets , Lists .newArrayList (txnInfo ), carryForwardTablets ,
1149+ baseVersion , txnVersion , null , computeResource , compactionScores , tabletStats ,
1150+ vectorIndexBuildInfos );
1151+ } else {
1152+ Utils .publishVersion (normalTablets , txnInfo , baseVersion , txnVersion , compactionScores ,
1153+ computeResource , tabletStats , useAggregatePublish , vectorIndexBuildInfos );
1154+ }
1155+ >>>>>>> 35 c68c6305 ( [BugFix ] Carry forward untouched indexes into the file -bundling bundle on compaction publish (#76105 ))
10971156
10981157 Quantiles quantiles = Quantiles .compute (compactionScores .values ());
10991158 partitionCommitInfo .setCompactionScore (quantiles );
@@ -1120,6 +1179,68 @@ private boolean publishPartition(@NotNull Database db, @NotNull TableCommitInfo
11201179 }
11211180 }
11221181
1182+ // For file bundling only: from the partition's currently-visible indexes, return the tablets of every
1183+ // NORMAL index that this transaction did not touch (i.e. whose index id is not in publishedNormalIndexIds).
1184+ // These must be carried forward (empty version bump) into the new version's bundle so it stays a complete
1185+ // whole-partition snapshot; otherwise a transaction with a stale index view (notably lake compaction
1186+ // whose loaded-index set is snapshotted at txn begin, before a rollup/MV index became visible) would
1187+ // publish a bundle missing that index and permanently wedge the partition. Returns null when nothing
1188+ // needs carrying forward (e.g. a normal load already covers every visible index). Caller must hold the
1189+ // table read lock while obtaining visibleIndexes.
1190+ static List <Tablet > collectFileBundlingCarryForwardTablets (List <MaterializedIndex > visibleIndexes ,
1191+ Set <Long > publishedNormalIndexIds ) {
1192+ List <Tablet > carryForwardTablets = null ;
1193+ for (MaterializedIndex index : visibleIndexes ) {
1194+ if (index .getState () == MaterializedIndex .IndexState .SHADOW
1195+ || publishedNormalIndexIds .contains (index .getId ())) {
1196+ continue ;
1197+ }
1198+ carryForwardTablets = (carryForwardTablets == null ) ? Lists .newArrayList () : carryForwardTablets ;
1199+ carryForwardTablets .addAll (index .getTablets ());
1200+ }
1201+ return carryForwardTablets ;
1202+ }
1203+
1204+ // File bundling only. Publish the touched tablets (this publish's real transactions) together with the
1205+ // carry-forward tablets (untouched but currently-visible indexes) in ONE aggregate request, so the whole
1206+ // partition's metadata for the new version lands in a single bundle file (meta/0_<newVersion>.meta). Two
1207+ // separate aggregate publishes would each truncate-write that same bundle and lose one of the two sets.
1208+ //
1209+ // The carry-forward tablets have no txn log for these transactions, so for every real transaction we emit a
1210+ // matching no-op empty transaction (no_op_publish=true): the BE bypasses log loading and advances the tablet
1211+ // version without any data change. The count must match the real transactions because the BE requires
1212+ // new_version == base_version + txns.size() for a multi-transaction batch (a batch of pre-rollup compactions
1213+ // can span several versions).
1214+ static void aggregatePublishWithCarryForward (List <Tablet > touchedTablets , List <TxnInfoPB > txnInfos ,
1215+ List <Tablet > carryForwardTablets , long baseVersion ,
1216+ long newVersion , Map <ComputeNode , List <Long >> nodeToTablets ,
1217+ ComputeResource computeResource ,
1218+ Map <Long , Double > compactionScores ,
1219+ Map <Long , TabletStatPB > tabletStats ,
1220+ List <VectorIndexBuildInfoPB > vectorIndexBuildInfos )
1221+ throws NoAliveBackendException , RpcException {
1222+ AggregatePublishVersionRequest request = new AggregatePublishVersionRequest ();
1223+ Utils .createSubRequestForAggregatePublish (touchedTablets , txnInfos , baseVersion , newVersion ,
1224+ nodeToTablets , computeResource , request );
1225+
1226+ List <TxnInfoPB > carryForwardTxnInfos = Lists .newArrayListWithCapacity (txnInfos .size ());
1227+ for (TxnInfoPB txnInfo : txnInfos ) {
1228+ TxnInfoPB emptyTxnInfo = new TxnInfoPB ();
1229+ emptyTxnInfo .txnId = -1L ;
1230+ emptyTxnInfo .combinedTxnLog = false ;
1231+ emptyTxnInfo .commitTime = txnInfo .commitTime ;
1232+ emptyTxnInfo .txnType = TxnTypePB .TXN_EMPTY ;
1233+ emptyTxnInfo .gtid = txnInfo .gtid ;
1234+ emptyTxnInfo .noOpPublish = true ;
1235+ carryForwardTxnInfos .add (emptyTxnInfo );
1236+ }
1237+ // The carry-forward tablets have no txn log to delete on success, so do not thread nodeToTablets here.
1238+ Utils .createSubRequestForAggregatePublish (carryForwardTablets , carryForwardTxnInfos , baseVersion , newVersion ,
1239+ null , computeResource , request );
1240+ Utils .sendAggregatePublishVersionRequest (request , baseVersion , computeResource , compactionScores , null ,
1241+ tabletStats , vectorIndexBuildInfos );
1242+ }
1243+
11231244 // Per-partition publishPartition phase breakdown for slow outliers.
11241245 // total = executor_queue + db_lock_wait + fe_prep + rpc, where:
11251246 // executor_queue = CompletableFuture submit -> lambda entry
0 commit comments