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


##########
fe/fe-core/src/main/java/org/apache/doris/cloud/catalog/CloudTabletRebalancer.java:
##########
@@ -1437,138 +1417,191 @@ 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);
+                }
+                Map<Long, Set<Long>> view = indexViews.get(indexId);
+                if (view != null && calculateTotalTablets(bes, view) != 0) {
+                    balanceImpl(bes, clusterId, view, BalanceType.PARTITION, 
infos);
                 }
-                balanceImpl(bes, clusterId, entry.getValue(), 
BalanceType.PARTITION, infos);
             }
         }
     }
 
     private void balanceInTable(List<Long> bes, String clusterId, 
List<UpdateCloudReplicaInfo> infos,
                                 ActiveSchedulePhase phase) {
-        // balance all tables (prefer active tables/dbs, put internal db at 
tail)
-        Iterable<Map.Entry<Long, ConcurrentHashMap<Long, Set<Long>>>> tables;
+        Iterable<Long> tables;
+        Set<Long> tableIds = new HashSet<>(partitionIdToTableId.values());
         if (Config.enable_cloud_active_tablet_priority_scheduling) {
-            final Comparator<Map.Entry<Long, ConcurrentHashMap<Long, 
Set<Long>>>> cmp = tableEntryComparator();
-            List<Map.Entry<Long, ConcurrentHashMap<Long, Set<Long>>>> 
nonInternalActive = new ArrayList<>();
-            List<Map.Entry<Long, ConcurrentHashMap<Long, Set<Long>>>> 
nonInternalInactive = new ArrayList<>();
-            List<Map.Entry<Long, ConcurrentHashMap<Long, Set<Long>>>> 
internalTables = new ArrayList<>();
-
-            for (Map.Entry<Long, ConcurrentHashMap<Long, Set<Long>>> e : 
futureBeToTabletsInTable.entrySet()) {
-                long tableId = e.getKey();
-                boolean internal = isInternalDbId(tableIdToDbId.get(tableId));
-                long activeCnt = tableIdToActiveCount.getOrDefault(tableId, 
0L);
-                if (internal) {
-                    internalTables.add(e);
-                    continue;
-                }
-                if (activeCnt > 0) {
-                    nonInternalActive.add(e);
+            Comparator<Long> cmp = tableEntryComparator();
+            List<Long> nonInternalActive = new ArrayList<>();
+            List<Long> nonInternalInactive = new ArrayList<>();
+            List<Long> internalTables = new ArrayList<>();
+            for (Long tableId : tableIds) {
+                if (isInternalDbId(tableIdToDbId.get(tableId))) {
+                    internalTables.add(tableId);
+                } else if (tableIdToActiveCount.getOrDefault(tableId, 0L) > 0) 
{
+                    nonInternalActive.add(tableId);
                 } else {
-                    nonInternalInactive.add(e);
+                    nonInternalInactive.add(tableId);
                 }
             }
-
             nonInternalActive.sort(cmp);
             nonInternalInactive.sort(cmp);
             internalTables.sort(cmp);
-
-            List<Map.Entry<Long, ConcurrentHashMap<Long, Set<Long>>>> ordered =
-                    new ArrayList<>(futureBeToTabletsInTable.size());
-            if (phase == ActiveSchedulePhase.ACTIVE_ONLY) {
-                ordered.addAll(nonInternalActive);
-            } else if (phase == ActiveSchedulePhase.INACTIVE_ONLY) {
-                ordered.addAll(nonInternalInactive);
-                ordered.addAll(internalTables);
-            } else { // ALL
+            List<Long> ordered = new ArrayList<>(tableIds.size());
+            if (phase != ActiveSchedulePhase.INACTIVE_ONLY) {
                 ordered.addAll(nonInternalActive);
+            }
+            if (phase != ActiveSchedulePhase.ACTIVE_ONLY) {
                 ordered.addAll(nonInternalInactive);
                 ordered.addAll(internalTables);
             }
-
             tables = ordered;
         } else {
-            tables = futureBeToTabletsInTable.entrySet();
+            tables = tableIds;
         }
 
-        for (Map.Entry<Long, ConcurrentHashMap<Long, Set<Long>>> entry : 
tables) {
-            // Fast path: this table has no tablets in this cluster, skip.
-            if (calculateTotalTablets(bes, entry.getValue()) == 0) {
-                continue;
+        for (Long tableId : tables) {
+            processFailedWarmupTasks();
+            Map<Long, Set<Long>> view = buildTableView(tableId, clusterId);
+            if (calculateTotalTablets(bes, view) != 0) {
+                balanceWithIndexCounts(bes, clusterId, view, 
BalanceType.TABLE, infos);
+            }
+        }
+    }
+
+    private long currentRouteBe(CloudReplica replica, String clusterId) {
+        Long beId = replica.getNonColocatedPrimaryBackendId(clusterId);
+        if (beId == null || 
Env.getCurrentSystemInfo().getBackendByIdWithBoxedId(beId) == null
+                || !allBes.contains(beId)) {
+            return -1L;
+        }
+        return beId;
+    }
+
+    private long futureRouteBe(long tabletId, CloudReplica replica, String 
clusterId) {
+        long currentBe = currentRouteBe(replica, clusterId);
+        if (currentBe == -1L || tabletToInfightTask.isEmpty()) {
+            return currentBe;
+        }
+        InfightTask task = tabletToInfightTask.get(new InfightTablet(tabletId, 
clusterId));
+        return task == null ? currentBe : task.destBe;

Review Comment:
   [P2] Resolve inflight state before reading the primary fallback
   
   The warmup checker can finish A→B after `currentRouteBe()` returns A but 
before the inflight lookup: it publishes primary B and removes the task, so 
this helper returns obsolete A. The checker and view builder both use table 
read locks, which permit this overlap. Queuing cancellation does not serialize 
successful completion.
   
   For example, on A/B/C use a seven-bucket partition with base-index FUTURE 
counts (3,2,2), three rollups with (3,2,2), and two with (2,3,2). PARTITION 
skips every balanced index. If its A→B tablet completes during TABLE's read 
above, the TABLE view becomes (17,13,12) instead of (16,14,12). Selecting that 
tablet from A schedules A→C because fresh conflict counts see C=2 < A=3; the 
tablet is actually at B, so its index becomes (3,1,3), requiring corrective 
balancing. The resident cache also decrements the wrong source. Previously 
partition/table/conflict membership came from the same snapshot, preventing 
this particular disagreement.
   
   Read the inflight task before the primary fallback, or revalidate the 
effective source before scheduling, and add a deterministic 
completion-during-view test.



-- 
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