Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wPPRO-000iTd-0T for pgsql-hackers@arkaria.postgresql.org; Tue, 19 May 2026 18:45:18 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wPPRM-004rjw-00 for pgsql-hackers@arkaria.postgresql.org; Tue, 19 May 2026 18:45:16 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wPPRL-004rjo-0I for pgsql-hackers@lists.postgresql.org; Tue, 19 May 2026 18:45:16 +0000 Received: from fout-a1-smtp.messagingengine.com ([103.168.172.144]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1wPPRI-00000000Mzm-4Bb0 for pgsql-hackers@lists.postgresql.org; Tue, 19 May 2026 18:45:15 +0000 Received: from phl-compute-06.internal (phl-compute-06.internal [10.202.2.46]) by mailfout.phl.internal (Postfix) with ESMTP id D8B32EC025D; Tue, 19 May 2026 14:45:12 -0400 (EDT) Received: from phl-frontend-03 ([10.202.2.162]) by phl-compute-06.internal (MEProxy); Tue, 19 May 2026 14:45:12 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kurilemu.de; h= cc:cc:content-transfer-encoding:content-type:content-type:date :date:from:from:in-reply-to:in-reply-to:message-id:mime-version :reply-to:subject:subject:to:to; s=fm2; t=1779216312; x= 1779302712; bh=coASHVjerE6Jqh3X3b65KZ1ZrqQAmudZ2tEPK637+ng=; b=Z pTGMIdtIsty4VSVHd4+FOdkFjTJFVoPNBmEa1Pxtyg0K08oURvd+K7b0glwgh1h4 e/JAppXSkZ7dN8XiCkTt6UZLCUmL5E4kCzXywH4tmXobrYPLpSUsh0jRJ7kUPInw mHT8QX4ZczNg4A1LsE3BGB6O5TnI6vbPbB+t0/ZMf/PD01O1oZZ+fxrYAcO9Zc8I vbYGuV3Ygp6kp9Tr3rlXEBWBd4ovc9vnHM4Lt7X3XJ9e/9E7YRnTV2Bg/aovyoga ergNycUOcmiwgJbmdEVl8W2IWmR0rtTxNLjjQoJkRH64rlL1vlCd419SJaeUE9uh NacvE4IEpkBnHMBBT5uvQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:content-type:date:date:feedback-id:feedback-id :from:from:in-reply-to:in-reply-to:message-id:mime-version :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1779216312; x=1779302712; bh=c oASHVjerE6Jqh3X3b65KZ1ZrqQAmudZ2tEPK637+ng=; b=qe2N1Ucf9jdze41Ty ACHUQqU7CtiNchZa+qX06dQ0iDE3OyE+sQRNVivAkdMNL7v1Xyk8CdSDLLdl1hmt fJ3zsu+zrdbCX0eRAAxASS1bDe+8JABSxEOZ/VBqHft/6JjPpqIXwTxT9ha9BozQ 3VKHoYqbAzQUIo7OYGbXNbA5zV07q+wa8nc1oQoBryfHjn47ittt89w22AZVcbU3 4BDAQ9Q+Fzvg17MojwYttw91vB8mqTkJiFp0rQQGm2z5XSYqc+BlsvGTqj2SA7+y 7W1y9L/UBS1Ued+dgWDpiXkrveqLcTCz7K+t6gcFOzbzLyzkRCwN6JNf9ywv4Ryc LEV3g== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefhedrtddtgddugedvhedtucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucenucfjughrpeffhffvvefukfggtggugfgjsehmkeerre dttdejnecuhfhrohhmpeetlhhvrghrohcujfgvrhhrvghrrgcuoegrlhhvhhgvrhhrvges khhurhhilhgvmhhurdguvgeqnecuggftrfgrthhtvghrnhepteekudejudettdeigfetje egtdfffeeuteffgeffhffggfellefhffejtdehfedvnecuffhomhgrihhnpegvnhhtvghr phhrihhsvggusgdrtghomhenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmh grihhlfhhrohhmpegrlhhvhhgvrhhrvgeskhhurhhilhgvmhhurdguvgdpnhgspghrtghp thhtohepvddpmhhouggvpehsmhhtphhouhhtpdhrtghpthhtohepsggrjhhirdhpghguvg hvsehgmhgrihhlrdgtohhmpdhrtghpthhtohepphhgshhqlhdqhhgrtghkvghrsheslhhi shhtshdrphhoshhtghhrvghsqhhlrdhorhhg X-ME-Proxy: Feedback-ID: ie3de48e3:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Tue, 19 May 2026 14:45:12 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kurilemu.de; s=schmee; t=1779216305; bh=pCq0Fr/xSrqQIwl6o3Pp07u5LLGxTlSfovOgLmB/Jw8=; h=Date:From:To:Cc:Subject:In-Reply-To:From; b=XjY59WKRMW6lqikXHLvdFwGktyoQdiZljqfGP6FjtRjT3G6Zw8JKLZaXYrE1sjfz7 7F1Vq22FHj+vyoYRDQzx7qrxldxdiXAueguPOCtoFE6xrVZ/xImFxR4pevs2xsYvWH 1aqZkETpKmr4bXcJWvB6q6RwZxh3CoQ35R0eRWiZtOIkMbMdxbQNruiaay26Ri7kyM mM4wf81fuS/wJszDseQgTk3/g3Iy69SxNHzhVWvZvB/RAYjgRWoVMAaF5ZFV/pk5vl iapoLMQx92cptcmYSV6/P756S2Nx7NHoGBLJbvlL2C5OtReLjvAEWoe0HaNYrgSKFh vYDl5V2ksCyDQ== Received: by ida.kurilemu.internal (Postfix, from userid 1000) id 9288EB05EF5; Tue, 19 May 2026 20:45:05 +0200 (CEST) Date: Tue, 19 May 2026 11:45:05 -0700 From: Alvaro Herrera To: Baji Shaik Cc: pgsql-hackers@lists.postgresql.org Subject: Re: [PATCH] Fix REPACK decoding worker not cleaned up on FATAL exit Message-ID: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="b42ct6adeyjj4cbm" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --b42ct6adeyjj4cbm Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit On 2026-May-17, Baji Shaik wrote: > v3 uses PG_ENSURE_ERROR_CLEANUP which: > - Handles both ERROR and FATAL exits > - Automatically cancels the callback on normal completion > (no slot leak) > - Runs before memory contexts are destroyed (no use-after-free) Yeah, looks good. I have pushed it, with some comment wordsmithing and other cosmetic changes. While looking at it, I realized that I didn't like the way stop_repack_decoding_worker() works, mainly because if there's no handle, we leak everything else -- and the way we initialize things means we leak the shared memory segment. This is maybe a rare case and just a small memory leak, but it seems better to do it nicely. So here's a followup patch that reworks that code. This also forced me to understand more clearly what is going on, so I rewrote the comments. > - 20 REPACK (CONCURRENTLY) in same session completes without > slot exhaustion FWIW I tested this by doing "repack (concurrently) foo \watch 0.1" and letting it run for some time. I happened to notice that if I have two psqls running, one with the above and the second with the equivalent for table bar, when they run together, each runs more quickly than when only one of them is running. I don't know what causes this; I suspect/assume it's because the WAL messages for initial historic snapshot creation from one gets the other running. > I have not added a dedicated regression test for the > pg_terminate_backend scenario yet, but I can write one using > injection points if needed. I don't feel a need for that. -- Álvaro Herrera Breisgau, Deutschland — https://www.EnterpriseDB.com/ --b42ct6adeyjj4cbm Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0001-Restructure-repack-worker-teardown.patch" Content-Transfer-Encoding: 8bit From db6f070e73f89c38f8a2bfafa10b0718ea51a57d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Herrera?= Date: Mon, 18 May 2026 10:13:24 -0700 Subject: [PATCH] Restructure repack worker teardown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original code would leave a shared memory segment unreleased if we fail partway through initialization. Change the shutdown order so that we already free it. Author: Álvaro Herrera Discussion: https://postgr.es/m/agtNn6ZCmdI2KJFn@alvherre.pgsql --- src/backend/commands/repack.c | 67 ++++++++++++++++------------------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index bfc62c8f752..c9064d8fd13 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -3411,10 +3411,14 @@ start_repack_decoding_worker(Oid relid) shm_mq_handle *mqh; BackgroundWorker bgw; + decoding_worker = palloc0_object(DecodingWorker); + /* Setup shared memory. */ size = BUFFERALIGN(offsetof(DecodingWorkerShared, error_queue)) + BUFFERALIGN(REPACK_ERROR_QUEUE_SIZE); seg = dsm_create(size, 0); + decoding_worker->seg = seg; + shared = (DecodingWorkerShared *) dsm_segment_address(seg); shared->initialized = false; shared->lsn_upto = InvalidXLogRecPtr; @@ -3454,14 +3458,12 @@ start_repack_decoding_worker(Oid relid) bgw.bgw_main_arg = UInt32GetDatum(dsm_segment_handle(seg)); bgw.bgw_notify_pid = MyProcPid; - decoding_worker = palloc0_object(DecodingWorker); if (!RegisterDynamicBackgroundWorker(&bgw, &decoding_worker->handle)) ereport(ERROR, errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED), errmsg("out of background worker slots"), errhint("You might need to increase \"%s\".", "max_worker_processes")); - decoding_worker->seg = seg; decoding_worker->error_mqh = mqh; /* @@ -3487,17 +3489,6 @@ start_repack_decoding_worker(Oid relid) ConditionVariableCancelSleep(); } -/* - * PG_ENSURE_ERROR_CLEANUP callback to stop the decoding worker. - * This ensures the worker is terminated on both ERROR and FATAL exits, - * unlike PG_FINALLY which only handles ERROR. - */ -static void -repack_decoding_worker_cleanup_cb(int code, Datum arg) -{ - stop_repack_decoding_worker(); -} - /* * Stop the decoding worker and cleanup the related resources. * @@ -3508,39 +3499,43 @@ static void stop_repack_decoding_worker(void) { BgwHandleStatus status; + dsm_segment *dsmseg; - /* Haven't reached the worker startup? */ + /* Nothing to do if no worker was set up. */ if (decoding_worker == NULL) return; - /* Could not register the worker? */ - if (decoding_worker->handle == NULL) - return; + /* Terminate the worker process, if one is running. */ + if (decoding_worker->handle != NULL) + { + TerminateBackgroundWorker(decoding_worker->handle); + /* The worker should really exit before the REPACK command does. */ + HOLD_INTERRUPTS(); + status = WaitForBackgroundWorkerShutdown(decoding_worker->handle); + RESUME_INTERRUPTS(); - TerminateBackgroundWorker(decoding_worker->handle); - /* The worker should really exit before the REPACK command does. */ - HOLD_INTERRUPTS(); - status = WaitForBackgroundWorkerShutdown(decoding_worker->handle); - RESUME_INTERRUPTS(); - - if (status == BGWH_POSTMASTER_DIED) - ereport(FATAL, - errcode(ERRCODE_ADMIN_SHUTDOWN), - errmsg("postmaster exited during REPACK command")); - - shm_mq_detach(decoding_worker->error_mqh); + if (status == BGWH_POSTMASTER_DIED) + ereport(FATAL, + errcode(ERRCODE_ADMIN_SHUTDOWN), + errmsg("postmaster exited during REPACK command")); + } /* - * If we could not cancel the current sleep due to ERROR, do that before - * we detach from the shared memory the condition variable is located in. - * If we did not, the bgworker ERROR handling code would try and fail - * badly. + * Now detach from our shared memory segment. In error cases there might + * still be messages from the worker in the queue, which ProcessInterrupts + * would try to read; this is pointless (and causes an assertion failure), + * so set the global pointer to NULL to have ProcessRepackMessages ignore + * them. */ - ConditionVariableCancelSleep(); - - dsm_detach(decoding_worker->seg); + dsmseg = decoding_worker->seg; pfree(decoding_worker); decoding_worker = NULL; + + /* We must also cancel the current sleep, if one is still set up */ + ConditionVariableCancelSleep(); + + if (dsmseg != NULL) + dsm_detach(dsmseg); } /* stop_repack_decoding_worker, wrapped as a before_shmem_exit callback */ -- 2.47.3 --b42ct6adeyjj4cbm--