924060929 commented on code in PR #68283:
URL: https://github.com/apache/doris/pull/68283#discussion_r4070870141


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/execute/ConnectorRewriteDriver.java:
##########
@@ -224,30 +223,63 @@ public void onTaskFailed(Long taskId, Exception error) {
             tasks.add(task);
         }
 
+        List<ConnectorRewriteGroupTask> submitted = Lists.newArrayList();
         try {
-            for (TransientTaskExecutor task : tasks) {
+            for (ConnectorRewriteGroupTask task : tasks) {
                 
Env.getCurrentEnv().getTransientTaskManager().addMemoryTask(task);
+                submitted.add(task);

Review Comment:
   Fixed. The driver now takes the outer `StmtExecutor` and installs a sticky 
cancellation handoff: it picks up a cancellation that landed before the driver 
existed, registers a cancel delegate for the ones that arrive later, and 
re-polls the sticky `StmtExecutor.getPendingCancelReason()` in the group wait 
loop. On cancellation it cancels and drains the submitted groups. Source 
registration and the final commit both run under a lock that the cancellation 
handoff also takes, so a cancellation that wins the lock rolls back instead of 
registering/committing, and one that arrives while the critical section runs is 
linearized after the commit. Added 
`outerCancellationBeforeCommitRollsBackWithoutRegisteringOrCommitting` (a group 
completes, cancellation is observed exactly at the register/commit decision: 
rollback, no register, no commit) and 
`outerCancellationWhileWaitingDrainsLiveGroupsBeforeRollback`.
   



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/execute/ConnectorRewriteDriver.java:
##########
@@ -224,30 +223,63 @@ public void onTaskFailed(Long taskId, Exception error) {
             tasks.add(task);
         }
 
+        List<ConnectorRewriteGroupTask> submitted = Lists.newArrayList();
         try {
-            for (TransientTaskExecutor task : tasks) {
+            for (ConnectorRewriteGroupTask task : tasks) {
                 
Env.getCurrentEnv().getTransientTaskManager().addMemoryTask(task);
+                submitted.add(task);
             }
         } catch (JobException e) {
+            // Groups submitted before the failing call already bind the 
shared transaction; drain them so the
+            // caller never rolls that transaction back while a live group 
still reports into it.
+            drain(submitted);
             throw new UserException("Failed to submit rewrite tasks: " + 
e.getMessage(), e);
         }
 
         int maxWaitTime = ctx.getSessionVariable().getInsertTimeoutS();
         try {
             boolean completed = collector.await(maxWaitTime, TimeUnit.SECONDS);
             if (!completed) {
+                // The owner gave up waiting: stop every live group and wait 
for its terminal callback so no
+                // group is still reporting into the shared transaction when 
the caller rolls it back.
+                drain(submitted);
                 throw new UserException("Rewrite tasks did not complete within 
timeout");
             }
             if (collector.getFirstError() != null) {
                 throw new UserException("Some rewrite tasks failed: " + 
collector.getFirstError().getMessage(),
                         collector.getFirstError());
             }
         } catch (InterruptedException e) {
+            // The interrupt flag was cleared by the exception, so the drain 
below can still wait.
+            drain(submitted);
             Thread.currentThread().interrupt();
             throw new UserException("Wait for rewrite tasks completion was 
interrupted", e);
         }
     }
 
+    /**
+     * Cancels every submitted group and waits (bounded) for its terminal 
callback, so a shared transaction
+     * is never rolled back while a live group still has commit data flowing 
into it.
+     */
+    private void drain(List<ConnectorRewriteGroupTask> submitted) {
+        for (ConnectorRewriteGroupTask task : submitted) {
+            try {
+                task.cancel();
+            } catch (Exception e) {
+                LOG.warn("Failed to cancel rewrite task {}: {}", task.getId(), 
e.getMessage());
+            }
+        }
+        long waitSeconds = Math.max(1, 
ctx.getSessionVariable().getInsertTimeoutS());
+        for (ConnectorRewriteGroupTask task : submitted) {
+            try {
+                task.awaitTerminal(waitSeconds, TimeUnit.SECONDS);

Review Comment:
   Fixed. `drain` now takes a single budget and computes one shared deadline 
for all submitted groups instead of granting each a fresh `insert_timeout`, so 
a timed-out rewrite cannot hold the shared transaction/task graph for `T + 
G*T`. `ConnectorRewriteGroupTask.cancel()` also makes queued cancellation 
conclusively terminal: a task the scheduler has not started writes nothing, so 
it counts down its terminal latch immediately and a drain does not burn a full 
timeout on it. The timeout/interruption/Nth-submission tests and the new 
multi-task never-terminal and outer-cancel crossing tests cover this.
   



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