parallel-worker-fork-failure.patch
application/octet-stream
Filename: parallel-worker-fork-failure.patch
Type: application/octet-stream
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/access/transam/parallel.c | 82 | 2 |
| src/include/access/parallel.h | 1 | 0 |
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index d3431a7c30..f0b977704f 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -422,6 +422,11 @@ ReinitializeParallelDSM(ParallelContext *pcxt)
WaitForParallelWorkersToFinish(pcxt);
WaitForParallelWorkersToExit(pcxt);
pcxt->nworkers_launched = 0;
+ if (pcxt->any_message_received)
+ {
+ pfree(pcxt->any_message_received);
+ pcxt->any_message_received = NULL;
+ }
}
/* Reset a few bits of fixed parallel state to a clean state. */
@@ -520,6 +525,14 @@ LaunchParallelWorkers(ParallelContext *pcxt)
}
}
+ /*
+ * Now that nworkers_launched has taken its final value, we can initialize
+ * any_message_received.
+ */
+ if (pcxt->nworkers_launched > 0)
+ pcxt->any_message_received =
+ palloc0(sizeof(bool) * pcxt->nworkers_launched);
+
/* Restore previous memory context. */
MemoryContextSwitchTo(oldcontext);
}
@@ -541,6 +554,7 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
for (;;)
{
bool anyone_alive = false;
+ int nfinished = 0;
int i;
/*
@@ -552,7 +566,15 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
for (i = 0; i < pcxt->nworkers_launched; ++i)
{
- if (pcxt->worker[i].error_mqh != NULL)
+ /*
+ * If error_mqh is NULL, then the worker has already exited
+ * cleanly. If we have received a message through error_mqh from
+ * the worker, we know it started up cleanly, and therefore we're
+ * certain to be notified when it exits.
+ */
+ if (pcxt->worker[i].error_mqh == NULL)
+ ++nfinished;
+ else if (pcxt->any_message_received[i])
{
anyone_alive = true;
break;
@@ -560,7 +582,62 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
}
if (!anyone_alive)
- break;
+ {
+ /* If all workers are known to have finished, we're done. */
+ if (nfinished >= pcxt->nworkers_launched)
+ {
+ Assert(nfinished == pcxt->nworkers_launched);
+ break;
+ }
+
+ /*
+ * We didn't detect any living workers, but not all workers are
+ * known to have exited cleanly. Either not all workers have
+ * launched yet, or maybe some of them failed to start or
+ * terminated abnormally.
+ */
+ for (i = 0; i < pcxt->nworkers_launched; ++i)
+ {
+ pid_t pid;
+ shm_mq *mq;
+
+ /*
+ * If the worker is BGWH_NOT_YET_STARTED or BGWH_STARTED, we
+ * should just keep waiting. If it is BGWH_STOPPED, then
+ * further investigation is needed.
+ */
+ if (pcxt->worker[i].error_mqh == NULL ||
+ pcxt->worker[i].bgwhandle == NULL ||
+ GetBackgroundWorkerPid(pcxt->worker[i].bgwhandle,
+ &pid) != BGWH_STOPPED)
+ continue;
+
+ /*
+ * Check whether the worker ended up stopped without ever
+ * attaching to the error queue. If so, the postmaster was
+ * unable to fork the worker or it exited without initializing
+ * properly. We must throw an error, since the caller may
+ * have been expecting the worker to do some work before
+ * exiting.
+ */
+ mq = shm_mq_get_queue(pcxt->worker[i].error_mqh);
+ if (shm_mq_get_sender(mq) == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("parallel worker failed to initialize"),
+ errhint("More details may be available in the server log.")));
+
+ /*
+ * The worker is stopped, but is attached to the error queue.
+ * Unless there's a bug somewhere, this will only happen when
+ * the worker writes messages and terminates after the
+ * CHECK_FOR_INTERRUPTS() near the top of this function and
+ * before the call to GetBackgroundWorkerPid(). In that case,
+ * or latch should have been set as well and the right things
+ * will happen on the next pass through the loop.
+ */
+ }
+ }
WaitLatch(MyLatch, WL_LATCH_SET, -1,
WAIT_EVENT_PARALLEL_FINISH);
@@ -817,6 +894,9 @@ HandleParallelMessage(ParallelContext *pcxt, int i, StringInfo msg)
{
char msgtype;
+ if (pcxt->any_message_received != NULL)
+ pcxt->any_message_received[i] = true;
+
msgtype = pq_getmsgbyte(msg);
switch (msgtype)
diff --git a/src/include/access/parallel.h b/src/include/access/parallel.h
index f4db88294a..431fa9a06a 100644
--- a/src/include/access/parallel.h
+++ b/src/include/access/parallel.h
@@ -43,6 +43,7 @@ typedef struct ParallelContext
void *private_memory;
shm_toc *toc;
ParallelWorkerInfo *worker;
+ bool *any_message_received;
} ParallelContext;
typedef struct ParallelWorkerContext