github-actions[bot] commented on code in PR #68995:
URL: https://github.com/apache/doris/pull/68995#discussion_r4281423788


##########
fe/fe-core/src/main/java/org/apache/doris/cloud/catalog/CloudTabletRebalancer.java:
##########
@@ -1413,13 +1396,10 @@ public void statRouteInfo() {
                             : tabletToInfightTask.get(new 
InfightTablet(tabletId, cluster));
                     Long futureBeId = task == null ? beId : 
Long.valueOf(task.destBe);
                     Long routeTabletId = task == null ? tabletId : 
task.pickedTabletId;
-                    fillBeToTablets(beId, tableId, partitionId, indexId, 
routeTabletId,
-                            currentGlobalTabletSetFactory,
-                            tmpBeToTabletsGlobal, beToTabletsInTable, 
this.partitionToTablets);
-
-                    fillBeToTablets(futureBeId, tableId, partitionId, indexId, 
routeTabletId,
-                            futureGlobalTabletSetFactory,
-                            tmpFutureBeToTabletsGlobal, 
futureBeToTabletsInTable, futurePartitionToTablets);
+                    fillGlobalBeToTablets(beId, routeTabletId, 
currentGlobalTabletSetFactory,
+                            tmpBeToTabletsGlobal);
+                    fillGlobalBeToTablets(futureBeId, routeTabletId, 
futureGlobalTabletSetFactory,

Review Comment:
   [P2] Resolve inflight state before retaining the GLOBAL route
   
   The task-before-primary fix in `futureRouteBe` leaves a separate instance 
here: `statRouteInfo` still reads primary A before looking up the inflight 
task. If the checker completes A→B between those reads, this GLOBAL snapshot 
retains A, while the new partition/table views see B. With healthy A/B/C and 
two single-index tables whose actual counts are `(3,2,2)` and `(2,3,2)`, both 
live phases skip balancing, but the stale GLOBAL counts are `(6,4,4)`. Under 
the defaults, GLOBAL can select the raced tablet from A for A→C; fresh 
index/table counts both allow it because A=3>C=2, although it is actually at B. 
Its index becomes `(3,1,3)`. The old shared snapshot instead saw `(4,1,2)` at 
partition scope and blocked GLOBAL after a partition move. Resolve the task 
before the primary fallback in this builder too, or revalidate the effective 
source before GLOBAL scheduling, and cover completion during `statRouteInfo` in 
a deterministic test. This is the remaining GLOBAL-input location, dist
 inct from the fixed TABLE-helper thread.



##########
fe/fe-core/src/main/java/org/apache/doris/cloud/catalog/CloudTabletRebalancer.java:
##########
@@ -1471,138 +1452,189 @@ public void loopCloudReplica(Operator operator) {
 
     private void balanceInPartition(List<Long> bes, String clusterId, 
List<UpdateCloudReplicaInfo> infos,
                                     ActiveSchedulePhase phase) {
-        // balance all partition (prefer active partitions/tables, put 
internal db at tail)
-        Iterable<Map.Entry<Long, ConcurrentHashMap<Long, 
ConcurrentHashMap<Long, Set<Long>>>>> partitions;
+        Iterable<Long> partitions;
         if (Config.enable_cloud_active_tablet_priority_scheduling) {
-            final Comparator<Map.Entry<Long, ConcurrentHashMap<Long, 
ConcurrentHashMap<Long, Set<Long>>>>> cmp =
-                    partitionEntryComparator();
-            // Phase-aware filtering and ordering.
-            // - ACTIVE_ONLY: only non-internal partitions with activeCnt > 0
-            // - INACTIVE_ONLY: all remaining partitions (non-internal 
inactive first, internal last)
-            // - ALL: active (TopN first if configured) -> inactive -> internal
-            List<Map.Entry<Long, ConcurrentHashMap<Long, 
ConcurrentHashMap<Long, Set<Long>>>>> nonInternalActive =
-                    new ArrayList<>();
-            List<Map.Entry<Long, ConcurrentHashMap<Long, 
ConcurrentHashMap<Long, Set<Long>>>>> nonInternalInactive =
-                    new ArrayList<>();
-            List<Map.Entry<Long, ConcurrentHashMap<Long, 
ConcurrentHashMap<Long, Set<Long>>>>> internalPartitions =
-                    new ArrayList<>();
-
-            for (Map.Entry<Long, ConcurrentHashMap<Long, 
ConcurrentHashMap<Long, Set<Long>>>> e
-                    : futurePartitionToTablets.entrySet()) {
-                long partId = e.getKey();
-                boolean internal = 
isInternalDbId(partitionIdToDbId.get(partId));
-                long activeCnt = partitionIdToActiveCount.getOrDefault(partId, 
0L);
-
-                if (internal) {
-                    // internal partitions are always handled at the end (not 
in ACTIVE_ONLY).
-                    internalPartitions.add(e);
-                    continue;
-                }
-
-                if (activeCnt > 0) {
-                    nonInternalActive.add(e);
+            Comparator<Long> cmp = partitionEntryComparator();
+            List<Long> nonInternalActive = new ArrayList<>();
+            List<Long> nonInternalInactive = new ArrayList<>();
+            List<Long> internalPartitions = new ArrayList<>();
+            for (Long partitionId : partitionIdToTableId.keySet()) {
+                if (isInternalDbId(partitionIdToDbId.get(partitionId))) {
+                    internalPartitions.add(partitionId);
+                } else if (partitionIdToActiveCount.getOrDefault(partitionId, 
0L) > 0) {
+                    nonInternalActive.add(partitionId);
                 } else {
-                    nonInternalInactive.add(e);
+                    nonInternalInactive.add(partitionId);
                 }
             }
-
             nonInternalActive.sort(cmp);
             nonInternalInactive.sort(cmp);
             internalPartitions.sort(cmp);
-
-            List<Map.Entry<Long, ConcurrentHashMap<Long, 
ConcurrentHashMap<Long, Set<Long>>>>> ordered =
-                    new ArrayList<>(futurePartitionToTablets.size());
-            if (phase == ActiveSchedulePhase.ACTIVE_ONLY) {
-                // In ACTIVE_ONLY phase, schedule all active partitions 
(already sorted by cmp, most active first)
-                ordered.addAll(nonInternalActive);
-            } else if (phase == ActiveSchedulePhase.INACTIVE_ONLY) {
-                ordered.addAll(nonInternalInactive);
-                ordered.addAll(internalPartitions);
-            } else { // ALL
-                // All active (already sorted by cmp, most active first), then 
inactive, then internal
+            List<Long> ordered = new ArrayList<>(partitionIdToTableId.size());
+            if (phase != ActiveSchedulePhase.INACTIVE_ONLY) {
                 ordered.addAll(nonInternalActive);
+            }
+            if (phase != ActiveSchedulePhase.ACTIVE_ONLY) {
                 ordered.addAll(nonInternalInactive);
                 ordered.addAll(internalPartitions);
             }
-
             partitions = ordered;
         } else {
-            partitions = futurePartitionToTablets.entrySet();
-        }
-
-        for (Map.Entry<Long, ConcurrentHashMap<Long, ConcurrentHashMap<Long, 
Set<Long>>>> partitionEntry
-                : partitions) {
-            Map<Long, ConcurrentHashMap<Long, Set<Long>>> indexToTablets = 
partitionEntry.getValue();
-            // balance all index of a partition
-            List<Map.Entry<Long, ConcurrentHashMap<Long, Set<Long>>>> indexes =
-                    new ArrayList<>(indexToTablets.entrySet());
-            // index-level ordering is not critical; keep stable by id
-            indexes.sort(Comparator.comparingLong(Map.Entry::getKey));
-            for (Map.Entry<Long, ConcurrentHashMap<Long, Set<Long>>> entry : 
indexes) {
-                // balance a index
-                // Fast path: this index has no tablets in this cluster, skip 
to avoid useless balanceImpl work.
-                if (calculateTotalTablets(bes, entry.getValue()) == 0) {
-                    continue;
+            partitions = partitionIdToTableId.keySet();
+        }
+
+        for (Long partitionId : partitions) {
+            processFailedWarmupTasks();
+            Map<Long, Map<Long, Set<Long>>> indexViews = 
buildPartitionViews(partitionId, clusterId);
+            for (Long indexId : indexViews.keySet()) {
+                if (processFailedWarmupTasks()) {
+                    indexViews = buildPartitionViews(partitionId, clusterId);

Review Comment:
   [P2] Invalidate only partition views affected by rollback
   
   `processFailedWarmupTasks()` drains a global queue and returns true even for 
an obsolete task or a rollback in another partition/compute group. Every such 
result here rebuilds *all* index views of the current partition, scanning and 
allocating all tablet memberships under its table read lock. If outstanding RPC 
batches for other partitions keep failing between index visits, an otherwise 
balanced partition with R indices and T tablets is rebuilt up to R times: 
O(R*T) work (O(R²*N) for N tablets per index), instead of one O(T) build. This 
can multiply round CPU/allocation and block DDL during warmup failures. Return 
the affected index identities and refresh only a changed view, build each next 
index independently, or apply rollback deltas; avoid invalidating this 
partition for unrelated/no-op failures.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to