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


##########
fe/fe-connector/fe-connector-hms/src/main/java/org/apache/doris/connector/hms/ThriftHmsClient.java:
##########
@@ -782,15 +861,47 @@ private static HmsPartitionInfo 
convertPartition(Partition partition) {
 
     // ========== Pool management ==========
 
-    private PooledHmsClient borrowClient() {
+    private PooledHmsClient borrowClient(ConnectorOperationControl 
operationControl) {
         if (clientPool == null) {
             return createFreshClient();
         }
-        try {
-            return clientPool.borrowObject();
-        } catch (Exception e) {
-            throw new HmsClientException(withRootCause("Failed to borrow HMS 
client "
-                    + "from pool: " + e.getMessage(), e), e);
+        long poolWaitDeadlineNanos = System.nanoTime() + 
TimeUnit.MILLISECONDS.toNanos(POOL_BORROW_TIMEOUT_MS);
+        while (true) {
+            operationControl.checkActive();
+            long operationRemainingMillis = 
operationControl.remainingTimeMillis();
+            if (operationRemainingMillis <= 0) {
+                throw new ConnectorOperationAbortedException(
+                        
ConnectorOperationAbortedException.Reason.DEADLINE_EXCEEDED,
+                        "HMS client pool wait deadline exceeded");
+            }
+            long poolRemainingNanos = poolWaitDeadlineNanos - 
System.nanoTime();
+            if (poolRemainingNanos <= 0) {
+                throw new HmsClientException("Timed out waiting for an HMS 
client from the pool");
+            }
+            long poolRemainingMillis = Math.max(1L,
+                    TimeUnit.NANOSECONDS.toMillis(poolRemainingNanos));
+            long waitMillis = Math.min(POOL_BORROW_CHECK_MILLIS, 
poolRemainingMillis);
+            if (operationRemainingMillis != Long.MAX_VALUE) {
+                waitMillis = Math.min(waitMillis, operationRemainingMillis);
+            }
+            try {
+                return clientPool.borrowObject(waitMillis);

Review Comment:
   **[P1] Bound HMS client creation with the operation control.** On an empty 
pool, Commons Pool 2.2 runs `HmsClientFactory.create()` synchronously inside 
`borrowObject(waitMillis)` before the timed idle-object wait, so `waitMillis` 
does not bound `createFreshClient()`; the pool-disabled branch calls it 
directly as well. Kerberos login, DNS, or socket construction can therefore 
remain stuck after KILL/deadline, before `HmsRemoteCallTracking` installs its 
watchdog and before the next `checkActive()`. Please make creation 
cancellable/deadline-aware (and destroy any client that completes late) for 
both branches, with blocking-provider KILL/deadline tests.



##########
fe/fe-connector/fe-connector-spi/src/main/java/org/apache/doris/connector/spi/ConnectorSession.java:
##########
@@ -155,4 +155,14 @@ default long allocateTransactionId() {
     default ConnectorStatementScope getStatementScope() {
         return ConnectorStatementScope.NONE;
     }
+
+    /** Returns cooperative cancellation and deadline control for connector 
metadata operations. */

Review Comment:
   **[P2] Freeze the new session/control API in the plugin surface.** 
`ConnectorPluginSurfaceTest.FROZEN_TYPES` does not include `ConnectorSession` 
or the new control/observer/event/abort types, so the regenerated baseline 
records `ConnectorContext#getMetadataAccessObserver()` but not these two 
session methods or the callable contracts they expose. The separate metadata 
baseline also omits return types. That leaves later removal/re-signing of this 
new 6.0 surface invisible to the stated compatibility speed bump. This is 
independent of whether 6.0 is still unpublished: please freeze these reachable 
SPI types (or recursively freeze reachable SPI contracts), regenerate the 
baseline, and assert the new methods are present.



##########
fe/fe-connector/fe-connector-hms/src/main/java/org/apache/doris/connector/hms/HmsPartitionRequest.java:
##########
@@ -0,0 +1,238 @@
+// 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.ConnectorMetadataAccessObserver;
+import org.apache.doris.connector.spi.ConnectorOperationControl;
+import org.apache.doris.connector.spi.ConnectorSession;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+/** Immutable logical HMS request data with request-scoped adaptive execution 
state. */
+final class HmsPartitionRequest {
+
+    static final long NO_FALLBACK_START_NANOS = Long.MIN_VALUE;
+
+    private final String dbName;
+    private final String tableName;
+    private final List<String> partitionNames;
+    private final HmsPartitionAccessSource source;
+    private final ConnectorOperationControl operationControl;
+    private final ConnectorMetadataAccessObserver metadataAccessObserver;
+    private final PartitionChunkConsumer partitionChunkConsumer;
+    private final BatchExecutionState batchExecutionState;
+
+    private HmsPartitionRequest(Builder builder) {
+        this.dbName = builder.dbName;
+        this.tableName = builder.tableName;
+        this.partitionNames = Collections.unmodifiableList(new 
ArrayList<>(builder.partitionNames));
+        this.source = builder.source;
+        this.operationControl = builder.operationControl;
+        this.metadataAccessObserver = builder.metadataAccessObserver;
+        this.partitionChunkConsumer = builder.partitionChunkConsumer;
+        this.batchExecutionState = builder.batchExecutionState;
+    }
+
+    static HmsPartitionRequest from(ConnectorSession session, 
HmsPartitionAccessSource source,
+            String dbName, String tableName, List<String> partitionNames) {
+        return builder()
+                .database(dbName)
+                .table(tableName)
+                .partitionNames(partitionNames)
+                .source(source)
+                .operationControl(session == null
+                        ? ConnectorOperationControl.NONE : 
session.getOperationControl())
+                .metadataAccessObserver(session == null
+                        ? ConnectorMetadataAccessObserver.NOOP : 
session.getMetadataAccessObserver())
+                .build();
+    }
+
+    static Builder builder() {
+        return new Builder();
+    }
+
+    String getDbName() {
+        return dbName;
+    }
+
+    String getTableName() {
+        return tableName;
+    }
+
+    List<String> getPartitionNames() {
+        return partitionNames;
+    }
+
+    HmsPartitionAccessSource getSource() {
+        return source;
+    }
+
+    ConnectorOperationControl getOperationControl() {
+        return operationControl;
+    }
+
+    ConnectorOperationControl getEffectiveOperationControl() {
+        ConnectorOperationControl effective = 
batchExecutionState.effectiveOperationControl.get();
+        return effective == null ? operationControl : effective;
+    }
+
+    void updateEffectiveOperationControl(ConnectorOperationControl 
effectiveOperationControl) {
+        
batchExecutionState.effectiveOperationControl.set(effectiveOperationControl);
+    }
+
+    ConnectorMetadataAccessObserver getMetadataAccessObserver() {
+        return metadataAccessObserver;
+    }
+
+    PartitionChunkConsumer getPartitionChunkConsumer() {
+        return partitionChunkConsumer;
+    }
+
+    int effectiveBatchSize(int configuredMaxBatchSize) {
+        return Math.min(configuredMaxBatchSize, 
batchExecutionState.effectiveBatchSize.get());
+    }
+
+    void reduceEffectiveBatchSize(int batchSize) {
+        batchExecutionState.effectiveBatchSize.accumulateAndGet(batchSize, 
Math::min);
+    }
+
+    long fallbackStartNanos() {
+        return batchExecutionState.fallbackStartNanos.get();
+    }
+
+    long startFallback(long startNanos) {
+        
batchExecutionState.fallbackStartNanos.compareAndSet(NO_FALLBACK_START_NANOS, 
startNanos);
+        return batchExecutionState.fallbackStartNanos.get();
+    }
+
+    /** Receives one fully validated and request-ordered physical chunk before 
the next HMS chunk starts. */
+    @FunctionalInterface
+    interface PartitionChunkConsumer {
+        PartitionChunkConsumer NOOP = (partitionNames, partitions, 
operationControl) -> { };
+
+        void accept(List<String> partitionNames, List<HmsPartitionInfo> 
partitions,
+                ConnectorOperationControl operationControl);
+    }
+
+    static final class Builder {
+        private String dbName;
+        private String tableName;
+        private List<String> partitionNames;
+        private HmsPartitionAccessSource source = 
HmsPartitionAccessSource.UNKNOWN;
+        private ConnectorOperationControl operationControl = 
ConnectorOperationControl.NONE;
+        private ConnectorMetadataAccessObserver metadataAccessObserver = 
ConnectorMetadataAccessObserver.NOOP;
+        private PartitionChunkConsumer partitionChunkConsumer = 
PartitionChunkConsumer.NOOP;
+        private BatchExecutionState batchExecutionState = new 
BatchExecutionState();
+
+        private Builder() {
+        }
+
+        Builder database(String dbName) {
+            this.dbName = dbName;
+            return this;
+        }
+
+        Builder table(String tableName) {
+            this.tableName = tableName;
+            return this;
+        }
+
+        Builder partitionNames(List<String> partitionNames) {
+            this.partitionNames = partitionNames;
+            return this;
+        }
+
+        Builder source(HmsPartitionAccessSource source) {
+            this.source = source;
+            return this;
+        }
+
+        Builder operationControl(ConnectorOperationControl operationControl) {
+            this.operationControl = operationControl;
+            return this;
+        }
+
+        Builder metadataAccessObserver(ConnectorMetadataAccessObserver 
metadataAccessObserver) {
+            this.metadataAccessObserver = metadataAccessObserver;
+            return this;
+        }
+
+        Builder partitionChunkConsumer(PartitionChunkConsumer 
partitionChunkConsumer) {
+            this.partitionChunkConsumer = partitionChunkConsumer;
+            return this;
+        }
+
+        Builder shareBatchExecutionWith(HmsPartitionRequest request) {
+            this.batchExecutionState = request.batchExecutionState;
+            return this;
+        }
+
+        HmsPartitionRequest build() {
+            requireName(dbName, "database");
+            requireName(tableName, "table");
+            Objects.requireNonNull(partitionNames, "partitionNames");
+            Objects.requireNonNull(source, "source");
+            Objects.requireNonNull(operationControl, "operationControl");
+            Objects.requireNonNull(metadataAccessObserver, 
"metadataAccessObserver");
+            Objects.requireNonNull(partitionChunkConsumer, 
"partitionChunkConsumer");
+            Objects.requireNonNull(batchExecutionState, "batchExecutionState");
+            Set<List<String>> identities = new HashSet<>();
+            List<String> partitionKeys = null;
+            for (int i = 0; i < partitionNames.size(); i++) {
+                if ((i & 1023) == 0) {
+                    operationControl.checkActive();
+                }
+                String partitionName = partitionNames.get(i);
+                HmsPartitionIdentity.ParsedPartitionName parsed = 
HmsPartitionIdentity.parse(partitionName);

Review Comment:
   **[P2] Retain parsed identities across the cache-backed request.** This 
builder validates every partition name with `HmsPartitionIdentity.parse()` and 
then discards the result. The normal cold-cache path reparses all names during 
cache lookup, registration, copied-window construction, raw validation, and 
final reconstruction—`6N+C` parse/unescape passes for a fully cold sole owner 
(roughly 720k canonicalizations for 120k names); even all hits take `3N`. The 
added 120k test covers only the raw loader, so it misses this decorator cost. 
Please carry immutable parsed keys/values on the request and share/slice them 
through windows and validation, with a cache-backed large-request 
parse-count/performance test.



##########
fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVPartitionUtil.java:
##########
@@ -245,6 +245,7 @@ public static boolean isMTMVSync(MTMVRefreshContext 
context, Set<BaseTableInfo>
             throws AnalysisException {
         MTMV mtmv = context.getMtmv();
         Set<String> partitionNames = mtmv.getPartitionNames();
+        context.preloadSnapshots(partitionNames, tables, excludeTables);

Review Comment:
   **[P2] Check the persisted partition set before eager freshness loading.** 
This preload fetches partition-detail freshness for the entire mapped PCT union 
before `isSyncWithPartitions()` compares the current names with the persisted 
`getPctSnapshots()` set. With a coarse mapping over 160k Hive partitions, 
adding or dropping one partition already proves the MTMV stale locally, but 
this order now issues the full HMS freshness request first (and can fail before 
returning that deterministic stale result); the pre-PR path performed the set 
gate before fetching snapshots. Please make the comparison two-phase—reject 
name-set mismatches first, then preload only mappings that still need version 
checks—and add large mismatch coverage asserting no partition-freshness call.



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