Improve fix for not entering parallel mode when holding interrupts.
authorTom Lane <[email protected]>
Fri, 8 Nov 2024 18:42:01 +0000 (13:42 -0500)
committerTom Lane <[email protected]>
Fri, 8 Nov 2024 18:42:01 +0000 (13:42 -0500)
Commit ac04aa84a put the shutoff for this into the planner, which is
not ideal because it doesn't prevent us from re-using a previously
made parallel plan.  Revert the planner change and instead put the
shutoff into InitializeParallelDSM, modeling it on the existing code
there for recovering from failure to allocate a DSM segment.

However, that code path is mostly untested, and testing a bit harder
showed there's at least one bug: ExecHashJoinReInitializeDSM is not
prepared for us to have skipped doing parallel DSM setup.  I also
thought the Assert in ReinitializeParallelWorkers is pretty
ill-advised, and replaced it with a silent Min() operation.

The existing test case added by ac04aa84a serves fine to test this
version of the fix, so no change needed there.

Patch by me, but thanks to Noah Misch for the core idea that we
could shut off worker creation when !INTERRUPTS_CAN_BE_PROCESSED.
Back-patch to v12, as ac04aa84a was.

Discussion: https://postgr.es/m/CAC-SaSzHUKT=vZJ8MPxYdC_URPfax+yoA1hKTcF4ROz_Q6z0_Q@mail.gmail.com

src/backend/access/transam/parallel.c
src/backend/executor/nodeHashjoin.c
src/backend/optimizer/plan/planner.c

index 2b8bc2f58dd8e152f6085c92e51e415aa9cf800d..93f90cbd9105e172d9b2b661fd60ef22a8be6ddc 100644 (file)
@@ -228,6 +228,15 @@ InitializeParallelDSM(ParallelContext *pcxt)
    shm_toc_estimate_chunk(&pcxt->estimator, sizeof(FixedParallelState));
    shm_toc_estimate_keys(&pcxt->estimator, 1);
 
+   /*
+    * If we manage to reach here while non-interruptible, it's unsafe to
+    * launch any workers: we would fail to process interrupts sent by them.
+    * We can deal with that edge case by pretending no workers were
+    * requested.
+    */
+   if (!INTERRUPTS_CAN_BE_PROCESSED())
+       pcxt->nworkers = 0;
+
    /*
     * Normally, the user will have requested at least one worker process, but
     * if by chance they have not, we can skip a bunch of things here.
@@ -474,6 +483,9 @@ InitializeParallelDSM(ParallelContext *pcxt)
        shm_toc_insert(pcxt->toc, PARALLEL_KEY_ENTRYPOINT, entrypointstate);
    }
 
+   /* Update nworkers_to_launch, in case we changed nworkers above. */
+   pcxt->nworkers_to_launch = pcxt->nworkers;
+
    /* Restore previous memory context. */
    MemoryContextSwitchTo(oldcontext);
 }
@@ -537,10 +549,11 @@ ReinitializeParallelWorkers(ParallelContext *pcxt, int nworkers_to_launch)
 {
    /*
     * The number of workers that need to be launched must be less than the
-    * number of workers with which the parallel context is initialized.
+    * number of workers with which the parallel context is initialized.  But
+    * the caller might not know that InitializeParallelDSM reduced nworkers,
+    * so just silently trim the request.
     */
-   Assert(pcxt->nworkers >= nworkers_to_launch);
-   pcxt->nworkers_to_launch = nworkers_to_launch;
+   pcxt->nworkers_to_launch = Min(pcxt->nworkers, nworkers_to_launch);
 }
 
 /*
index 0ec48025ff9e207cbf5d42f995f96840973de7af..c40d8a722b7c5dfd45df19553f6cac7edd2e0c44 100644 (file)
@@ -1622,8 +1622,13 @@ void
 ExecHashJoinReInitializeDSM(HashJoinState *state, ParallelContext *pcxt)
 {
    int         plan_node_id = state->js.ps.plan->plan_node_id;
-   ParallelHashJoinState *pstate =
-       shm_toc_lookup(pcxt->toc, plan_node_id, false);
+   ParallelHashJoinState *pstate;
+
+   /* Nothing to do if we failed to create a DSM segment. */
+   if (pcxt->seg == NULL)
+       return;
+
+   pstate = shm_toc_lookup(pcxt->toc, plan_node_id, false);
 
    /*
     * It would be possible to reuse the shared hash table in single-batch
index d46ad3999d9524bf4c002c25f9ce5f01928bf624..a1bceafa30f9698af5b73c03e473ea5c07c9ee2a 100644 (file)
@@ -338,11 +338,6 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
     * if we want to allow parallel inserts in general; updates and deletes
     * have additional problems especially around combo CIDs.)
     *
-    * We don't try to use parallel mode unless interruptible.  The leader
-    * expects ProcessInterrupts() calls to reach HandleParallelMessages().
-    * Even if we called HandleParallelMessages() another way, starting a
-    * parallel worker is too delay-prone to be prudent when uncancellable.
-    *
     * For now, we don't try to use parallel mode if we're running inside a
     * parallel worker.  We might eventually be able to relax this
     * restriction, but for now it seems best not to have parallel workers
@@ -353,7 +348,6 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
        parse->commandType == CMD_SELECT &&
        !parse->hasModifyingCTE &&
        max_parallel_workers_per_gather > 0 &&
-       INTERRUPTS_CAN_BE_PROCESSED() &&
        !IsParallelWorker())
    {
        /* all the cheap tests pass, so scan the query tree */