This is an automated email from the ASF dual-hosted git repository.

yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.2 by this push:
     new c2aaed99db8 branch-4.2: [fix](scan) Use query-available check for scan 
replica selection #66964 (#70025)
c2aaed99db8 is described below

commit c2aaed99db8355529f2924eda68e3598fb46e7b3
Author: morrySnow <[email protected]>
AuthorDate: Tue Nov 3 17:46:53 2026 +0800

    branch-4.2: [fix](scan) Use query-available check for scan replica 
selection #66964 (#70025)
    
    picked from master #66964
    
    Co-authored-by: minghong <[email protected]>
---
 .../worker/LoadBalanceScanWorkerSelector.java      |  5 ++-
 .../org/apache/doris/planner/OlapScanNode.java     | 12 +++---
 ...anceScanWorkerSelectorBackendSelectionTest.java | 26 +++++++++++-
 .../OlapScanNodeBackendSelectionConfigTest.java    | 48 ++++++++++++++++++++++
 4 files changed, 83 insertions(+), 8 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/distribute/worker/LoadBalanceScanWorkerSelector.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/distribute/worker/LoadBalanceScanWorkerSelector.java
index dca544ae857..39c47088588 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/distribute/worker/LoadBalanceScanWorkerSelector.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/distribute/worker/LoadBalanceScanWorkerSelector.java
@@ -263,6 +263,9 @@ public class LoadBalanceScanWorkerSelector implements 
ScanWorkerSelector {
         if (replicaLocations.size() == 1) {
             TScanRangeLocation replicaLocation = replicaLocations.get(0);
             DistributedPlanWorker worker = workerManager.getWorker(catalogId, 
replicaLocation.getBackendId());
+            if (!((BackendWorker) worker).getBackend().isQueryAvailable()) {
+                throw new AnalysisException("No available workers");
+            }
             ScanRanges scanRanges = new ScanRanges();
             TScanRangeParams scanReplicaParams =
                     ScanWorkerSelector.buildScanReplicaParams(tabletLocation, 
replicaLocation);
@@ -302,7 +305,7 @@ public class LoadBalanceScanWorkerSelector implements 
ScanWorkerSelector {
 
         for (TScanRangeLocation replicaLocation : replicaLocations) {
             DistributedPlanWorker worker = workerManager.getWorker(catalogId, 
replicaLocation.getBackendId());
-            if (!worker.available()) {
+            if (!((BackendWorker) worker).getBackend().isQueryAvailable()) {
                 continue;
             }
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/planner/OlapScanNode.java 
b/fe/fe-core/src/main/java/org/apache/doris/planner/OlapScanNode.java
index b371c6db270..36b9601fbad 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/planner/OlapScanNode.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/planner/OlapScanNode.java
@@ -635,9 +635,9 @@ public class OlapScanNode extends ScanNode {
                     }
                     Backend backend = allBackends.get(beId);
                     // If the fixed replica is bad, then not clear the 
replicas using random replica
-                    if (backend == null || !backend.isAlive()) {
+                    if (backend == null || !backend.isQueryAvailable()) {
                         if (LOG.isDebugEnabled()) {
-                            LOG.debug("backend {} not exists or is not alive 
for replica {}", beId,
+                            LOG.debug("backend {} not exists or is not query 
available for replica {}", beId,
                                     replica.getId());
                         }
                         Collections.shuffle(replicas);
@@ -666,7 +666,7 @@ public class OlapScanNode extends ScanNode {
                     if (replicaOptional.isPresent()) {
                         Replica replica = replicaOptional.get();
                         Backend backend = 
allBackends.get(replica.getBackendIdWithoutException());
-                        if (backend != null && backend.isAlive()) {
+                        if (backend != null && backend.isQueryAvailable()) {
                             replicas.clear();
                             replicas.add(replica);
                         }
@@ -701,14 +701,14 @@ public class OlapScanNode extends ScanNode {
                     clusterException = true;
                     continue;
                 }
-                if (backend == null || !backend.isAlive()) {
+                if (backend == null || !backend.isQueryAvailable()) {
                     if (LOG.isDebugEnabled()) {
-                        LOG.debug("backend {} not exists or is not alive for 
replica {}", backendId,
+                        LOG.debug("backend {} not exists or is not query 
available for replica {}", backendId,
                                 replica.getId());
                     }
                     String err = "replica " + replica.getId() + "'s backend " 
+ backendId
                             + (backend != null ? " with tag " + 
backend.getLocationTag() : "")
-                            + " does not exist or not alive";
+                            + " does not exist or is not query available";
                     errs.add(err);
                     continue;
                 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/distribute/worker/LoadBalanceScanWorkerSelectorBackendSelectionTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/distribute/worker/LoadBalanceScanWorkerSelectorBackendSelectionTest.java
index 68c99e8ccfd..5629b9da18e 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/distribute/worker/LoadBalanceScanWorkerSelectorBackendSelectionTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/distribute/worker/LoadBalanceScanWorkerSelectorBackendSelectionTest.java
@@ -22,6 +22,7 @@ import org.apache.doris.common.Config;
 import org.apache.doris.common.NereidsException;
 import org.apache.doris.common.UserException;
 import org.apache.doris.datasource.InternalCatalog;
+import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.resource.BackendSelection;
 import org.apache.doris.resource.BackendSelectionManager;
@@ -178,13 +179,36 @@ class LoadBalanceScanWorkerSelectorBackendSelectionTest {
     }
 
     @Test
-    void testSingleReplicaSelectionPreservesUnavailableWorkerBehavior() throws 
Exception {
+    void testSingleReplicaSelectionRejectsUnavailableWorker() {
         Backend unavailable = backend(1L, "preferred");
         unavailable.setAlive(false);
         DistributedPlanWorkerManager workerManager = 
workerManager(unavailable);
         LoadBalanceScanWorkerSelector selector = new 
LoadBalanceScanWorkerSelector(
                 workerManager, new ConnectContext(), false);
 
+        Assertions.assertThrows(AnalysisException.class,
+                () -> select(selector, locations(location(1L)), 100L));
+    }
+
+    @Test
+    void testSingleReplicaSelectionRejectsQueryDisabledWorker() {
+        Backend queryDisabled = backend(1L, "preferred");
+        queryDisabled.setQueryDisabled(true);
+        DistributedPlanWorkerManager workerManager = 
workerManager(queryDisabled);
+        LoadBalanceScanWorkerSelector selector = new 
LoadBalanceScanWorkerSelector(
+                workerManager, new ConnectContext(), false);
+
+        Assertions.assertThrows(AnalysisException.class,
+                () -> select(selector, locations(location(1L)), 100L));
+    }
+
+    @Test
+    void testSingleReplicaSelectionKeepsAvailableWorker() throws Exception {
+        Backend available = backend(1L, "preferred");
+        DistributedPlanWorkerManager workerManager = workerManager(available);
+        LoadBalanceScanWorkerSelector selector = new 
LoadBalanceScanWorkerSelector(
+                workerManager, new ConnectContext(), false);
+
         WorkerScanRanges selected = select(selector, locations(location(1L)), 
100L);
 
         Assertions.assertEquals(1L, selected.worker.id());
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/planner/OlapScanNodeBackendSelectionConfigTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/planner/OlapScanNodeBackendSelectionConfigTest.java
index 62c8996e0ce..df0d2a0de2c 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/planner/OlapScanNodeBackendSelectionConfigTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/planner/OlapScanNodeBackendSelectionConfigTest.java
@@ -288,6 +288,54 @@ class OlapScanNodeBackendSelectionConfigTest {
         }
     }
 
+    @Test
+    void testQueryDisabledBackendExcludedFromScanCandidates() throws Exception 
{
+        Replica replica = replica(1, 10);
+        Backend disabledBackend = backend(1, "group_a");
+        disabledBackend.setQueryDisabled(true);
+        Backend otherBackend = backend(2, "group_b");
+
+        Tablet tablet = Mockito.mock(Tablet.class);
+        Mockito.when(tablet.getId()).thenReturn(20L);
+        Mockito.when(tablet.getQueryableReplicas(
+                        Mockito.eq(10L), Mockito.anyMap(), Mockito.eq(false)))
+                .thenReturn(new ArrayList<>(ImmutableList.of(replica)));
+        Mockito.when(tablet.getCooldownReplicaId()).thenReturn(-1L);
+
+        UserException ex = Assertions.assertThrows(UserException.class,
+                () -> createScanRanges(tablet, disabledBackend, otherBackend));
+        Assertions.assertTrue(ex.getMessage().contains("has no queryable 
replicas"));
+    }
+
+    @Test
+    void testCooldownReplicaOnQueryDisabledBackendIsNotNarrowed() throws 
Exception {
+        Replica preferredReplica = replica(1, 10);
+        Replica cooldownReplica = replica(2, 10);
+        Backend preferredBackend = backend(1, "group_a");
+        Backend cooldownBackend = backend(2, "group_b");
+        cooldownBackend.setQueryDisabled(true);
+
+        SessionVariable sessionVariable = new SessionVariable();
+        sessionVariable.enableCooldownReplicaAffinity = true;
+        ConnectContext context = Mockito.mock(ConnectContext.class);
+        QueryState queryState = Mockito.mock(QueryState.class);
+        Mockito.when(context.getSessionVariable()).thenReturn(sessionVariable);
+        Mockito.when(context.getState()).thenReturn(queryState);
+        Mockito.when(context.getStatementContext()).thenReturn(new 
StatementContext());
+        Mockito.when(context.getComputeGroupSafely()).thenReturn(null);
+
+        try (MockedStatic<ConnectContext> mockedContext = 
Mockito.mockStatic(ConnectContext.class)) {
+            mockedContext.when(ConnectContext::get).thenReturn(context);
+
+            List<TScanRangeLocations> scanRanges = createScanRanges(
+                    preferredReplica, cooldownReplica, preferredBackend, 
cooldownBackend);
+
+            Assertions.assertEquals(ImmutableList.of(1L),
+                    scanRanges.get(0).getLocations().stream()
+                            .map(location -> 
location.getBackendId()).collect(Collectors.toList()));
+        }
+    }
+
     private List<TScanRangeLocations> createScanRanges(Replica 
preferredReplica, Replica cooldownReplica,
             Backend preferredBackend, Backend cooldownBackend) throws 
Exception {
         Tablet tablet = Mockito.mock(Tablet.class);


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

Reply via email to