Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Handle interrupts while waiting on Append's async subplans
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Wed, 12 Mar 2025 18:53:09 +0000 (20:53 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Wed, 12 Mar 2025 18:53:19 +0000 (20:53 +0200)
We did not wake up on interrupts while waiting on async events on an
async-capable append node. For example, if you tried to cancel the
query, nothing would happen until one of the async subplans becomes
readable. To fix, add WL_LATCH_SET to the WaitEventSet.

Backpatch down to v14 where async Append execution was introduced.

Discussion: https://www.postgresql.org/message-id/37a40570-f558-40d3-b5ea-5c2079b3b30b@iki.fi

src/backend/executor/nodeAppend.c

index 338484bac9ec41bf14aa99ce1c1253de2864369a..0dcd18320b0ba1e252a8c80d3cb991b58e22aa2d 100644 (file)
@@ -1016,7 +1016,7 @@ ExecAppendAsyncRequest(AppendState *node, TupleTableSlot **result)
 static void
 ExecAppendAsyncEventWait(AppendState *node)
 {
-   int         nevents = node->as_nasyncplans + 1;
+   int         nevents = node->as_nasyncplans + 2;
    long        timeout = node->as_syncdone ? -1 : 0;
    WaitEvent   occurred_event[EVENT_BUFFER_SIZE];
    int         noccurred;
@@ -1043,13 +1043,28 @@ ExecAppendAsyncEventWait(AppendState *node)
        }
 
        /*
-        * If there are no configured events other than the postmaster death
-        * event, we don't need to wait or poll.
+        * No need for further processing if none of the subplans configured
+        * any events.
         */
        if (GetNumRegisteredWaitEvents(node->as_eventset) == 1)
            noccurred = 0;
        else
        {
+           /*
+            * Add the process latch to the set, so that we wake up to process
+            * the standard interrupts with CHECK_FOR_INTERRUPTS().
+            *
+            * NOTE: For historical reasons, it's important that this is added
+            * to the WaitEventSet after the ExecAsyncConfigureWait() calls.
+            * Namely, postgres_fdw calls "GetNumRegisteredWaitEvents(set) ==
+            * 1" to check if any other events are in the set.  That's a poor
+            * design, it's questionable for postgres_fdw to be doing that in
+            * the first place, but we cannot change it now.  The pattern has
+            * possibly been copied to other extensions too.
+            */
+           AddWaitEventToSet(node->as_eventset, WL_LATCH_SET, PGINVALID_SOCKET,
+                             MyLatch, NULL);
+
            /* Return at most EVENT_BUFFER_SIZE events in one call. */
            if (nevents > EVENT_BUFFER_SIZE)
                nevents = EVENT_BUFFER_SIZE;
@@ -1098,6 +1113,13 @@ ExecAppendAsyncEventWait(AppendState *node)
                ExecAsyncNotify(areq);
            }
        }
+
+       /* Handle standard interrupts */
+       if ((w->events & WL_LATCH_SET) != 0)
+       {
+           ResetLatch(MyLatch);
+           CHECK_FOR_INTERRUPTS();
+       }
    }
 }