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


##########
fe/fe-core/src/main/java/org/apache/doris/datasource/hive/HMSExternalTable.java:
##########
@@ -1246,14 +1258,17 @@ public void beforeMTMVRefresh(MTMV mtmv) throws 
DdlException {
 
     @Override
     public MvccSnapshot loadSnapshot(Optional<TableSnapshot> tableSnapshot, 
Optional<TableScanParams> scanParams) {
-        if (getDlaType() == DLAType.HUDI) {
-            return HudiUtils.getHudiMvccSnapshot(tableSnapshot, this);
-        } else if (getDlaType() == DLAType.ICEBERG) {
-            return new IcebergMvccSnapshot(
-                    IcebergUtils.getSnapshotCacheValue(tableSnapshot, this, 
scanParams));
-        } else {
-            return new EmptyMvccSnapshot();
-        }
+        return withMissingTableRepair(() -> {

Review Comment:
   [P2] Guard HMS capability discovery before metadata preload
   
   With `enable_preload_external_metadata=true`, warm the names and an 
uninitialized HMS object using `SHOW TABLES` plus `SHOW CREATE TABLE`, then 
drop the remote table. A mixed internal/HMS query reaches this reduced 
collection-stage plan:
   
   ```text
   Project(*)
     Join(a.k = b.k)
       UnboundRelation(internal.db.local AS a)
       UnboundRelation(ctl.db.t AS b)
   ```
   
   `PreloadExternalMetadata.preloadExternalTable()` first calls 
`supportsLatestSnapshotPreload()`, which invokes `getDlaType()` -> 
`makeSureInitialized()` -> live HMS `loadHiveTable()`. The missing-table 
exception therefore escapes before preload reaches this guarded 
`loadSnapshot()` or `getBaseSchema()`. The internal OLAP relation enables the 
preload gate, so repeated mixed queries leave the stale names/object/ID state 
unrepaired. Guard the outer capability/init probe after the table monitor 
unwinds, or make capability discovery avoid remote initialization, and cover 
this SELECT-first path with preload enabled.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalTable.java:
##########
@@ -393,6 +394,31 @@ public Optional<ColumnStatistic> getColumnStatistic(String 
colName) {
         return Optional.empty();
     }
 
+    /** Only table-level remote reads use this seam; partition misses and 
arbitrary failures do not repair names. */
+    protected <R> R withMissingTableRepair(Supplier<R> read) {
+        // Metadata validators can enter a schema read under this monitor. 
Repair only after
+        // an outer unlocked entry point has unwound it, preserving catalog -> 
table lock order.
+        if (Thread.holdsLock(this)) {
+            return read.get();
+        }
+        Optional<Runnable> observation = db.observeTableLookup(this);

Review Comment:
   [P2] Cover the live HMS SHOW CREATE read with this guard
   
   After warming `SHOW TABLES` and the cached HMS table object, drop `t` 
through another catalog/engine and run `SHOW CREATE TABLE ctl.db.t` first. 
`ShowCreateTableCommand.doRun()` calls 
`HiveMetaStoreClientHelper.showCreateTable()` directly, which always invokes 
the live HMS `getTable()`. Its `HMSClientException` retains the definite 
`NoSuchObjectException`, but neither command validation nor this read enters 
any of the newly wrapped schema/view/snapshot methods. Consequently this failed 
table-level read leaves the names, object and ID caches intact: `SHOW TABLES` 
still lists `t`, and repeated `SHOW CREATE` calls keep bypassing repair. Route 
the live HMS DDL read through this guarded seam and add a SHOW CREATE-first 
remote-drop test.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalDatabase.java:
##########
@@ -680,6 +683,64 @@ public boolean unregisterTableForReplay(String tableName) {
         return true;
     }
 
+    public void invalidateTableLookup(String tableName) {
+        if (metaCache != null) {
+            String localName = 
getTableForReplay(tableName).map(ExternalTable::getName).orElse(tableName);
+            metaCache.invalidateLookup(localName, 
extCatalog.getLowerCaseTableNames() != 0);
+        }
+    }
+
+    /** Historical spellings require typed engine invalidation and an 
independent DB row-count fence. */
+    public void invalidateTableLookupDependencies() {
+        ExternalMetaCacheMgr cacheMgr = 
Env.getCurrentEnv().getExtMetaCacheMgr();
+        long catalogId = getCatalog().getId();
+        cacheMgr.invalidateRowCountCache(catalogId, getId());
+        try {
+            cacheMgr.invalidateDb(this);
+        } finally {
+            cacheMgr.invalidateRowCountCache(catalogId, getId());
+        }
+    }
+
+    public Optional<Runnable> beginTableMetadataRefresh() {
+        return metaCache == null ? Optional.empty() : 
Optional.of(metaCache.beginMetadataRefresh());
+    }
+
+    /** A missing result is authorized only against the retained DB, names and 
object generations. */
+    @SuppressWarnings("unchecked")
+    public Optional<Runnable> observeTableLookup(ExternalTable table) {
+        MetaCache<T> cache = metaCache;
+        if (!initialized || cache == null) {
+            return Optional.empty();
+        }
+        long epoch = acquireTableMetadataLoadEpoch();

Review Comment:
   [P2] Observe pinned reads without reopening a retired catalog
   
   For `EXPLAIN ANALYZED PLAN SELECT 1 FROM ctl.db.t@branch(b)` on a Paimon 
REST catalog (with `enable_materialized_view_rewrite=false`), binding has 
already retained the branch table when 
`LogicalFileScan.captureRelationSchema()` calls `getFullSchema(snapshot)`. Let 
this observer pass the `initialized` check, then let another session change 
`schema.cache.ttl-second` through `ALTER CATALOG`, resetting the catalog and 
retiring this database. Resuming here calls `acquireTableMetadataLoadEpoch()` 
-> `ExternalCatalog.acquireMetadataLoadEpoch()` -> `makeSureInitialized()`, 
which reopens the REST catalog before attempting the retained schema read. If 
its `/v1/config` endpoint is unavailable, the EXPLAIN now fails during 
observation setup. Previously, the branch's `schemaFromSnapshotTable` path 
projected the retained table's local schema; analyzed EXPLAIN returns before 
descriptor serialization or scan block-rule checks, so neither required this 
live initialization. Capture an already initi
 alized generation without reopening the catalog, and abandon repair 
observation when its parent/database has retired; add a 
reset-versus-pinned-read 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