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


##########
fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalMetaCacheMgr.java:
##########
@@ -228,15 +244,30 @@ public void invalidateCatalogByEngine(long catalogId, 
String engine) {
     }
 
     public void removeCatalog(long catalogId) {
-        routeCatalogEngines(catalogId, cache -> safeInvalidate(
-                cache, catalogId, "removeCatalog",
-                () -> cache.invalidateCatalog(catalogId)));
+        synchronized (catalogLifecycleLock(catalogId)) {
+            routeCatalogEngines(catalogId, cache -> safeInvalidate(
+                    cache, catalogId, "removeCatalog",
+                    () -> cache.invalidateCatalog(catalogId)));
+        }
     }
 
     public void removeCatalogByEngine(long catalogId, String engine) {
-        routeSpecifiedEngine(engine, cache -> safeInvalidate(
-                cache, catalogId, "removeCatalogByEngine",
-                () -> cache.invalidateCatalog(catalogId)));
+        synchronized (catalogLifecycleLock(catalogId)) {
+            routeSpecifiedEngine(engine, cache -> safeInvalidate(
+                    cache, catalogId, "removeCatalogByEngine",
+                    () -> cache.invalidateCatalog(catalogId)));
+        }
+    }
+
+    /**
+     * Fences a catalog runtime transition against lazy cache-group 
initialization. The transition callback
+     * must cover both cache removal and the catalog property/runtime 
mutation; otherwise an accessor can
+     * snapshot the retiring properties after removal and publish that group 
into the new generation.
+     */
+    public void runCatalogLifecycle(long catalogId, Runnable transition) {

Review Comment:
   [P1] Include tentative ALTER validation in this fence
   
   The real legacy validation path in `CatalogMgr.replayAlterCatalogProps()` 
calls `tryModifyCatalogProps(newProps)` and `checkProperties()` before the 
final modify/reset, without holding this lifecycle lock; query lookups do not 
take the catalog-manager write lock. A query can therefore enter 
`prepareCatalogByEngine()` during validation, snapshot candidate G2 properties, 
and load/cache a table using the still-live G1 catalog/ops/executor. If 
validation fails, `rollBackCatalogProps()` restores G1 properties but performs 
no cache removal or runtime reset, so the mixed generation remains reachable. 
Validate against a detached property object, or place tentative mutation, 
validation, rollback/final publication, and reset under this same fence.



##########
fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java:
##########
@@ -984,21 +984,96 @@ public PlSqlOperation getPlSqlOperation() {
     // with "Split source X is released". These executors are finalized when 
the next query starts
     // on this connection, or when the connection is torn down. See #62259.
     private final List<StmtExecutor> flightSqlDeferredExecutors = new 
ArrayList<>();
+    private boolean flightSqlDeferredExecutorsSealed;
+    private int flightSqlResultPublishers;
 
-    public void addFlightSqlDeferredExecutor(StmtExecutor executor) {
+    public boolean addFlightSqlDeferredExecutor(StmtExecutor executor) {
         synchronized (flightSqlDeferredExecutors) {
+            if (flightSqlDeferredExecutorsSealed) {
+                return false;
+            }
             flightSqlDeferredExecutors.add(executor);
+            return true;
+        }
+    }
+
+    /** Linearizes GetFlightInfo publication with the terminal session seal. */
+    public boolean canPublishFlightSqlResult() {
+        synchronized (flightSqlDeferredExecutors) {
+            return !flightSqlDeferredExecutorsSealed;
+        }
+    }
+
+    public boolean beginFlightSqlResultPublication() {
+        synchronized (flightSqlDeferredExecutors) {
+            if (flightSqlDeferredExecutorsSealed) {
+                return false;
+            }
+            flightSqlResultPublishers++;
+            return true;
+        }
+    }
+
+    public boolean endFlightSqlResultPublication() {
+        List<StmtExecutor> toClose = null;
+        boolean published;
+        synchronized (flightSqlDeferredExecutors) {
+            published = !flightSqlDeferredExecutorsSealed;
+            if (--flightSqlResultPublishers == 0 && 
flightSqlDeferredExecutorsSealed) {
+                toClose = drainFlightSqlDeferredExecutors();
+                flightSqlDeferredExecutors.notifyAll();
+            }
         }
+        finalizeFlightSqlDeferredExecutors(toClose);
+        return published;
     }
 
     public void closeFlightSqlDeferredExecutors() {
-        List<StmtExecutor> toClose;
+        closeFlightSqlDeferredExecutors(false);
+    }
+
+    /** Prevents a session teardown race from accepting an executor after the 
final drain. */
+    public void sealAndCloseFlightSqlDeferredExecutors() {
+        closeFlightSqlDeferredExecutors(true);
+    }
+
+    private void closeFlightSqlDeferredExecutors(boolean seal) {
+        List<StmtExecutor> toClose = null;
         synchronized (flightSqlDeferredExecutors) {
-            if (flightSqlDeferredExecutors.isEmpty()) {
-                return;
+            if (seal) {
+                flightSqlDeferredExecutorsSealed = true;
+                // The result channel is destroyed immediately after this 
method returns. Wait until every
+                // admitted publisher has either committed or observed the 
seal, so a losing local-result
+                // publisher cannot insert Arrow buffers after the channel's 
one-time invalidation.
+                boolean interrupted = false;
+                while (flightSqlResultPublishers != 0) {

Review Comment:
   [P1] Avoid a wait-before-cancel teardown cycle
   
   This blocks teardown until every admitted publisher reaches 
`endFlightSqlResultPublication()`, but `FlightSqlConnectContext.kill(true)` 
reaches this wait through `unregisterConnection()` before it calls 
`cancelQuery()`. If the publisher is stuck in query work, the kill therefore 
waits forever before sending the signal that could let it finish. The new 
publication tests expose the same cycle deterministically by calling 
`sealAndCloseFlightSqlDeferredExecutors()` on the admitted publisher thread and 
only decrementing below that call. Cancel/signal first and wait from an 
independent owner, or make terminal sealing nonblocking/cancel-aware.



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