wenzhenghu commented on code in PR #64035:
URL: https://github.com/apache/doris/pull/64035#discussion_r3348814909


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java:
##########
@@ -992,6 +1045,172 @@ public void setSnapshot(MvccTableInfo mvccTableInfo, 
MvccSnapshot snapshot) {
         snapshots.put(mvccTableInfo, snapshot);
     }
 
+    private Optional<String> getExternalMetadataPreloadSkipReason() {
+        if (connectContext == null || connectContext.getSessionVariable() == 
null
+                || 
!connectContext.getSessionVariable().isEnablePreloadExternalMetadata()) {
+            return Optional.of("session variable 
enable_preload_external_metadata is disabled");
+        }
+        if (externalTablePreloadInfos.isEmpty()) {
+            return Optional.of("no external preload candidates were 
collected");
+        }
+        if (containsPlanReadLockTable(tables.values())
+                || containsPlanReadLockTable(mtmvRelatedTables.values())
+                || containsPlanReadLockTable(insertTargetTables.values())) {
+            return Optional.empty();
+        }
+        return Optional.of("no internal tables require plan-time read lock");
+    }
+
+    private boolean containsPlanReadLockTable(Collection<TableIf> tableIfs) {
+        for (TableIf tableIf : tableIfs) {
+            if (tableIf.needReadLockWhenPlan()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean supportsExternalMetadataPreload(TableIf table) {
+        return table instanceof HMSExternalTable
+                || table instanceof IcebergExternalTable
+                || table instanceof PaimonExternalTable
+                // Limit plugin-driven preload to JDBC catalogs until other 
connectors are verified.
+                || isJdbcPluginExternalTable(table);
+    }
+
+    // Preload metadata that is commonly accessed during planning before 
internal table locks are acquired.
+    private boolean preloadExternalTable(ExternalTablePreloadInfo preloadInfo) 
{
+        ExternalTable table = preloadInfo.table;
+        long preloadStartTime = TimeUtils.getStartTimeMs();
+        // Preload the latest snapshot only when every relation uses the 
latest view of the table.
+        boolean supportsLatestSnapshot = supportsLatestSnapshotPreload(table);
+        boolean latestOnlyRelation = preloadInfo.shouldPreloadLatestSnapshot();
+        boolean preloadLatestSnapshot = latestOnlyRelation && 
supportsLatestSnapshot;
+        // Skip schema and partition warmup for snapshot-aware tables when the 
query targets
+        // only non-latest relations such as time travel, branch, or tag 
references.
+        boolean preloadSchema = !supportsLatestSnapshot || latestOnlyRelation;
+        boolean preloadPartition = preloadSchema && 
table.supportInternalPartitionPruned();
+        if (preloadLatestSnapshot) {
+            loadSnapshots(table, Optional.empty(), Optional.empty());
+        }
+        // Preload schema access while no internal table lock is held.
+        if (preloadSchema) {
+            table.getBaseSchema();
+        }
+        // Preload partition metadata only when the warmed schema matches the 
relation view.
+        if (preloadPartition) {
+            table.initSelectedPartitions(getSnapshot(table));
+        }
+        // Log the actual preload path per table to simplify manual 
verification in debug mode.
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("{} preloaded external metadata for table {} "
+                            + "[supportsLatestSnapshot={}, 
preloadLatestSnapshot={}, preloadSchema={}, "
+                            + "preloadPartition={}, hasLatestRelation={}, 
hasNonLatestRelation={}, elapsedMs={}]",
+                    getPreloadQueryIdentifier(), 
getExternalTableLogName(table), supportsLatestSnapshot,
+                    preloadLatestSnapshot, preloadSchema, preloadPartition, 
preloadInfo.hasLatestOnlyRelation,
+                    preloadInfo.hasNonLatestRelation, 
TimeUtils.getElapsedTimeMs(preloadStartTime));
+        }
+        return preloadLatestSnapshot || preloadSchema || preloadPartition;
+    }
+
+    // Use the query identifier in debug logs so preload events can be 
correlated with a single statement.
+    private String getPreloadQueryIdentifier() {
+        return connectContext == null ? "stmt[unknown]" : 
connectContext.getQueryIdentifier();
+    }
+
+    // Build a stable table name for preload debug logs.
+    private String getExternalTableLogName(ExternalTable table) {
+        return table.getCatalog().getName() + "." + table.getDbName() + "." + 
table.getName();
+    }
+
+    // Restrict plugin-driven preload to JDBC because other connector types 
have not been evaluated yet.
+    private boolean isJdbcPluginExternalTable(TableIf table) {
+        if (!(table instanceof PluginDrivenExternalTable)) {
+            return false;
+        }
+        ExternalTable externalTable = (ExternalTable) table;
+        if (!(externalTable.getCatalog() instanceof 
PluginDrivenExternalCatalog)) {
+            return false;
+        }
+        return "jdbc".equalsIgnoreCase(((PluginDrivenExternalCatalog) 
externalTable.getCatalog()).getType());
+    }
+
+    // Keep the latest snapshot preload decision explicit so metadata preload 
support and
+    // snapshot preload support are not conflated.
+    private boolean supportsLatestSnapshotPreload(ExternalTable table) {
+        // Paimon and Iceberg are not HMSExternalTable even when their catalog 
metadata comes from HMS.
+        if (table instanceof PaimonExternalTable || table instanceof 
IcebergExternalTable) {
+            return true;
+        }
+        if (!(table instanceof HMSExternalTable)) {
+            return false;
+        }
+        // Hive and Hudi share HMSExternalTable, but only Hudi has a 
meaningful latest snapshot to preload.
+        DLAType dlaType = ((HMSExternalTable) table).getDlaType();
+        return dlaType == DLAType.HUDI;
+    }
+
+    private static class ExternalTablePreloadInfo {

Review Comment:
   Good point. I will move ExternalTablePreloadInfo out of StatementContext 
into a dedicated class to keep StatementContext lighter and avoid adding 
another helper type in this file.
   



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