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


##########
fe/fe-core/src/main/java/org/apache/doris/qe/Coordinator.java:
##########
@@ -18,747 +18,634 @@
 package org.apache.doris.qe;
 
 import org.apache.doris.analysis.DescriptorTable;
-import org.apache.doris.analysis.DescriptorToThriftConverter;
 import org.apache.doris.analysis.StorageBackend;
 import org.apache.doris.arrowflight.results.FlightSqlEndpointsLocation;
-import org.apache.doris.catalog.AIResource;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.catalog.FsBroker;
-import org.apache.doris.catalog.Resource;
+import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.Config;
-import org.apache.doris.common.MarkedCountDownLatch;
-import org.apache.doris.common.Pair;
-import org.apache.doris.common.Reference;
 import org.apache.doris.common.Status;
 import org.apache.doris.common.ThreadPoolManager;
 import org.apache.doris.common.UserException;
 import org.apache.doris.common.profile.ExecutionProfile;
-import org.apache.doris.common.profile.SummaryProfile;
 import org.apache.doris.common.util.DebugUtil;
-import org.apache.doris.common.util.ListUtil;
-import org.apache.doris.datasource.scan.ExternalScanNode;
-import org.apache.doris.datasource.scan.FileQueryScanNode;
-import org.apache.doris.datasource.split.FileScanRangeCompactor;
-import org.apache.doris.load.loadv2.LoadJob;
-import org.apache.doris.metric.MetricRepo;
+import org.apache.doris.datasource.InternalCatalog;
 import org.apache.doris.nereids.NereidsPlanner;
 import org.apache.doris.nereids.stats.StatsErrorEstimator;
-import org.apache.doris.nereids.trees.plans.distribute.DistributedPlan;
-import org.apache.doris.nereids.trees.plans.distribute.FragmentIdMapping;
-import org.apache.doris.nereids.trees.plans.physical.TopnFilter;
-import org.apache.doris.planner.DataPartition;
+import org.apache.doris.nereids.trees.plans.distribute.PipelineDistributedPlan;
+import org.apache.doris.nereids.trees.plans.distribute.worker.BackendWorker;
+import 
org.apache.doris.nereids.trees.plans.distribute.worker.DistributedPlanWorker;
+import org.apache.doris.nereids.trees.plans.distribute.worker.job.AssignedJob;
+import org.apache.doris.nereids.util.Utils;
 import org.apache.doris.planner.DataSink;
-import org.apache.doris.planner.DataStreamSink;
-import org.apache.doris.planner.DictionarySink;
-import org.apache.doris.planner.ExceptNode;
-import org.apache.doris.planner.ExchangeNode;
-import org.apache.doris.planner.HashJoinNode;
-import org.apache.doris.planner.IntersectNode;
-import org.apache.doris.planner.MultiCastDataSink;
-import org.apache.doris.planner.MultiCastPlanFragment;
-import org.apache.doris.planner.OlapScanNode;
 import org.apache.doris.planner.OlapTableSink;
 import org.apache.doris.planner.PlanFragment;
-import org.apache.doris.planner.PlanFragmentId;
-import org.apache.doris.planner.PlanNode;
-import org.apache.doris.planner.PlanNodeId;
-import org.apache.doris.planner.Planner;
 import org.apache.doris.planner.ResultFileSink;
 import org.apache.doris.planner.ResultSink;
-import org.apache.doris.planner.RuntimeFilter;
-import org.apache.doris.planner.RuntimeFilterId;
 import org.apache.doris.planner.ScanNode;
 import org.apache.doris.planner.SchemaScanNode;
-import org.apache.doris.planner.SetOperationNode;
-import org.apache.doris.planner.SortNode;
-import org.apache.doris.planner.TVFTableSink;
-import org.apache.doris.planner.UnionNode;
-import org.apache.doris.proto.InternalService;
-import org.apache.doris.proto.InternalService.PExecPlanFragmentResult;
-import org.apache.doris.proto.InternalService.PExecPlanFragmentStartRequest;
-import org.apache.doris.proto.Types;
-import org.apache.doris.proto.Types.PUniqueId;
 import org.apache.doris.qe.QueryStatisticsItem.FragmentInstanceInfo;
+import org.apache.doris.qe.runtime.LoadProcessor;
+import org.apache.doris.qe.runtime.MultiFragmentsPipelineTask;
+import org.apache.doris.qe.runtime.PipelineExecutionTask;
+import org.apache.doris.qe.runtime.PipelineExecutionTaskBuilder;
+import org.apache.doris.qe.runtime.QueryProcessor;
+import org.apache.doris.qe.runtime.SingleFragmentPipelineTask;
+import org.apache.doris.qe.runtime.ThriftPlansBuilder;
 import org.apache.doris.resource.BackendSelection;
 import org.apache.doris.resource.BackendSelectionManager;
 import org.apache.doris.resource.workloadgroup.QueryQueue;
 import org.apache.doris.resource.workloadgroup.QueueToken;
 import org.apache.doris.resource.workloadgroup.WorkloadGroup;
-import org.apache.doris.rpc.BackendServiceProxy;
-import org.apache.doris.rpc.RpcException;
-import org.apache.doris.service.FrontendOptions;
 import org.apache.doris.system.Backend;
-import org.apache.doris.system.SystemInfoService;
-import org.apache.doris.task.LoadEtlTask;
-import org.apache.doris.thrift.PaloInternalServiceVersion;
-import org.apache.doris.thrift.TAIResource;
-import org.apache.doris.thrift.TBrokerScanRange;
-import org.apache.doris.thrift.TDataSinkType;
-import org.apache.doris.thrift.TDescriptorTable;
 import org.apache.doris.thrift.TErrorTabletInfo;
-import org.apache.doris.thrift.TEsScanRange;
-import org.apache.doris.thrift.TExternalScanRange;
-import org.apache.doris.thrift.TFileScanRange;
-import org.apache.doris.thrift.TFileScanRangeParams;
-import org.apache.doris.thrift.TFragmentInstanceReport;
 import org.apache.doris.thrift.TNetworkAddress;
-import org.apache.doris.thrift.TOlapTableSink;
-import org.apache.doris.thrift.TPaloScanRange;
 import org.apache.doris.thrift.TPipelineFragmentParams;
 import org.apache.doris.thrift.TPipelineFragmentParamsList;
-import org.apache.doris.thrift.TPipelineInstanceParams;
 import org.apache.doris.thrift.TPipelineWorkloadGroup;
-import org.apache.doris.thrift.TPlanFragment;
-import org.apache.doris.thrift.TPlanFragmentDestination;
-import org.apache.doris.thrift.TQueryGlobals;
 import org.apache.doris.thrift.TQueryOptions;
 import org.apache.doris.thrift.TQueryType;
 import org.apache.doris.thrift.TReportExecStatusParams;
-import org.apache.doris.thrift.TResourceInfo;
-import org.apache.doris.thrift.TRuntimeFilterParams;
-import org.apache.doris.thrift.TRuntimeFilterTargetParamsV2;
-import org.apache.doris.thrift.TScanRange;
-import org.apache.doris.thrift.TScanRangeLocation;
-import org.apache.doris.thrift.TScanRangeLocations;
-import org.apache.doris.thrift.TScanRangeParams;
 import org.apache.doris.thrift.TStatusCode;
 import org.apache.doris.thrift.TTabletCommitInfo;
-import org.apache.doris.thrift.TTopnFilterDesc;
 import org.apache.doris.thrift.TUniqueId;
-import org.apache.doris.transaction.CommitDataSerializer;
-import org.apache.doris.transaction.Transaction;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-import com.google.common.collect.HashMultiset;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Multiset;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.protobuf.ByteString;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.tuple.ImmutableTriple;
-import org.apache.commons.lang3.tuple.Triple;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.apache.thrift.TException;
-import org.apache.thrift.TSerializer;
-import org.apache.thrift.protocol.TCompactProtocol;
-import org.jetbrains.annotations.NotNull;
-import org.joda.time.DateTime;
-
-import java.security.SecureRandom;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
+
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Optional;
-import java.util.Random;
 import java.util.Set;
-import java.util.TreeSet;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
+/** Coordinator */
 public class Coordinator implements CoordInterface {
+    public static final ExecutorService backendRpcCallbackExecutor = 
ThreadPoolManager.newDaemonProfileThreadPool(
+            32, 100, "backend-rpc-callback", true);
+
     private static final Logger LOG = LogManager.getLogger(Coordinator.class);
 
-    public static final String localIP = FrontendOptions.getLocalHostAddress();
+    protected final CoordinatorContext coordinatorContext;
 
-    // Random is used to shuffle instances of partitioned
-    private static final Random instanceRandom = new SecureRandom();
+    protected volatile PipelineExecutionTask executionTask;
 
-    public static ExecutorService backendRpcCallbackExecutor = 
ThreadPoolManager.newDaemonProfileThreadPool(
-            32, 100, "backend-rpc-callback", true);
+    private final boolean needEnqueue;
+
+    // sql execution
+    public Coordinator(ConnectContext context,
+            NereidsPlanner planner, StatsErrorEstimator statsErrorEstimator) {
+        this.coordinatorContext = CoordinatorContext.buildForSql(planner, 
this);
+        this.needEnqueue = true;
+
+        DataSink dataSink = coordinatorContext.dataSink;
+        // output to mysql or to file
+        if ((dataSink instanceof ResultSink || dataSink instanceof 
ResultFileSink)) {
+            setForQuery();
+        } else {
+            setForInsert(-1L);
+        }
 
-    // Overall status of the entire query; set to the first reported fragment 
error
-    // status or to CANCELLED, if Cancel() is called.
-    private Status queryStatus = new Status();
-
-    // save of related backends of this query
-    private Map<TNetworkAddress, Long> addressToBackendID = Maps.newHashMap();
-
-    protected ImmutableMap<Long, Backend> idToBackend = ImmutableMap.of();
-
-    private final TDescriptorTable descTable;
-    private FragmentIdMapping<DistributedPlan> distributedPlans;
-
-    // scan node id -> TFileScanRangeParams
-    private Map<Integer, TFileScanRangeParams> fileScanRangeParamsMap = 
Maps.newHashMap();
-
-    // Why do we use query global?
-    // When `NOW()` function is in sql, we need only one now(),
-    // but, we execute `NOW()` distributed.
-    // So we make a query global value here to make one `now()` value in one 
query process.
-    private final TQueryGlobals queryGlobals = new TQueryGlobals();
-    private TQueryOptions queryOptions;
-    private TNetworkAddress coordAddress;
-    // fe audit log in connected FE,if a query is forward
-    // we should send the connected FE to be,
-    // then be report query statistics to the connected FE
-    private TNetworkAddress currentConnectFE;
-
-    // protects all fields below
-    private final Lock lock = new ReentrantLock();
-
-    // If true, the query is done returning all results.  It is possible that 
the
-    // coordinator still needs to wait for cleanup on remote fragments (e.g. 
queries
-    // with limit)
-    // Once this is set to true, errors from remote fragments are ignored.
-    private boolean returnedAllResults = false;
-
-    // populated in computeFragmentExecParams()
-    private final Map<PlanFragmentId, FragmentExecParams> 
fragmentExecParamsMap = Maps.newHashMap();
-
-    private final List<PlanFragment> fragments;
-
-    private Map<Long, PipelineExecContexts> beToPipelineExecCtxs = 
Maps.newHashMap();
-    private final Set<Long> dispatchedBackendIdsForAudit = 
Sets.newConcurrentHashSet();
-
-    private final Map<Pair<Integer, Long>, PipelineExecContext> 
pipelineExecContexts = new HashMap<>();
-    private final List<PipelineExecContext> needCheckPipelineExecContexts = 
Lists.newArrayList();
-    private List<ResultReceiver> receivers = Lists.newArrayList();
-    private ResultReceiverConsumer receiverConsumer;
-    private final List<ScanNode> scanNodes;
-    private int scanRangeNum = 0;
-    // number of instances of this query, equals to
-    // number of backends executing plan fragments on behalf of this query;
-    // set in computeFragmentExecParams();
-    // same as backend_exec_states_.size() after Exec()
-    private final Set<TUniqueId> instanceIds = Sets.newHashSet();
-
-    private int numReceivedRows = 0;
-
-    private List<String> deltaUrls;
-    private Map<String, String> loadCounters;
-    private String trackingUrl;
-    private String firstErrorMsg;
-    // related txnId and label of group commit
-    // Final reports race with status readers, so the transaction identity 
must be safely published.
-    private volatile long txnId;
-    private String label;
-
-    // for export
-    private List<String> exportFiles;
-
-    private final List<TTabletCommitInfo> commitInfos = Lists.newArrayList();
-    private final List<TErrorTabletInfo> errorTabletInfos = 
Lists.newArrayList();
-
-    // Input parameter
-    private long jobId = -1; // job which this task belongs to
-    private TUniqueId queryId;
-
-    // a timestamp represent the absolute timeout
-    // eg, System.currentTimeMillis() + executeTimeoutS * 1000
-    private long timeoutDeadline;
-
-    private boolean enableShareHashTableForBroadcastJoin = false;
-
-    private boolean useNereids = false;
-
-    private Backend groupCommitBackend;
-
-    // Runtime filter merge instance address and ID
-    private TNetworkAddress runtimeFilterMergeAddr;
-    private TUniqueId runtimeFilterMergeInstanceId;
-    // Runtime filter ID to the target instance address of the fragment,
-    // that is expected to use this runtime filter, the instance address is 
not repeated
-    private Map<RuntimeFilterId, List<FRuntimeFilterTargetParam>> 
ridToTargetParam = Maps.newHashMap();
-    // The runtime filter that expects the instance to be used
-    private List<RuntimeFilter> assignedRuntimeFilters = new ArrayList<>();
-    private List<TopnFilter> topnFilters = new ArrayList<>();
-    // Runtime filter ID to the builder instance number
-    private Map<RuntimeFilterId, Integer> ridToBuilderNum = Maps.newHashMap();
-    private ConnectContext context;
-
-    private StatsErrorEstimator statsErrorEstimator;
-
-    // A countdown latch to mark the completion of each instance.
-    // use for old pipeline
-    // instance id -> dummy value
-    private MarkedCountDownLatch<TUniqueId, Long> instancesDoneLatch = null;
-
-    // A countdown latch to mark the completion of each fragment. use for 
pipelineX
-    // fragmentid -> backendid
-    private MarkedCountDownLatch<Integer, Long> fragmentsDoneLatch = null;
-
-    public void setGroupCommitBe(Backend backend) {
-        this.groupCommitBackend = backend;
+        syncLocalShufflePlannerOption();
+        Preconditions.checkState(!planner.getFragments().isEmpty()
+                && coordinatorContext.instanceNum.get() > 0, "Fragment and 
Instance can not be empty˚");
     }
 
-    public void setTWorkloadGroups(List<TPipelineWorkloadGroup> 
tWorkloadGroups) {
-        this.tWorkloadGroups = tWorkloadGroups;
+    // load, with JobId
+    public Coordinator(ConnectContext context,
+            NereidsPlanner planner, StatsErrorEstimator statsErrorEstimator, 
long jobId) {
+        this.coordinatorContext = CoordinatorContext.buildForSql(planner, 
this);
+        this.needEnqueue = true;
+
+        // we don't need to check the dataSink, Because setting jobId means 
this must be a load operation
+        setForInsert(jobId);
+        syncLocalShufflePlannerOption();
+        Preconditions.checkState(!planner.getFragments().isEmpty()
+                && coordinatorContext.instanceNum.get() > 0, "Fragment and 
Instance can not be empty˚");
     }
 
-    public long getNumReceivedRows() {
-        return numReceivedRows;
+    // broker load
+    public Coordinator(Long jobId, TUniqueId queryId, DescriptorTable 
descTable,
+            List<PlanFragment> fragments, List<PipelineDistributedPlan> 
distributedPlans,
+            List<ScanNode> scanNodes, String timezone, boolean 
loadZeroTolerance,
+            boolean enableProfile) {
+        this.coordinatorContext = CoordinatorContext.buildForLoad(
+                this, jobId, queryId, fragments, distributedPlans, scanNodes,
+                descTable, timezone, loadZeroTolerance, enableProfile
+        );
+        // same reason in `setForInsert`
+        this.coordinatorContext.queryOptions.setDisableFileCache(true);
+        this.needEnqueue = false;
+        syncLocalShufflePlannerOption();
+
+        Preconditions.checkState(!fragments.isEmpty()
+                && coordinatorContext.instanceNum.get() > 0, "Fragment and 
Instance can not be empty˚");
     }
 
-    public List<TPipelineWorkloadGroup> getTWorkloadGroups() {
-        return tWorkloadGroups;
+    private void syncLocalShufflePlannerOption() {
+        coordinatorContext.queryOptions.setEnableLocalShufflePlanner(
+                coordinatorContext.distributedPlans != null
+                && !coordinatorContext.distributedPlans.isEmpty()
+                && coordinatorContext.connectContext != null
+                && 
coordinatorContext.connectContext.getSessionVariable().isEnableLocalShufflePlanner());
     }
 
-    private List<TPipelineWorkloadGroup> tWorkloadGroups = 
Lists.newArrayList();
+    @Override
+    public void exec() throws Exception {
+        Status status = getQueryStatus();
+        if (!status.ok()) {
+            throw new UserException(status.getErrorMsg());
+        }
+        enqueue(coordinatorContext.connectContext);
+
+        processTopSink(coordinatorContext, 
coordinatorContext.topDistributedPlan);
+        status = getQueryStatus();
+        if (!status.ok()) {
+            throw new UserException(status.getErrorMsg());
+        }
+
+        QeProcessorImpl.INSTANCE.registerInstances(coordinatorContext.queryId, 
coordinatorContext.instanceNum.get());
 
-    private final ExecutionProfile executionProfile;
+        Map<DistributedPlanWorker, TPipelineFragmentParamsList> 
workerToFragments
+                = ThriftPlansBuilder.plansToThrift(coordinatorContext);
+        executionTask = PipelineExecutionTaskBuilder.build(coordinatorContext, 
workerToFragments);
+        executionTask.execute();
+    }
 
-    private volatile QueueToken queueToken = null;
-    private QueryQueue queryQueue = null;
+    public boolean isTimeout() {
+        return System.currentTimeMillis() > 
coordinatorContext.timeoutDeadline.get();
+    }
 
-    public ExecutionProfile getExecutionProfile() {
-        return executionProfile;
+    @Override
+    public void cancel(Status cancelReason) {
+        if (cancelReason.ok()) {
+            throw new RuntimeException("Should use correct cancel reason, but 
it is " + cancelReason);
+        }
+
+        TUniqueId queryId = coordinatorContext.queryId;
+        try {
+            Status originQueryStatus = 
coordinatorContext.updateStatusIfOk(cancelReason);
+            if (!originQueryStatus.ok()) {
+                if (LOG.isDebugEnabled()) {
+                    // Print an error stack here to know why send cancel again.
+                    LOG.warn("Query {} already in abnormal status {}, but 
received cancel again,"
+                                    + "so that send cancel to BE again",
+                            DebugUtil.printId(queryId), 
originQueryStatus.toString(),
+                            new Exception("cancel failed"));
+                }
+            } else {
+                LOG.warn("Cancel execution of query {}, this is a outside 
invoke, cancelReason {}",
+                        DebugUtil.printId(queryId), cancelReason);
+            }
+        } finally {
+            // Publishing the status above can itself cancel a partially 
initialized processor. Start the
+            // non-throwing cleanup scope before that publication so the queue 
token, the scan nodes, and
+            // the final internal cancel are never skipped, even if the 
publication was only half wired.
+            try {
+                
coordinatorContext.getQueueToken().ifPresent(QueueToken::cancel);
+                // Scan cleanup is best-effort and must never escape: a 
throwing scan would otherwise skip
+                // the remaining scans (and the caller's coordinator close), 
masking the retained reason. A
+                // scan whose first stop() threw still removes its own sources 
on the close-time retry
+                // because SplitAssignment.stop() is idempotent.
+                for (ScanNode scanNode : coordinatorContext.scanNodes) {
+                    try {
+                        scanNode.stop();
+                    } catch (Throwable t) {
+                        LOG.error("error happens when scannode stop during 
cancel, query id: {}",
+                                DebugUtil.printId(queryId), t);
+                    }
+                }
+            } finally {
+                cancelInternal(cancelReason);
+            }
+        }
     }
 
-    // True if all scan node are ExternalScanNode.
-    private boolean isAllExternalScan = true;
+    public QueryProcessor asQueryProcessor() {
+        return coordinatorContext.asQueryProcessor();
+    }
 
-    // Used for query/insert
-    public Coordinator(ConnectContext context, Planner planner,
-            StatsErrorEstimator statsErrorEstimator) {
-        this(context, planner);
-        this.statsErrorEstimator = statsErrorEstimator;
+    public JobProcessor getJobProcessor() {
+        return coordinatorContext.getJobProcessor();
     }
 
-    public Coordinator(ConnectContext context, Planner planner,
-            StatsErrorEstimator statsErrorEstimator, long jobId) {
-        this(context, planner, statsErrorEstimator);
-        this.jobId = jobId;
+    public LoadProcessor asLoadProcessor() {
+        return coordinatorContext.asLoadProcessor();
     }
 
-    // Used for query/insert/test
-    public Coordinator(ConnectContext context, Planner planner) {
-        this.context = context;
-        this.queryId = context.queryId();
-        this.fragments = planner.getFragments();
-        this.scanNodes = planner.getScanNodes();
-        this.descTable = 
DescriptorToThriftConverter.toThrift(planner.getDescTable());
+    public void setTWorkloadGroups(List<TPipelineWorkloadGroup> 
tWorkloadGroups) {
+        coordinatorContext.setWorkloadGroups(tWorkloadGroups);
+    }
 
-        this.returnedAllResults = false;
-        this.enableShareHashTableForBroadcastJoin = 
context.getSessionVariable().enableShareHashTableForBroadcastJoin;
+    public List<TPipelineWorkloadGroup> getTWorkloadGroups() {
+        return coordinatorContext.getWorkloadGroups();
+    }
 
-        this.queryOptions = CoordinatorContext.initQueryOptions(
-                context, planner.getQueryOptions().getBeExecVersion());
-        // Old Coordinator never plans local exchange in FE. Force BE to plan 
its own.
-        this.queryOptions.setEnableLocalShufflePlanner(false);
-        useNereids = planner instanceof NereidsPlanner;
-        if (!useNereids) {
-            // Enable local shuffle on pipelineX engine only if Nereids 
planner is applied.
-            queryOptions.setEnableLocalShuffle(false);
-        } else {
-            distributedPlans = ((NereidsPlanner) 
planner).getDistributedPlans();
-        }
-        CoordinatorContext.refreshQueryGlobals(this.queryGlobals, context);
-        this.queryGlobals.setLoadZeroTolerance(false);
-        this.assignedRuntimeFilters = planner.getRuntimeFilters();
-        this.topnFilters = planner.getTopnFilters();
-
-        List<Integer> fragmentIds = new ArrayList<>();
-        for (PlanFragment fragment : fragments) {
-            fragmentIds.add(fragment.getFragmentId().asInt());
+    public boolean isQueryCancelled() {
+        return coordinatorContext.readCloneStatus().isCancelled();
+    }
+
+    protected Status getQueryStatus() {
+        return coordinatorContext.readCloneStatus();
+    }
+
+    @Override
+    public RowBatch getNext() throws Exception {
+        return coordinatorContext.asQueryProcessor().getNext();
+    }
+
+    public boolean mustOutliveDispatch() {
+        return 
coordinatorContext.scanNodes.stream().anyMatch(ScanNode::coordinatorMustOutliveDispatch);
+    }
+
+    public List<Long> getScanBackendIds() {
+        return coordinatorContext.scanNodes.stream()
+                .flatMap(scan -> scan.getScanBackendIds().stream())
+                .collect(Collectors.toList());
+    }
+
+    public Status shouldCancel(List<Backend> currentBackends) {
+        PipelineExecutionTask task = executionTask;
+        if (task == null) {
+            return Status.OK;
         }
-        this.executionProfile = new ExecutionProfile(queryId, fragmentIds);
-    }
-
-    // Used for broker load task/export task/update coordinator
-    // Constructor of Coordinator is too complicated.
-    public Coordinator(Long jobId, TUniqueId queryId, DescriptorTable 
descTable, List<PlanFragment> fragments,
-            List<ScanNode> scanNodes, String timezone, boolean 
loadZeroTolerance, boolean enableProfile) {
-        this.jobId = jobId;
-        this.queryId = queryId;
-        this.descTable = DescriptorToThriftConverter.toThrift(descTable);
-        this.fragments = fragments;
-        this.scanNodes = scanNodes;
-        this.queryOptions = new TQueryOptions();
-        this.queryOptions.setEnableProfile(enableProfile);
-        this.queryOptions.setProfileLevel(2);
-        CoordinatorContext.setQueryGlobalsForLoad(this.queryGlobals, timezone, 
loadZeroTolerance);
-        this.queryOptions.setBeExecVersion(Config.be_exec_version);
-        this.queryOptions.setNewVersionUnixTimestamp(true);
-        this.queryOptions.setNewVersionPercentile(true);
-        this.queryOptions.setNewVersionBitmapOpCount(true);
-
-        List<Integer> fragmentIds = new ArrayList<>();
-        for (PlanFragment fragment : fragments) {
-            fragmentIds.add(fragment.getFragmentId().asInt());
+        Set<Long> aliveBackendIds = 
currentBackends.stream().filter(Backend::isAlive)
+                .map(Backend::getId).collect(Collectors.toSet());
+        for (Map.Entry<BackendWorker, MultiFragmentsPipelineTask> entry : 
task.getChildrenTasks().entrySet()) {
+            // Remote Doris workers belong to another catalog and are absent 
from the local heartbeat inventory.
+            if (entry.getKey().getCatalogId() != 
InternalCatalog.INTERNAL_CATALOG_ID) {
+                continue;
+            }
+            MultiFragmentsPipelineTask backendTask = entry.getValue();
+            if (!aliveBackendIds.contains(backendTask.getBackend().getId())) {
+                return new Status(TStatusCode.CANCELLED, "Backend {} not 
exists or dead, query {} should be cancelled",
+                        backendTask.getBackend(), 
DebugUtil.printId(getQueryId()));
+            }
+            for (SingleFragmentPipelineTask fragmentTask : 
backendTask.getChildrenTasks().values()) {
+                Status status = 
fragmentTask.getBackendHealthStatus(getJobId());

Review Comment:
   [P1] Preserve cancellation when a backend epoch changes from the 
uninitialized value — `shouldCancel()` now delegates to 
`SingleFragmentPipelineTask.getBackendHealthStatus()`, whose guard only reports 
a restart when both the captured and current epochs are positive. A `Backend` 
starts with `lastStartTime=-1` (and can be observed at epoch 0 during replay), 
then heartbeat processing updates it to a positive start time; the legacy check 
treated any `previous != current && current != 0` as a restart. With the new 
guard, a query whose fragment was captured at -1/0 can keep waiting for reports 
until its execution timeout after that backend restarts, instead of being 
cancelled promptly. Keep the zero-current upgrade exception, but treat a 
nonpositive captured epoch followed by a positive current epoch as unhealthy 
and add a regression.



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