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


##########
fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergExternalCatalog.java:
##########
@@ -173,17 +182,58 @@ protected List<String> 
listTableNamesFromRemote(SessionContext ctx, String dbNam
     }
 
     @Override
-    public void onClose() {
+    public synchronized void onClose() {
         super.onClose();
-        if (null != catalog) {
-            try {
-                if (catalog instanceof AutoCloseable) {
-                    ((AutoCloseable) catalog).close();
-                }
-                catalog = null;
-            } catch (Exception e) {
-                LOG.warn("Failed to close iceberg catalog: {}", getName(), e);
+        Catalog retiredCatalog = catalog;
+        catalog = null;
+        if (retiredCatalog != null) {
+            resourceTracker.retireCurrent(() -> closeCatalog(retiredCatalog));

Review Comment:
   [P1] Invalidate table generations when rotating the catalog
   
   A URI, warehouse, catalog-type, or credential update calls 
`resetToUninitialized(false)`, so this retires the current catalog but leaves 
the Iceberg table cache intact (the override only removes it for cache-control 
keys). Subsequent statements can therefore keep borrowing old `Table`/FileIO 
generations and continue using the previous runtime configuration until refresh 
or expiry. Invalidate this catalog's Iceberg cache group whenever the runtime 
generation rotates; the tracker can still defer cleanup for active old 
borrowers.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergExternalMetaCache.java:
##########
@@ -159,23 +189,131 @@ public void invalidateCatalogEntries(long catalogId) {
     }
 
     private IcebergTableCacheValue loadTableCacheValue(NameMapping 
nameMapping) {
+        CatalogIf catalog = 
Env.getCurrentEnv().getCatalogMgr().getCatalog(nameMapping.getCtlId());
+        if (catalog instanceof IcebergExternalCatalog) {
+            IcebergExternalCatalog icebergCatalog = (IcebergExternalCatalog) 
catalog;
+            try (IcebergExternalCatalog.TableLoadContext loadContext = 
icebergCatalog.beginTableLoad()) {
+                IcebergMetadataOps ops = loadContext.getOps();
+                Table table;
+                try {
+                    table = 
loadContext.loadTable(nameMapping.getRemoteDbName(), 
nameMapping.getRemoteTblName());
+                } catch (Exception e) {
+                    throw new 
RuntimeException(ExceptionUtils.getRootCauseMessage(e), e);
+                }
+                ExternalTable dorisTable = findExternalTable(nameMapping, 
ENGINE);
+                Runnable tableCleanup = 
tableCleanup(loadContext.getCatalogType(), ops, table);
+                IcebergCatalogResourceTracker.ResourceLease catalogLease = 
loadContext.promote();
+                return new IcebergTableCacheValue(table, () -> 
loadSnapshotProjection(dorisTable, table), () -> {
+                    try {
+                        tableCleanup.run();
+                    } finally {
+                        catalogLease.close();
+                    }
+                });
+            }
+        }
+        Table table = loadTable(nameMapping);
+        IcebergMetadataOps ops = resolveMetadataOps(catalog);
+        ExternalTable dorisTable = findExternalTable(nameMapping, ENGINE);
+        return new IcebergTableCacheValue(table, () -> 
loadSnapshotProjection(dorisTable, table),
+                tableCleanup(catalog, ops, table));
+    }
+
+    private Table loadTable(NameMapping nameMapping) {
         CatalogIf catalog = 
Env.getCurrentEnv().getCatalogMgr().getCatalog(nameMapping.getCtlId());
         if (catalog == null) {
             throw new RuntimeException(String.format("Cannot find catalog %d 
when loading table %s/%s.",
                     nameMapping.getCtlId(), nameMapping.getLocalDbName(), 
nameMapping.getLocalTblName()));
         }
 
-        IcebergMetadataOps ops = resolveMetadataOps(catalog);
+        return loadTable(nameMapping, catalog, resolveMetadataOps(catalog));
+    }
+
+    private Table loadTable(NameMapping nameMapping, CatalogIf catalog, 
IcebergMetadataOps ops) {
         try {
-            Table table = ((ExternalCatalog) 
catalog).getExecutionAuthenticator()
+            return ((ExternalCatalog) catalog).getExecutionAuthenticator()
                     .execute(() -> 
ops.loadTable(nameMapping.getRemoteDbName(), nameMapping.getRemoteTblName()));
-            ExternalTable dorisTable = findExternalTable(nameMapping, ENGINE);
-            return new IcebergTableCacheValue(table, () -> 
loadSnapshotProjection(dorisTable, table));
         } catch (Exception e) {
             throw new RuntimeException(ExceptionUtils.getRootCauseMessage(e), 
e);
         }
     }
 
+    private IcebergTableCacheValue.Lease statementLease(NameMapping 
nameMapping) {
+        ConnectContext connectContext = ConnectContext.get();
+        StatementContext statementContext = connectContext == null ? null : 
connectContext.getStatementContext();
+        if (statementContext == null) {
+            return null;
+        }
+        String resourceKey = "iceberg-table:" + nameMapping.getCtlId() + 
"\u0000"
+                + nameMapping.getRemoteDbName() + "\u0000" + 
nameMapping.getRemoteTblName();
+        return statementContext.getOrRegisterStatementResource(resourceKey, () 
-> borrow(nameMapping));

Review Comment:
   [P1] Close leases after forwarded master execution
   
   This lease is registered in the `StatementContext` created by the proxy-side 
`StmtExecutor`, but `ConnectProcessor.proxyExecute()` returns without ever 
closing that context; `FrontendServiceImpl.forward()` only removes the 
thread-local. A forwarded Iceberg query therefore leaves a permanent borrower, 
so later eviction cannot close its table FileIO or retired catalog generation. 
Close the proxy statement context in a `finally` covering ordinary, 
prepared-forwarded, and error paths, and add a forwarded-query lifecycle test.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergExternalCatalog.java:
##########
@@ -173,17 +182,58 @@ protected List<String> 
listTableNamesFromRemote(SessionContext ctx, String dbNam
     }
 
     @Override
-    public void onClose() {
+    public synchronized void onClose() {

Review Comment:
   [P1] Keep the planning executor with the leased generation
   
   The tracker only defers `Catalog.close()`, but `super.onClose()` has already 
shut down this generation's pre-auth executor. An active scan can acquire a 
leased old table and call 
`planWith(source.getCatalog().getThreadPoolWithPreAuth())` only after a 
concurrent reset; it then gets either the shut-down pool or a pool from the new 
runtime generation. Keep the executor and other operations resources used after 
table acquisition inside the tracked generation, and test reset between table 
acquisition and `planFiles()`/commit.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java:
##########
@@ -918,7 +1025,11 @@ protected void finalize() throws Throwable {
     @Override
     public void close() {
         clearExternalScanTasks();
-        releasePlannerResources();
+        try {
+            releaseStatementResources();

Review Comment:
   [P1] Close resources in auto-close contexts
   
   `AutoCloseConnectContext.close()` only reaches `ConnectContext.clear()`, 
which nulls the StatementContext without invoking this release. External 
statistics queries in `BaseAnalysisTask` use that owner, so an Iceberg analysis 
discards its lease's only close handle; refresh or invalidation can then never 
drain the old table/FileIO/catalog generation, and repeated analyze runs 
accumulate borrowers. Close the current StatementContext before 
clearing/restoring the context on every success and failure path, and add an 
external-analyze lifecycle test that does not close it manually.



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