In [1] I noted that ERROR like "lost connection to ... worker" (currently
issued in ProcessParallelMessages()) might also be appropriate for REPACK
(CONCURRENTLY). While I'm still not sure about that, I suspect that something
is wrong about handling unclean exits in parallel.c.

According to the header comment, ParallelWorkerShutdown() should handle cases
like direct call of proc_exit(). By adding one like this

diff --git a/src/backend/access/transam/parallel.c 
b/src/backend/access/transam/parallel.c
index 17fcd246b0c..e8700ce8594 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -1385,6 +1385,7 @@ ParallelWorkerMain(Datum main_arg)
         * Hooray! Primary initialization is complete.  Now, we need to set up 
our
         * backend-local state to match the original backend.
         */
+       proc_exit(1);
 
        /*
         * Join locking group.  We must do this before anything that could try 
to

and by running the following

begin;
create table a(i int);
set parallel_setup_cost to 0;
set parallel_tuple_cost to 0;
set min_parallel_table_scan_size to 0;
set min_parallel_index_scan_size to 0;
set max_parallel_workers_per_gather to 1;
set parallel_leader_participation to off;
set debug_parallel_query to 'regress';
table a;
rollback;

I can reproduce the expected behavior:

ERROR:  lost connection to parallel worker


However, an additional sleep() call in ParallelWorkerShutdown()

diff --git a/src/backend/access/transam/parallel.c 
b/src/backend/access/transam/parallel.c
index 17fcd246b0c..1a05f16e0bc 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -1618,7 +1618,7 @@ ParallelWorkerShutdown(int code, Datum arg)
        SendProcSignal(ParallelLeaderPid,
                                   PROCSIG_PARALLEL_MESSAGE,
                                   ParallelLeaderProcNumber);
-
+       sleep(1);
        dsm_detach((dsm_segment *) DatumGetPointer(arg));
 }
 
appears to cause race conditions. What I see is that the leader process runs
ProcessParallelMessages() while the worker is still attached to the error
queue, so the leader gets SHM_MQ_WOULD_BLOCK when trying to read from the
queue. Thus the ERROR is not raised.

Moreover, the leader, after having seen the worker detached from the tuple
queue (TupleQueueReaderNext) a bit later, starts executor cleanup and, as the
worker does not send signals anymore, it gets stuck in
WaitForParallelWorkersToExit().

Attached is what might be a fix, but I'm not sure if the Terminate message is
appropriate even if the worker in fact didn't finish with success.


[1] https://www.postgresql.org/message-id/32123.1788806780%40localhost

-- 
Antonin Houska
Web: https://www.cybertec-postgresql.com

diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index 17fcd246b0c..b4802623b80 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -1597,10 +1598,9 @@ ParallelWorkerReportLastRecEnd(XLogRecPtr last_xlog_end)
 }
 
 /*
- * Make sure the leader tries to read from our error queue one more time.
- * This guards against the case where we exit uncleanly without sending an
- * ErrorResponse to the leader, for example because some code calls proc_exit
- * directly.
+ * Make sure the leader knows we're done.  This guards against the case where
+ * we exit uncleanly without sending an ErrorResponse to the leader, for
+ * example because some code calls proc_exit directly.
  *
  * Also explicitly detach from dsm segment so that subsystems using
  * on_dsm_detach() have a chance to send stats before the stats subsystem is
@@ -1615,6 +1615,8 @@ ParallelWorkerReportLastRecEnd(XLogRecPtr last_xlog_end)
 static void
 ParallelWorkerShutdown(int code, Datum arg)
 {
+	pq_putmessage(PqMsg_Terminate, NULL, 0);
+
 	SendProcSignal(ParallelLeaderPid,
 				   PROCSIG_PARALLEL_MESSAGE,
 				   ParallelLeaderProcNumber);

Reply via email to