CalvinKirs commented on code in PR #67186:
URL: https://github.com/apache/doris/pull/67186#discussion_r3879685062


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/PreloadExternalMetadata.java:
##########
@@ -58,6 +62,7 @@ public List<Rule> buildRules() {
      */
     public ExternalMetadataPreloadResult executePreload(StatementContext 
statementContext) {
         long preloadStartTime = TimeUtils.getStartTimeMs();
+        preloadCloudMtmvRefreshContexts(statementContext);

Review Comment:
   Fixed in 9487b21cb47. Before building each cloud MTMV refresh context, the 
preload stage now installs/reuses the PCT table snapshot through 
StatementContext.loadSnapshots. The added test verifies snapshot loading 
precedes MTMVRefreshContext.buildContext, keeping mapping/freshness/scan on the 
statement pin.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java:
##########
@@ -307,6 +308,8 @@ public enum TableFrom {
 
     // Record mtmv and valid partitions map because this is time-consuming 
behavior
     private final Map<BaseTableInfo, Collection<Partition>> 
mvCanRewritePartitionsMap = new HashMap<>();
+    // Cloud MTMV versions are loaded before planner table locks and 
revalidated from their local caches later.
+    private final Map<BaseTableInfo, MTMVRefreshContext> 
preloadedMtmvRefreshContexts = new HashMap<>();

Review Comment:
   Fixed in 9487b21cb47. StatementContext.resetMvccSnapshots now clears 
preloadedMtmvRefreshContexts together with the other execution-scoped snapshot 
state. StatementContextTest verifies a preloaded MTMV context is removed on 
reset.



##########
fe/fe-connector/fe-connector-hms/src/main/java/org/apache/doris/connector/hms/HmsRemoteCallTracking.java:
##########
@@ -0,0 +1,273 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.connector.hms;
+
+import org.apache.doris.connector.spi.ConnectorOperationAbortedException;
+import org.apache.doris.connector.spi.ConnectorOperationControl;
+
+import shade.doris.hive.org.apache.thrift.TException;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/** Bridges one logical client invocation to every raw HMS attempt made by 
RetryingMetaStoreClient. */
+final class HmsRemoteCallTracking {
+
+    private static final long CONTROL_CHECK_MILLIS = 100L;
+    private static final ThreadLocal<Context> CURRENT = new ThreadLocal<>();
+    private static final ScheduledExecutorService CONTROL_WATCHDOG =
+            Executors.newSingleThreadScheduledExecutor(runnable -> {
+                Thread thread = new Thread(runnable, 
"hms-operation-control-watchdog");
+                thread.setDaemon(true);
+                return thread;
+            });
+
+    private HmsRemoteCallTracking() {
+    }
+
+    static <T> T withTracker(HmsPartitionBatchLoader.RemoteCallTracker 
tracker, int itemCount,
+            ConnectorOperationControl operationControl, Callable<T> 
clientInvocation) throws Exception {
+        Context previous = CURRENT.get();
+        operationControl.checkActive();
+        Context context = new Context(tracker, itemCount, operationControl, 
Thread.currentThread());
+        CURRENT.set(context);
+        ScheduledFuture<?> watchdog = operationControl == 
ConnectorOperationControl.NONE
+                ? null : CONTROL_WATCHDOG.scheduleWithFixedDelay(
+                        context::checkOperation, CONTROL_CHECK_MILLIS, 
CONTROL_CHECK_MILLIS, TimeUnit.MILLISECONDS);
+        try {
+            try {
+                T result = clientInvocation.call();
+                operationControl.checkActive();
+                return result;
+            } catch (Exception e) {
+                ConnectorOperationAbortedException abort = context.getAbort();
+                if (abort != null) {
+                    throw abort;
+                }
+                if (causedByInterruptedException(e)) {
+                    // RetryingMetaStoreClient's retry delay uses 
Thread.sleep. A direct Future.cancel(true)
+                    // can interrupt that sleep before the watchdog observes 
the caller control. The Hive dynamic
+                    // proxy wraps the undeclared InterruptedException in 
UndeclaredThrowableException, and sleep
+                    // clears the flag while throwing. Restore it and preserve 
cancellation semantics; after a
+                    // failed wire attempt the pooled client is ambiguous and 
the specialized abort taints it.
+                    Thread.currentThread().interrupt();
+                    throw context.interruptedAbort();
+                }
+                throw e;
+            }
+        } finally {
+            context.finish();
+            if (watchdog != null) {
+                watchdog.cancel(false);
+            }
+            if (context.wasInterruptedByWatchdog()) {
+                Thread.interrupted();
+            }
+            if (previous == null) {
+                CURRENT.remove();
+            } else {
+                CURRENT.set(previous);
+            }
+        }
+    }
+
+    private static boolean causedByInterruptedException(Throwable failure) {
+        for (Throwable cause = failure; cause != null; cause = 
cause.getCause()) {
+            if (cause instanceof InterruptedException) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    static <T> T trackWireAttempt(ThriftCall<T> wireAttempt) throws TException 
{
+        Context context = CURRENT.get();
+        if (context == null) {
+            return wireAttempt.call();
+        }
+        context.operationControl.checkActive();
+        context.startWireAttempt();
+        try {
+            T result = context.tracker.call(context.itemCount, 
wireAttempt::call);
+            context.finishWireAttempt(false);
+            context.operationControl.checkActive();
+            return result;
+        } catch (TException e) {
+            context.finishWireAttempt(true);
+            throw e;
+        } catch (RuntimeException e) {
+            context.finishWireAttempt(false);
+            throw e;
+        } catch (Exception e) {
+            context.finishWireAttempt(false);
+            throw new TException(e);
+        }
+    }
+
+    static void checkReconnectActive() {
+        Context context = CURRENT.get();
+        if (context != null) {
+            context.checkRetryPhaseActive();
+        }
+    }
+
+    static void markReconnectFailure() {
+        Context context = CURRENT.get();
+        if (context != null) {
+            context.markReconnectFailure();
+        }
+    }
+
+    static void markReconnectSuccess() {
+        Context context = CURRENT.get();
+        if (context != null) {
+            context.markReconnectSuccess();
+        }
+    }
+
+    static boolean shouldTaintClient(ConnectorOperationAbortedException abort) 
{
+        return abort instanceof RetryPhaseOperationAbortedException;
+    }
+
+    @FunctionalInterface
+    interface ThriftCall<T> {
+        T call() throws TException;
+    }
+
+    private static final class Context {
+        private final HmsPartitionBatchLoader.RemoteCallTracker tracker;
+        private final int itemCount;
+        private final ConnectorOperationControl operationControl;
+        private final Thread invocationThread;
+        private ConnectorOperationAbortedException abort;
+        private boolean finished;
+        private boolean interruptedByWatchdog;
+        private boolean wireCallActive;
+        private boolean retryingAfterWireFailure;
+        private boolean clientUnsafeAfterReconnectFailure;
+
+        private Context(HmsPartitionBatchLoader.RemoteCallTracker tracker, int 
itemCount,
+                ConnectorOperationControl operationControl, Thread 
invocationThread) {
+            this.tracker = tracker;
+            this.itemCount = itemCount;
+            this.operationControl = operationControl;
+            this.invocationThread = invocationThread;
+        }
+
+        private void checkOperation() {
+            try {
+                operationControl.checkActive();
+            } catch (ConnectorOperationAbortedException e) {
+                synchronized (this) {
+                    if (finished || abort != null) {
+                        return;
+                    }
+                    if ((retryingAfterWireFailure || 
clientUnsafeAfterReconnectFailure) && !wireCallActive) {

Review Comment:
   Resolved by scope reduction in 9487b21cb47. The query cancellation watchdog 
and its phase model were removed. Authentication cancellation is no longer 
claimed and is explicitly outside this PR scope.



##########
fe/fe-connector/fe-connector-hms/src/main/java/org/apache/doris/connector/hms/ThriftHmsClient.java:
##########
@@ -674,17 +747,43 @@ public synchronized void close() throws IOException {
     // ========== Internal execution framework ==========
 
     private <T> T execute(HmsAction<T> action) {
+        return execute(ConnectorOperationControl.NONE, action);
+    }
+
+    private <T> T execute(ConnectorOperationControl operationControl, 
HmsAction<T> action) {
         if (closed) {
             throw new HmsClientException("HMS client is closed");
         }
-        try (PooledHmsClient pooled = borrowClient()) {
+        operationControl.checkActive();
+        try (PooledHmsClient pooled = borrowClient(operationControl)) {
+            operationControl.checkActive();
+            T result;
             try {
-                return doAs(() -> action.call(pooled.client));
+                result = doAs(() -> {

Review Comment:
   Resolved by scope reduction in 9487b21cb47. The asynchronous/watchdog 
cancellation lifecycle around outer Kerberos authentication was removed. 
Authentication cancellation is no longer claimed and is explicitly outside this 
PR scope.



##########
fe/fe-connector/fe-connector-hms/src/main/java/org/apache/doris/connector/hms/TrackingHiveMetaStoreClient.java:
##########
@@ -0,0 +1,58 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.connector.hms;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hive.metastore.HiveMetaHookLoader;
+import org.apache.hadoop.hive.metastore.HiveMetaStoreClient;
+import org.apache.hadoop.hive.metastore.IMetaStoreClient;
+import org.apache.hadoop.hive.metastore.api.MetaException;
+import org.apache.hadoop.hive.metastore.api.Partition;
+import shade.doris.hive.org.apache.thrift.TException;
+
+import java.util.List;
+
+/** Raw HMS client used under RetryingMetaStoreClient so every retry attempt 
is observable. */
+public final class TrackingHiveMetaStoreClient extends HiveMetaStoreClient 
implements IMetaStoreClient {
+
+    public TrackingHiveMetaStoreClient(Configuration conf, HiveMetaHookLoader 
hookLoader, Boolean allowEmbedded)
+            throws MetaException {
+        super(conf, hookLoader, allowEmbedded);
+    }
+
+    @Override
+    public List<Partition> getPartitionsByNames(String dbName, String 
tableName, List<String> partitionNames)
+            throws TException {
+        return HmsRemoteCallTracking.trackWireAttempt(
+                () -> super.getPartitionsByNames(dbName, tableName, 
partitionNames));
+    }
+
+    @Override
+    public void reconnect() throws MetaException {
+        HmsRemoteCallTracking.checkReconnectActive();

Review Comment:
   Resolved by scope reduction in 9487b21cb47. The reconnect 
cancellation/watchdog lifecycle was removed together with query-level HMS 
cancellation support. TrackingHiveMetaStoreClient now tracks only physical 
getPartitionsByNames attempts for observability.



##########
fe/fe-connector/fe-connector-hive/src/main/java/org/apache/doris/connector/hive/HiveConnectorMetadata.java:
##########
@@ -1086,33 +1090,61 @@ static long scaleSampledSize(long sampledSize, int 
totalPartitions, int sampledP
     }
 
     /**
-     * Resolves the data locations to list: the table location for an 
unpartitioned table, else every
-     * partition's location (bounded by {@link #MAX_PARTITIONS_FOR_STATS}). A 
partition or table with no
-     * location contributes nothing.
+     * Resolves the data locations to list. For an estimate, sampling happens 
on lightweight partition names
+     * before any partition object is requested. A non-positive sample size 
means that the explicit file-size
+     * path needs every partition object. A partition or table with no 
location contributes nothing.
      */
-    private List<PartitionRef> resolvePartitionRefs(HiveTableHandle handle) {
+    private PartitionRefSelection resolvePartitionRefs(
+            ConnectorSession session, HiveTableHandle handle, int sampleSize) {
         List<String> partKeyNames = handle.getPartitionKeyNames();
         if (partKeyNames == null || partKeyNames.isEmpty()) {
             String location = handle.getLocation();
-            return (location == null || location.isEmpty())
+            List<PartitionRef> refs = (location == null || location.isEmpty())
                     ? Collections.emptyList()
                     : Collections.singletonList(new PartitionRef(location, 
Collections.emptyList()));
+            return new PartitionRefSelection(refs, refs.size(), refs.size(), 
false);
         }
         List<String> partNames = hmsClient.listPartitionNames(
-                handle.getDbName(), handle.getTableName(), 
MAX_PARTITIONS_FOR_STATS);
+                handle.getDbName(), handle.getTableName(), ALL_PARTITIONS);

Review Comment:
   Resolved by scope reduction in 9487b21cb47. Query-level 
cancellation/deadline propagation has been removed from this PR, including 
ConnectorOperationControl and all session/control wiring. The PR description 
now explicitly lists name listing and the complete HMS cancellation lifecycle 
as out of scope.



##########
fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVRewriteUtil.java:
##########
@@ -85,8 +105,19 @@ public static Collection<Partition> 
getMTMVCanRewritePartitions(MTMV mtmv, Conne
             }
             if (mtmvNeedComparePartitions == null) {
                 try {
-                    mtmvNeedComparePartitions = 
getMtmvPartitionsByRelatedPartitions(mtmv, refreshContext,
-                            queryUsedPartitions);
+                    mtmvNeedComparePartitions = Sets.newLinkedHashSet(
+                            getMtmvPartitionsByRelatedPartitions(mtmv, 
refreshContext, queryUsedPartitions));
+                    mtmvNeedComparePartitions.retainAll(partitionsToCompare);
+                    MTMVRefreshContext currentRefreshContext = refreshContext;
+                    mtmvNeedComparePartitions.removeIf(
+                            partitionName -> 
!currentRefreshContext.persistedPartitionSetsMatch(partitionName));
+                    if (mtmvNeedComparePartitions.isEmpty()) {
+                        return res;
+                    }
+                    Set<TableNameInfo> excludeTables = forceConsistent
+                            ? ImmutableSet.of() : 
mtmv.getQueryRewriteConsistencyRelaxedTables();
+                    refreshContext.preloadSnapshots(mtmvNeedComparePartitions,

Review Comment:
   Resolved by scope reduction in 9487b21cb47. 
ConnectorOperationAbortedException and query-level cancellation/deadline 
propagation were removed from this PR, so there is no operation-abort exception 
for this hook to swallow.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/PreloadExternalMetadata.java:
##########
@@ -79,6 +84,22 @@ public ExternalMetadataPreloadResult 
executePreload(StatementContext statementCo
                 TimeUtils.getElapsedTimeMs(preloadStartTime));
     }
 
+    private void preloadCloudMtmvRefreshContexts(StatementContext 
statementContext) {
+        if (Config.isNotCloudMode()) {
+            return;
+        }
+        for (MTMV mtmv : statementContext.getCandidateMTMVs()) {
+            if 
(statementContext.getPreloadedMtmvRefreshContext(mtmv).isPresent()) {
+                continue;
+            }
+            try {
+                statementContext.putPreloadedMtmvRefreshContext(mtmv, 
MTMVRefreshContext.buildContext(mtmv));
+            } catch (AnalysisException e) {

Review Comment:
   Fixed in 9487b21cb47. The optional cloud MTMV candidate boundary now catches 
AnalysisException and DorisConnectorException per candidate, logs the failure, 
and continues base-table analysis. PreloadExternalMetadataTest covers this 
connector-failure path.



##########
fe/fe-core/src/main/java/org/apache/doris/connector/ConnectorSessionBuilder.java:
##########
@@ -175,7 +194,55 @@ public ConnectorSession build() {
         }
         return new ConnectorSessionImpl(queryId, user, timeZone, locale,
                 catalogId, catalogName, catalogProperties, sessionProperties, 
sid, cred,
-                captureStatementScope());
+                captureStatementScope(), captureOperationControl(), 
captureMetadataAccessObserver());
+    }
+
+    private ConnectorMetadataAccessObserver captureMetadataAccessObserver() {
+        if (metadataAccessObserver != null) {
+            return metadataAccessObserver;
+        }
+        ConnectContext ctx = connectContext != null ? connectContext : 
ConnectContext.get();
+        if (ctx == null || !ctx.getSessionVariable().enableProfile()) {
+            return ConnectorMetadataAccessObserver.NOOP;
+        }
+        SummaryProfile profile = SummaryProfile.getSummaryProfile(ctx);
+        return profile == null ? ConnectorMetadataAccessObserver.NOOP
+                : event -> profile.recordConnectorMetadataAccess(catalogName, 
event);
+    }
+
+    private ConnectorOperationControl captureOperationControl() {
+        if (operationControl != null) {
+            return operationControl;
+        }
+        ConnectContext ctx = connectContext != null ? connectContext : 
ConnectContext.get();
+        if (ctx == null) {
+            return ConnectorOperationControl.NONE;
+        }
+        long startMillis = ctx.getStartTime() > 0 ? ctx.getStartTime() : 
System.currentTimeMillis();
+        long deadlineMillis = startMillis + ctx.getExecTimeoutS() * 1000L;
+        // The connection may execute another statement later; bind 
cancellation to this session's statement.
+        StmtExecutor originatingExecutor = ctx.getExecutor();

Review Comment:
   Resolved by scope reduction in 9487b21cb47. Connector session cancellation 
capture and the StmtExecutor cancellation state added by this PR were removed. 
Background-task cancellation propagation is no longer claimed by this PR and is 
explicitly out of scope.



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