public inbox for [email protected]
help / color / mirror / Atom feed[PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
67+ messages / 12 participants
[nested] [flat]
* [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-03 09:27 Aya Iwata (Fujitsu) <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2025-10-03 09:27 UTC (permalink / raw)
To: pgsql-hackers
Hi,
Background
==========
If the background workers connect to databases, some database-related commands
like ALTER DATABASE RENAME and ALTER DATABASE SET TABLESPACE cannot be done.
Users must do DROP EXTENSION related with workers, or terminate them by themselves
if they want to drop or alter the database.
Proposal
========
Based on above, I would like to propose to terminate background workers automatically
when such SQLs are executed.
This feature allows the DBMS daemon to send a termination signal to background workers
created by users currently operating on the database when executing commands that make
significant changes to the database.
To receive the termination signal, the background worker must call the
AcceptBackgroundWorkerCancel() function, using the database's OID and a flag
indicating whether to terminate. This means existing background worker processes
will not abruptly terminate.
This termination occurs when executing the DROP DATABASE, ALTER DATABASE RENAME TO,
or ALTER DATABASE SET TABLE SPACE commands, which check the existence of processes.
When a user creates a background worker to perform some data processing or monitoring,
and wants to terminate it along with the database deletion, this feature enables
achieving that goal.
The test set for this feature will be shared later.
How do you feel? Your feedback is very welcome.
Regards,
Aya Iwata
Fujitsu Limited
Attachments:
[application/octet-stream] v0001-0001-Allow-background-workers-to-be-terminated.patch (6.5K, 3-v0001-0001-Allow-background-workers-to-be-terminated.patch)
download | inline diff:
From 81f67744ed2b191e1613f2fd8b2ab5a271639429 Mon Sep 17 00:00:00 2001
From: "iwata.aya" <[email protected]>
Date: Thu, 11 Sep 2025 21:16:51 +0900
Subject: [PATCH v0001] Allow background workers to be terminated at DROP
DATABASE
---
doc/src/sgml/bgworker.sgml | 12 ++++
src/backend/postmaster/bgworker.c | 88 +++++++++++++++++++++++++++++
src/backend/storage/ipc/procarray.c | 6 ++
src/include/postmaster/bgworker.h | 13 +++++
4 files changed, 119 insertions(+)
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index 2c393385a91..cef2ef53c15 100644
--- a/doc/src/sgml/bgworker.sgml
+++ b/doc/src/sgml/bgworker.sgml
@@ -283,6 +283,18 @@ typedef struct BackgroundWorker
<literal>BGWH_POSTMASTER_DIED</literal>.
</para>
+ <para>
+ By using <function>AcceptBackgroundWorkerCancel(<parameter>oid</parameter>,
+ <parameter>int</parameter>)</function> with the database's OID and a flag
+ value indicating whether to cancel, the DBMS daemon can issue a termination
+ signal to the background worker when changes occur in the database it is
+ connected to, thereby terminating the target background worker. This occurs
+ only when significant changes affecting the entire database take place.
+ Specifically, major changes include when the <command>DROP DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>, and
+ <command>ALTER DATABASE SET TABLESPACE</command> commands are executed.
+ </para>
+
<para>
Background workers can send asynchronous notification messages, either by
using the <command>NOTIFY</command> command via <acronym>SPI</acronym>,
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 1ad65c237c3..bcdd931af06 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -356,6 +356,22 @@ BackgroundWorkerStateChange(bool allow_new_workers)
return;
}
+ /*
+ * Set shmem slot number, and initialize cancel flags.
+ */
+ rw->rw_worker.bgw_shmem_slot = slotno;
+
+ rw->rw_worker.bgw_cancel_databaseId = InvalidOid;
+ rw->rw_worker.bgw_cancel_flags = BGWORKER_CANCEL_NOACCEPT;
+
+ /*
+ * Update the contents in the shared memory also, these are used in
+ * EXEC_BACKEND (win32) case
+ */
+ slot->worker.bgw_shmem_slot = slotno;
+ slot->worker.bgw_cancel_databaseId = InvalidOid;
+ slot->worker.bgw_cancel_flags = BGWORKER_CANCEL_NOACCEPT;
+
/*
* Copy strings in a paranoid way. If shared memory is corrupted, the
* source data might not even be NUL-terminated.
@@ -1396,3 +1412,75 @@ GetBackgroundWorkerTypeByPid(pid_t pid)
return result;
}
+
+/*
+ * Accept background worker cancel.
+ * Set cancel flags and databaseId.
+ */
+void
+AcceptBackgroundWorkerCancel(Oid databaseId, int cancel_flags)
+{
+ int slotno;
+ BackgroundWorkerSlot *slot;
+
+ /* Get shmem slot number from BGW entry. */
+ Assert(MyBgworkerEntry);
+ slotno = MyBgworkerEntry->bgw_shmem_slot;
+
+ /* Get shmem slot address. */
+ Assert(slotno < BackgroundWorkerData->total_slots);
+ slot = &BackgroundWorkerData->slot[slotno];
+
+ /* Set cancel flags and databaseId to sgmem slot. */
+ /* 1st, set databaseId. */
+ slot->worker.bgw_cancel_databaseId = databaseId;
+ /* 2nd, set cancel flags. */
+ slot->worker.bgw_cancel_flags = cancel_flags;
+
+ /*
+ * This operation doesn't need LOCK, because 'bgw_cancel_flags' is 32bit
+ * value.
+ */
+}
+
+/*
+ * Cancel background workers.
+ */
+void
+CancelBackgroundWorkers(Oid databaseId, int cancel_flags)
+{
+ int slotno;
+ bool signal_postmaster = false;
+
+ LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
+
+ for (slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
+ {
+ BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
+
+ /* Check worker slot. */
+ if (slot->in_use)
+ {
+ /* 1st, check cancel flags. */
+ if (slot->worker.bgw_cancel_flags & cancel_flags)
+ {
+ /* 2nd, compare databaseId. */
+ if (slot->worker.bgw_cancel_databaseId == databaseId)
+ {
+ /*
+ * Set terminate flag in shared memory, unless slot has
+ * been reused.
+ */
+ slot->terminate = true;
+ signal_postmaster = true;
+ }
+ }
+ }
+ }
+
+ LWLockRelease(BackgroundWorkerLock);
+
+ /* Make sure the postmaster notices the change to shared memory. */
+ if (signal_postmaster)
+ SendPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE);
+}
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 200f72c6e25..36571354324 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -56,6 +56,7 @@
#include "catalog/pg_authid.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/bgworker.h"
#include "port/pg_lfind.h"
#include "storage/proc.h"
#include "storage/procarray.h"
@@ -3768,6 +3769,11 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
for (index = 0; index < nautovacs; index++)
(void) kill(autovac_pids[index], SIGTERM); /* ignore any error */
+ /*
+ * Cancel background workers by admin commands.
+ */
+ CancelBackgroundWorkers(databaseId, BGWORKER_CANCEL_ADMIN_COMMANDS);
+
/* sleep, then try again */
pg_usleep(100 * 1000L); /* 100ms */
}
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index 058667a47a0..8cf49f5810b 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -66,8 +66,14 @@
* background workers should not use this class.
*/
#define BGWORKER_CLASS_PARALLEL 0x0010
+
/* add additional bgworker classes here */
+/*
+ * Flags for cancel by admin commands.
+ */
+#define BGWORKER_CANCEL_NOACCEPT 0x0000
+#define BGWORKER_CANCEL_ADMIN_COMMANDS 0x0001
typedef void (*bgworker_main_type) (Datum main_arg);
@@ -98,6 +104,9 @@ typedef struct BackgroundWorker
Datum bgw_main_arg;
char bgw_extra[BGW_EXTRALEN];
pid_t bgw_notify_pid; /* SIGUSR1 this backend on start/stop */
+ int bgw_shmem_slot; /* shmem slot ID */
+ Oid bgw_cancel_databaseId; /* cancel target */
+ int bgw_cancel_flags; /* cancel by admin commands */
} BackgroundWorker;
typedef enum BgwHandleStatus
@@ -161,4 +170,8 @@ extern void BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, ui
extern void BackgroundWorkerBlockSignals(void);
extern void BackgroundWorkerUnblockSignals(void);
+/* Cancel background workers. */
+extern void AcceptBackgroundWorkerCancel(Oid databaseId, int cancel_flags);
+extern void CancelBackgroundWorkers(Oid databaseId, int cancel_flags);
+
#endif /* BGWORKER_H */
--
2.39.3
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-06 01:59 Hayato Kuroda (Fujitsu) <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2025-10-06 01:59 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: pgsql-hackers
Dear Iwata-san,
>Background
>==========
>If the background workers connect to databases, some database-related commands
>like ALTER DATABASE RENAME and ALTER DATABASE SET TABLESPACE cannot be done.
>Users must do DROP EXTENSION related with workers, or terminate them by themselves
>if they want to drop or alter the database.
>
>Proposal
>========
>Based on above, I would like to propose to terminate background workers automatically
>when such SQLs are executed.
>
>This feature allows the DBMS daemon to send a termination signal to background workers
>created by users currently operating on the database when executing commands that make
>significant changes to the database.
Per my understanding, we already have a facility that terminates a background
worker, TerminateBackgroundWorker(). So, I'm afraid your proposal has already
been done by combining this function and ProcessUtility_hook.
So, is the main benefit of the patch to shorten extensions codes which uses
bgworker?
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-06 03:17 Michael Paquier <[email protected]>
parent: Hayato Kuroda (Fujitsu) <[email protected]>
0 siblings, 2 replies; 67+ messages in thread
From: Michael Paquier @ 2025-10-06 03:17 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: Aya Iwata (Fujitsu) <[email protected]>; pgsql-hackers
On Mon, Oct 06, 2025 at 01:59:08AM +0000, Hayato Kuroda (Fujitsu) wrote:
> Per my understanding, we already have a facility that terminates a background
> worker, TerminateBackgroundWorker(). So, I'm afraid your proposal has already
> been done by combining this function and ProcessUtility_hook.
The main take that I am getting here from Iwata-san is that this would
lead to less code duplication. Another item, which you are not
mentioning, is that this would be more flexible with bgworkers that
have been starting dynamically, where shared_preload_libraries may not
be used, still a bgworker would need to react. So the suggestion of a
new API to control if a bgworker should be stopped like any other
backend when there is a database activity worth it is a good one, as
long as it is in line with what we do with normal backends.
AcceptBackgroundWorkerCancel() is going backwards, IMO. Wouldn't it
be better to pass it down as an option in bgw_flags, to mark that a
bgworker connected to a database can be shutdown due to the effect of
a database getting dropped or moved? There could be an argument
behind using bgw_extra, but that would not be in line with the
database connection argument which is a state defined when a bgworker
is registered.
> So, is the main benefit of the patch to shorten extensions codes which uses
> bgworker?
I'd ask for the addition of tests when it comes to such a facility,
and your proposal has none of that. I would suggest worker_spi with
an option that can be passed to worker_spi_launch().
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, 2-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-06 09:15 Hayato Kuroda (Fujitsu) <[email protected]>
parent: Michael Paquier <[email protected]>
1 sibling, 0 replies; 67+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2025-10-06 09:15 UTC (permalink / raw)
To: 'Michael Paquier' <[email protected]>; +Cc: Aya Iwata (Fujitsu) <[email protected]>; pgsql-hackers
Dear Michael,
> The main take that I am getting here from Iwata-san is that this would
> lead to less code duplication. Another item, which you are not
> mentioning, is that this would be more flexible with bgworkers that
> have been starting dynamically, where shared_preload_libraries may not
> be used, still a bgworker would need to react. So the suggestion of a
> new API to control if a bgworker should be stopped like any other
> backend when there is a database activity worth it is a good one, as
> long as it is in line with what we do with normal backends.
Okay, so this proposal has the advantage that we can terminate workers, even if the
extensions do not control workers on the shared memory, right?
> AcceptBackgroundWorkerCancel() is going backwards, IMO. Wouldn't it
> be better to pass it down as an option in bgw_flags, to mark that a
> bgworker connected to a database can be shutdown due to the effect of
> a database getting dropped or moved?
+1 to extend the bgw_flags.
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-06 11:21 Aya Iwata (Fujitsu) <[email protected]>
parent: Michael Paquier <[email protected]>
1 sibling, 2 replies; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2025-10-06 11:21 UTC (permalink / raw)
To: 'Michael Paquier' <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: pgsql-hackers
Hi,
Thank you for your comments.
> On Mon, Oct 06, 2025 at 01:59:08AM +0000, Hayato Kuroda (Fujitsu) wrote:
> > Per my understanding, we already have a facility that terminates a
> background
> > worker, TerminateBackgroundWorker(). So, I'm afraid your proposal has
> already
> > been done by combining this function and ProcessUtility_hook.
>
> The main take that I am getting here from Iwata-san is that this would
> lead to less code duplication.
Yeah. My company sometimes wrote extensions which uses background workers,
and it was painful to implement the same part every time.
> lead to less code duplication. Another item, which you are not
> mentioning, is that this would be more flexible with bgworkers that
> have been starting dynamically, where shared_preload_libraries may not
> be used, still a bgworker would need to react. So the suggestion of a
> new API to control if a bgworker should be stopped like any other
> backend when there is a database activity worth it is a good one, as
> long as it is in line with what we do with normal backends.
Thanks for the agreement. Thus I want to proceed the patch.
> AcceptBackgroundWorkerCancel() is going backwards, IMO. Wouldn't it
> be better to pass it down as an option in bgw_flags, to mark that a
> bgworker connected to a database can be shutdown due to the effect of
> a database getting dropped or moved? There could be an argument
> behind using bgw_extra, but that would not be in line with the
> database connection argument which is a state defined when a bgworker
> is registered.
I updated my patch using bgw_flags to set whether accept to terminate bgworker or not.
And I also removed AcceptBackgroundWorkerCancel() function.
Please check my attached patch.
>
> > So, is the main benefit of the patch to shorten extensions codes which uses
> > bgworker?
>
> I'd ask for the addition of tests when it comes to such a facility,
> and your proposal has none of that. I would suggest worker_spi with
> an option that can be passed to worker_spi_launch().
I added the TAP test using worker_spi too.
Regards,
Aya Iwata
Fujitsu Limited
Attachments:
[application/octet-stream] v0002-0001-Allow-background-workers-to-be-terminated.patch (9.8K, 2-v0002-0001-Allow-background-workers-to-be-terminated.patch)
download | inline diff:
From 172b7df5273d1e90d8d9d550607a17ba6859d197 Mon Sep 17 00:00:00 2001
From: "iwata.aya" <[email protected]>
Date: Thu, 11 Sep 2025 21:16:51 +0900
Subject: [PATCH v0002] Allow background workers to be terminated at DROP
DATABASE
---
doc/src/sgml/bgworker.sgml | 17 ++++
src/backend/postmaster/bgworker.c | 49 ++++++++++++
src/backend/storage/ipc/procarray.c | 6 ++
src/include/postmaster/bgworker.h | 16 ++++
src/test/modules/worker_spi/meson.build | 1 +
.../worker_spi/t/002_worker_terminate.pl | 80 +++++++++++++++++++
src/test/modules/worker_spi/worker_spi.c | 7 +-
7 files changed, 174 insertions(+), 2 deletions(-)
create mode 100644 src/test/modules/worker_spi/t/002_worker_terminate.pl
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index 2c393385a91..dd3d529d762 100644
--- a/doc/src/sgml/bgworker.sgml
+++ b/doc/src/sgml/bgworker.sgml
@@ -108,6 +108,23 @@ typedef struct BackgroundWorker
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>BGWORKER_EXIT_AT_DATABASE_DROP</literal></term>
+ <listitem>
+ <para>
+ <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_DROP</primary></indexterm>
+ Requests to terminate background worker when the database connected by
+ the background worker is changed. DBMS damon can issue a termination
+ signal to the background worker.
+ This occurs only when significant changes affecting the entire database
+ take place.
+ Specifically, major changes include when the <command>DROP DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>, and
+ <command>ALTER DATABASE SET TABLESPACE</command> commands are executed.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 1ad65c237c3..337efca38bd 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -26,6 +26,7 @@
#include "storage/lwlock.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
+#include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/shmem.h"
#include "tcop/tcopprot.h"
@@ -1396,3 +1397,51 @@ GetBackgroundWorkerTypeByPid(pid_t pid)
return result;
}
+
+
+/*
+ * Cancel background workers.
+ */
+void
+CancelBackgroundWorkers(Oid databaseId, int cancel_flags)
+{
+ int slotno;
+ bool signal_postmaster = false;
+
+ LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
+
+ for (slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
+ {
+ BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
+
+ /* Check worker slot. */
+ if (!slot->in_use)
+ continue;
+
+ /* 1st, check cancel flags. */
+ if ((slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_DROP) & cancel_flags)
+ {
+ PGPROC *proc = BackendPidGetProc(slot->pid);
+
+ if (!proc)
+ continue;
+
+ /* 2nd, compare databaseId. */
+ if (proc->databaseId == databaseId)
+ {
+ /*
+ * Set terminate flag in shared memory, unless slot has
+ * been reused.
+ */
+ slot->terminate = true;
+ signal_postmaster = true;
+ }
+ }
+ }
+
+ LWLockRelease(BackgroundWorkerLock);
+
+ /* Make sure the postmaster notices the change to shared memory. */
+ if (signal_postmaster)
+ SendPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE);
+}
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 200f72c6e25..36571354324 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -56,6 +56,7 @@
#include "catalog/pg_authid.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/bgworker.h"
#include "port/pg_lfind.h"
#include "storage/proc.h"
#include "storage/procarray.h"
@@ -3768,6 +3769,11 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
for (index = 0; index < nautovacs; index++)
(void) kill(autovac_pids[index], SIGTERM); /* ignore any error */
+ /*
+ * Cancel background workers by admin commands.
+ */
+ CancelBackgroundWorkers(databaseId, BGWORKER_CANCEL_ADMIN_COMMANDS);
+
/* sleep, then try again */
pg_usleep(100 * 1000L); /* 100ms */
}
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index 058667a47a0..637cd753c22 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -59,6 +59,14 @@
*/
#define BGWORKER_BACKEND_DATABASE_CONNECTION 0x0002
+/*
+ * This flag means the bgworker must be exit when the connecting database is
+ * being dropped or moved.
+ * It requires both BGWORKER_SHMEM_ACCESS and
+ * BGWORKER_BACKEND_DATABASE_CONNECTION were passed too.
+ */
+#define BGWORKER_EXIT_AT_DATABASE_DROP 0x0004
+
/*
* This class is used internally for parallel queries, to keep track of the
* number of active parallel workers and make sure we never launch more than
@@ -68,6 +76,11 @@
#define BGWORKER_CLASS_PARALLEL 0x0010
/* add additional bgworker classes here */
+/*
+ * Flags for cancel by admin commands.
+ */
+#define BGWORKER_CANCEL_NOACCEPT 0x0000
+#define BGWORKER_CANCEL_ADMIN_COMMANDS 0x0004
typedef void (*bgworker_main_type) (Datum main_arg);
@@ -161,4 +174,7 @@ extern void BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, ui
extern void BackgroundWorkerBlockSignals(void);
extern void BackgroundWorkerUnblockSignals(void);
+/* Cancel background workers. */
+extern void CancelBackgroundWorkers(Oid databaseId, int cancel_flags);
+
#endif /* BGWORKER_H */
diff --git a/src/test/modules/worker_spi/meson.build b/src/test/modules/worker_spi/meson.build
index d673ece48a0..1d30048aec8 100644
--- a/src/test/modules/worker_spi/meson.build
+++ b/src/test/modules/worker_spi/meson.build
@@ -28,6 +28,7 @@ tests += {
'tap': {
'tests': [
't/001_worker_spi.pl',
+ 't/002_worker_terminate.pl'
],
},
}
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
new file mode 100644
index 00000000000..78dcd40ae34
--- /dev/null
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -0,0 +1,80 @@
+# Copyright (c) 2023-2025, PostgreSQL Global Development Group
+
+# Test background workers can be terminated when ALTER/DROP DATABASE happens
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Ensure the worker_spi dynamic worker is launched on the specified database
+sub launch_bgworker
+{
+ my ($node, $database) = @_;
+
+ # Launch a background worker on the given database
+ my $result = $node->safe_psql(
+ $database, qq(
+ SELECT worker_spi_launch(4, oid) IS NOT NULL
+ FROM pg_database WHERE datname = '$database';
+ ));
+ is($result, 't', "dynamic bgworker launched");
+
+ # Check the worker is running correctly
+ $result = $node->safe_psql('postgres',
+ "SELECT count(1) FROM pg_stat_activity WHERE datname = '$database' AND backend_type = 'worker_spi dynamic';"
+ );
+ is($result, 1, 'dynamic bgworker has exist');
+}
+
+# Run the given query and verify the background worker can be terminated
+sub run_db_command
+{
+ my ($node, $command, $testname) = @_;
+ my $offset = -s $node->logfile;
+
+ $node->safe_psql('postgres', $command);
+ ok( $node->log_contains(
+ "terminating background worker \"worker_spi dynamic\" due to administrator command",
+ $offset),
+ "background worker can be terminated at $testname");
+}
+
+my $node = PostgreSQL::Test::Cluster->new('mynode');
+$node->init;
+$node->start;
+
+# Create another database. "postgres" is still used to run db commands and
+# checks the pg_stat_activity.
+$node->safe_psql('postgres', 'CREATE DATABASE testdb');
+$node->safe_psql('testdb', 'CREATE EXTENSION worker_spi;');
+
+launch_bgworker($node, 'testdb');
+
+# Testcase 1: ALTER DATABASE SET TABLESPACE
+run_db_command(
+ $node,
+ "ALTER DATABASE testdb RENAME TO renameddb",
+ "ALTER DATABASE SET TABLESPACE");
+
+# Preparation for the next test; create another tablespace
+my $basedir = $node->basedir();
+my $tablespace = "$basedir/tablespace";
+mkdir($tablespace);
+$node->safe_psql('postgres',
+ "CREATE TABLESPACE test_tablespace LOCATION '$tablespace'");
+launch_bgworker($node, 'renameddb');
+
+# Testcase 2: ALTER DATABASE SET TABLESPACE
+run_db_command(
+ $node,
+ "ALTER DATABASE renameddb SET TABLESPACE test_tablespace",
+ "ALTER DATABASE SET TABLESPACE");
+
+launch_bgworker($node, 'renameddb');
+
+# Testcase 2: DROP DATABASE
+run_db_command($node, "DROP DATABASE renameddb", "DROP DATABASE");
+
+done_testing();
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index bea8339f464..02bed2ac110 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -128,6 +128,7 @@ initialize_worker_spi(worktable *table)
CommitTransactionCommand();
debug_query_string = NULL;
pgstat_report_activity(STATE_IDLE, NULL);
+
}
void
@@ -361,7 +362,8 @@ _PG_init(void)
/* set up common data for all our workers */
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
- BGWORKER_BACKEND_DATABASE_CONNECTION;
+ BGWORKER_BACKEND_DATABASE_CONNECTION |
+ BGWORKER_EXIT_AT_DATABASE_DROP;
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART;
sprintf(worker.bgw_library_name, "worker_spi");
@@ -407,7 +409,8 @@ worker_spi_launch(PG_FUNCTION_ARGS)
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
- BGWORKER_BACKEND_DATABASE_CONNECTION;
+ BGWORKER_BACKEND_DATABASE_CONNECTION |
+ BGWORKER_EXIT_AT_DATABASE_DROP;
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART;
sprintf(worker.bgw_library_name, "worker_spi");
--
2.39.3
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-07 02:00 Hayato Kuroda (Fujitsu) <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
1 sibling, 0 replies; 67+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2025-10-07 02:00 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; 'Michael Paquier' <[email protected]>; +Cc: pgsql-hackers
Dear Iwata-san,
Thanks for updating the patch.
> I updated my patch using bgw_flags to set whether accept to terminate bgworker
> or not.
> And I also removed AcceptBackgroundWorkerCancel() function.
> Please check my attached patch.
```
+/*
+ * Cancel background workers.
+ */
+void
+CancelBackgroundWorkers(Oid databaseId, int cancel_flags)
```
Do we still need the cancel_flags? I cannot find other reasons to terminate
workers. Also the things I don't like is that BGWORKER_CANCEL_ADMIN_COMMANDS must
have the same value as BGWORKER_EXIT_AT_DATABASE_DROP. Only one flag exists but
it has 0x0004. Can we remove the argument and flags from the patch?
[1]: https://www.postgresql.org/message-id/OSCPR01MB149662AEA64F4E66F494C2584F5E3A%40OSCPR01MB14966.jpnpr...
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-07 03:12 Peter Smith <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
1 sibling, 1 reply; 67+ messages in thread
From: Peter Smith @ 2025-10-07 03:12 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: Michael Paquier <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
Hi,
Here are some more minor review comments:
======
doc/src/sgml/bgworker.sgml
1. Typo?
s/damon/daemon/
======
src/backend/postmaster/bgworker.c
2.
+void
+CancelBackgroundWorkers(Oid databaseId, int cancel_flags)
+{
+ int slotno;
+ bool signal_postmaster = false;
+
+ LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
+
+ for (slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
+ {
+ BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
+
+ /* Check worker slot. */
+ if (!slot->in_use)
+ continue;
+
+ /* 1st, check cancel flags. */
+ if ((slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_DROP) & cancel_flags)
+ {
+ PGPROC *proc = BackendPidGetProc(slot->pid);
+
+ if (!proc)
+ continue;
+
+ /* 2nd, compare databaseId. */
+ if (proc->databaseId == databaseId)
+ {
+ /*
+ * Set terminate flag in shared memory, unless slot has
+ * been reused.
+ */
+ slot->terminate = true;
+ signal_postmaster = true;
+ }
+ }
+ }
2a.
Declare slotno as a 'for' loop variable.
~
2b.
There seem to be excessive conditions in the code. Is it better to
restructure with less, like:
for (int slotno = 0; ...)
{
...
if (!slot->in_use)
continue;
if (slot flags are not set to drop)
continue;
proc = BackendPidGetProc(slot->pid);
if (proc && proc->databaseId == databaseId)
{
slot->terminate = true;
signal_postmaster = true;
}
}
======
Kind Regards,
Peter Smith.
Fujitsu Australia
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-07 10:33 Aya Iwata (Fujitsu) <[email protected]>
parent: Peter Smith <[email protected]>
0 siblings, 2 replies; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2025-10-07 10:33 UTC (permalink / raw)
To: 'Peter Smith' <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: Michael Paquier <[email protected]>; pgsql-hackers
Hi,
Thank you for your comments. I updated patch to v0003.
> Do we still need the cancel_flags? I cannot find other reasons to terminate
> workers. Also the things I don't like is that
> BGWORKER_CANCEL_ADMIN_COMMANDS must
> have the same value as BGWORKER_EXIT_AT_DATABASE_DROP. Only one
> flag exists but
> it has 0x0004. Can we remove the argument and flags from the patch?
One reason for adding these flags was that I considered a case where
we might not want to allow all worker terminations during database deletion,
even when the BGWORKER_EXIT_AT_DATABASE_DROP flag is set.
However, This might be a rare case. Therefore, I removed these flags.
> Here are some more minor review comments:
>
> ======
> doc/src/sgml/bgworker.sgml
>
> 1. Typo?
>
> s/damon/daemon/
Thank you. Yes, it is a typo. I fixed this.
> ======
> src/backend/postmaster/bgworker.c
>
> 2.
> +void
> +CancelBackgroundWorkers(Oid databaseId, int cancel_flags)
> +{
> + int slotno;
> + bool signal_postmaster = false;
> +
> + LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
> +
> + for (slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
> + {
> + BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
> +
> + /* Check worker slot. */
> + if (!slot->in_use)
> + continue;
> +
> + /* 1st, check cancel flags. */
> + if ((slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_DROP) &
> cancel_flags)
> + {
> + PGPROC *proc = BackendPidGetProc(slot->pid);
> +
> + if (!proc)
> + continue;
> +
> + /* 2nd, compare databaseId. */
> + if (proc->databaseId == databaseId)
> + {
> + /*
> + * Set terminate flag in shared memory, unless slot has
> + * been reused.
> + */
> + slot->terminate = true;
> + signal_postmaster = true;
> + }
> + }
> + }
>
> 2a.
> Declare slotno as a 'for' loop variable.
Thank you. I fixed this.
> ~
>
> 2b.
> There seem to be excessive conditions in the code. Is it better to
> restructure with less, like:
>
> for (int slotno = 0; ...)
> {
> ...
>
> if (!slot->in_use)
> continue;
>
> if (slot flags are not set to drop)
> continue;
> proc = BackendPidGetProc(slot->pid);
> if (proc && proc->databaseId == databaseId)
> {
> slot->terminate = true;
> signal_postmaster = true;
> }
> }
Thank you. I fixed this, too.
Regards,
Aya Iwata
Fujitsu Limited
Attachments:
[application/octet-stream] v0003-0001-Allow-background-workers-to-be-terminated.patch (9.4K, 2-v0003-0001-Allow-background-workers-to-be-terminated.patch)
download | inline diff:
From d83e2dceda50e0dab1e90bdee14c1d3f5a90db7a Mon Sep 17 00:00:00 2001
From: "iwata.aya" <[email protected]>
Date: Thu, 11 Sep 2025 21:16:51 +0900
Subject: [PATCH v0003] Allow background workers to be terminated at DROP
DATABASE
---
doc/src/sgml/bgworker.sgml | 17 ++++
src/backend/postmaster/bgworker.c | 45 +++++++++++
src/backend/storage/ipc/procarray.c | 6 ++
src/include/postmaster/bgworker.h | 12 ++-
src/test/modules/worker_spi/meson.build | 1 +
.../worker_spi/t/002_worker_terminate.pl | 80 +++++++++++++++++++
src/test/modules/worker_spi/worker_spi.c | 6 +-
7 files changed, 164 insertions(+), 3 deletions(-)
create mode 100644 src/test/modules/worker_spi/t/002_worker_terminate.pl
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index 2c393385a91..53bd0be408e 100644
--- a/doc/src/sgml/bgworker.sgml
+++ b/doc/src/sgml/bgworker.sgml
@@ -108,6 +108,23 @@ typedef struct BackgroundWorker
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>BGWORKER_EXIT_AT_DATABASE_DROP</literal></term>
+ <listitem>
+ <para>
+ <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_DROP</primary></indexterm>
+ Requests to terminate background worker when the database connected by
+ the background worker is changed. DBMS daemon can issue a termination
+ signal to the background worker.
+ This occurs only when significant changes affecting the entire database
+ take place.
+ Specifically, major changes include when the <command>DROP DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>, and
+ <command>ALTER DATABASE SET TABLESPACE</command> commands are executed.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 1ad65c237c3..a8657e9be63 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -26,6 +26,7 @@
#include "storage/lwlock.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
+#include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/shmem.h"
#include "tcop/tcopprot.h"
@@ -1396,3 +1397,47 @@ GetBackgroundWorkerTypeByPid(pid_t pid)
return result;
}
+
+
+/*
+ * Cancel background workers.
+ */
+void
+CancelBackgroundWorkers(Oid databaseId)
+{
+ bool signal_postmaster = false;
+
+ LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
+
+ for (int slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
+ {
+ BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
+
+ /* Check worker slot. */
+ if (!slot->in_use)
+ continue;
+
+ /* 1st, check cancel flags. */
+ if (slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_DROP)
+ {
+ PGPROC *proc = BackendPidGetProc(slot->pid);
+
+ /* 2nd, compare databaseId. */
+ if (proc && proc->databaseId == databaseId)
+ {
+ /*
+ * Set terminate flag in shared memory, unless slot has
+ * been reused.
+ */
+ slot->terminate = true;
+ signal_postmaster = true;
+ }
+ }
+ }
+
+ LWLockRelease(BackgroundWorkerLock);
+
+ /* Make sure the postmaster notices the change to shared memory. */
+ if (signal_postmaster)
+ SendPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE);
+}
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 200f72c6e25..7150f93d05d 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -56,6 +56,7 @@
#include "catalog/pg_authid.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/bgworker.h"
#include "port/pg_lfind.h"
#include "storage/proc.h"
#include "storage/procarray.h"
@@ -3768,6 +3769,11 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
for (index = 0; index < nautovacs; index++)
(void) kill(autovac_pids[index], SIGTERM); /* ignore any error */
+ /*
+ * Cancel background workers by admin commands.
+ */
+ CancelBackgroundWorkers(databaseId);
+
/* sleep, then try again */
pg_usleep(100 * 1000L); /* 100ms */
}
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index 058667a47a0..8354ab040d8 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -59,6 +59,14 @@
*/
#define BGWORKER_BACKEND_DATABASE_CONNECTION 0x0002
+/*
+ * This flag means the bgworker must be exit when the connecting database is
+ * being dropped or moved.
+ * It requires both BGWORKER_SHMEM_ACCESS and
+ * BGWORKER_BACKEND_DATABASE_CONNECTION were passed too.
+ */
+#define BGWORKER_EXIT_AT_DATABASE_DROP 0x0004
+
/*
* This class is used internally for parallel queries, to keep track of the
* number of active parallel workers and make sure we never launch more than
@@ -68,7 +76,6 @@
#define BGWORKER_CLASS_PARALLEL 0x0010
/* add additional bgworker classes here */
-
typedef void (*bgworker_main_type) (Datum main_arg);
/*
@@ -161,4 +168,7 @@ extern void BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, ui
extern void BackgroundWorkerBlockSignals(void);
extern void BackgroundWorkerUnblockSignals(void);
+/* Cancel background workers. */
+extern void CancelBackgroundWorkers(Oid databaseId);
+
#endif /* BGWORKER_H */
diff --git a/src/test/modules/worker_spi/meson.build b/src/test/modules/worker_spi/meson.build
index d673ece48a0..1d30048aec8 100644
--- a/src/test/modules/worker_spi/meson.build
+++ b/src/test/modules/worker_spi/meson.build
@@ -28,6 +28,7 @@ tests += {
'tap': {
'tests': [
't/001_worker_spi.pl',
+ 't/002_worker_terminate.pl'
],
},
}
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
new file mode 100644
index 00000000000..78dcd40ae34
--- /dev/null
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -0,0 +1,80 @@
+# Copyright (c) 2023-2025, PostgreSQL Global Development Group
+
+# Test background workers can be terminated when ALTER/DROP DATABASE happens
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Ensure the worker_spi dynamic worker is launched on the specified database
+sub launch_bgworker
+{
+ my ($node, $database) = @_;
+
+ # Launch a background worker on the given database
+ my $result = $node->safe_psql(
+ $database, qq(
+ SELECT worker_spi_launch(4, oid) IS NOT NULL
+ FROM pg_database WHERE datname = '$database';
+ ));
+ is($result, 't', "dynamic bgworker launched");
+
+ # Check the worker is running correctly
+ $result = $node->safe_psql('postgres',
+ "SELECT count(1) FROM pg_stat_activity WHERE datname = '$database' AND backend_type = 'worker_spi dynamic';"
+ );
+ is($result, 1, 'dynamic bgworker has exist');
+}
+
+# Run the given query and verify the background worker can be terminated
+sub run_db_command
+{
+ my ($node, $command, $testname) = @_;
+ my $offset = -s $node->logfile;
+
+ $node->safe_psql('postgres', $command);
+ ok( $node->log_contains(
+ "terminating background worker \"worker_spi dynamic\" due to administrator command",
+ $offset),
+ "background worker can be terminated at $testname");
+}
+
+my $node = PostgreSQL::Test::Cluster->new('mynode');
+$node->init;
+$node->start;
+
+# Create another database. "postgres" is still used to run db commands and
+# checks the pg_stat_activity.
+$node->safe_psql('postgres', 'CREATE DATABASE testdb');
+$node->safe_psql('testdb', 'CREATE EXTENSION worker_spi;');
+
+launch_bgworker($node, 'testdb');
+
+# Testcase 1: ALTER DATABASE SET TABLESPACE
+run_db_command(
+ $node,
+ "ALTER DATABASE testdb RENAME TO renameddb",
+ "ALTER DATABASE SET TABLESPACE");
+
+# Preparation for the next test; create another tablespace
+my $basedir = $node->basedir();
+my $tablespace = "$basedir/tablespace";
+mkdir($tablespace);
+$node->safe_psql('postgres',
+ "CREATE TABLESPACE test_tablespace LOCATION '$tablespace'");
+launch_bgworker($node, 'renameddb');
+
+# Testcase 2: ALTER DATABASE SET TABLESPACE
+run_db_command(
+ $node,
+ "ALTER DATABASE renameddb SET TABLESPACE test_tablespace",
+ "ALTER DATABASE SET TABLESPACE");
+
+launch_bgworker($node, 'renameddb');
+
+# Testcase 2: DROP DATABASE
+run_db_command($node, "DROP DATABASE renameddb", "DROP DATABASE");
+
+done_testing();
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index bea8339f464..dea9fd8281a 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -361,7 +361,8 @@ _PG_init(void)
/* set up common data for all our workers */
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
- BGWORKER_BACKEND_DATABASE_CONNECTION;
+ BGWORKER_BACKEND_DATABASE_CONNECTION |
+ BGWORKER_EXIT_AT_DATABASE_DROP;
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART;
sprintf(worker.bgw_library_name, "worker_spi");
@@ -407,7 +408,8 @@ worker_spi_launch(PG_FUNCTION_ARGS)
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
- BGWORKER_BACKEND_DATABASE_CONNECTION;
+ BGWORKER_BACKEND_DATABASE_CONNECTION |
+ BGWORKER_EXIT_AT_DATABASE_DROP;
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART;
sprintf(worker.bgw_library_name, "worker_spi");
--
2.39.3
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-07 11:49 Hayato Kuroda (Fujitsu) <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
1 sibling, 0 replies; 67+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2025-10-07 11:49 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; 'Peter Smith' <[email protected]>; +Cc: Michael Paquier <[email protected]>; pgsql-hackers
Dear Iwata-san,
Thanks for updating the patch. Comments:
```
+ /* Check worker slot. */
+ if (!slot->in_use)
+ continue;
```
The comment has less meaning. How about:
"Skip if the slot is not used"
```
+ /* 1st, check cancel flags. */
+ if (slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_DROP)
```
Missing update 2b [1]. Also, since cancel flag does not exist anymore, the comment should be
Updated. How about something like:
"Skip if the background worker does not want to exit"
```
+ /* 2nd, compare databaseId. */
+ if (proc && proc->databaseId == databaseId)
```
Here should describes what are you trying to do. How about something like:
Checks the connecting database of the worker, and instruct the postmaster to terminate it if needed
```
+ /*
+ * Cancel background workers by admin commands.
+ */
+ CancelBackgroundWorkers(databaseId);
```
Since we removed the flag, the comment is outdated.
```
-
typedef void (*bgworker_main_type) (Datum main_arg);
```
This change is not related with this patch.
```
@@ -361,7 +361,8 @@ _PG_init(void)
/* set up common data for all our workers */
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
- BGWORKER_BACKEND_DATABASE_CONNECTION;
+ BGWORKER_BACKEND_DATABASE_CONNECTION |
+ BGWORKER_EXIT_AT_DATABASE_DROP;
```
The new flag was added to both static and dynamic background workers. So, how about
testing both? I think it is enough to use one of case, like ALTER DATABASE SET TABLESPACE.
[1]: https://www.postgresql.org/message-id/CAHut%2BPt4Tn1bQYCsYeUt_gtcSB-KOTtRB70SLghkpsjfKGsm7w%40mail.g...
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-07 21:39 Peter Smith <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
1 sibling, 1 reply; 67+ messages in thread
From: Peter Smith @ 2025-10-07 21:39 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: Hayato Kuroda (Fujitsu) <[email protected]>; Michael Paquier <[email protected]>; pgsql-hackers
HI Iwata-San,
Here are some more review comments for v3.
======
doc/src/sgml/bgworker.sgml
1.
+ <para>
+ <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_DROP</primary></indexterm>
+ Requests to terminate background worker when the database connected by
+ the background worker is changed. DBMS daemon can issue a termination
+ signal to the background worker.
+ This occurs only when significant changes affecting the entire database
+ take place.
+ Specifically, major changes include when the <command>DROP
DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>, and
+ <command>ALTER DATABASE SET TABLESPACE</command> commands are executed.
+ </para>
Here is a reworded version of that for your consideration
(AI-generated -- pls verify for correctness!):
<para>
<indexterm><primary>BGWORKER_EXIT_AT_DATABASE_DROP</primary></indexterm>
Requests termination of the background worker when the database it is
connected to undergoes significant changes. The postmaster will send a
termination signal to the background worker when any of the following
commands are executed: <command>DROP DATABASE</command>,
<command>ALTER DATABASE RENAME TO</command>, or
<command>ALTER DATABASE SET TABLESPACE</command>.
</para>
======
2.
+ for (int slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
+ {
+ BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
+
+ /* Check worker slot. */
+ if (!slot->in_use)
+ continue;
+
+ /* 1st, check cancel flags. */
+ if (slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_DROP)
+ {
+ PGPROC *proc = BackendPidGetProc(slot->pid);
+
+ /* 2nd, compare databaseId. */
+ if (proc && proc->databaseId == databaseId)
+ {
+ /*
+ * Set terminate flag in shared memory, unless slot has
+ * been reused.
+ */
+ slot->terminate = true;
+ signal_postmaster = true;
+ }
+ }
+ }
IMO, most of those comments do not have any benefit because they only
repeat what is already obvious from the code.
2a.
+ /* Check worker slot. */
+ if (!slot->in_use)
Remove that one. It is the same as the code.
~
2b.
+ /* 1st, check cancel flags. */
+ if (slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_DROP)
Remove that one. It is the same as the code
~
2c.
+ /* 2nd, compare databaseId. */
+ if (proc && proc->databaseId == databaseId)
Remove that one. It is the same as the code.
~
2d.
+ /*
+ * Set terminate flag in shared memory, unless slot has
+ * been reused.
+ */
This comment is a bit strange -- It seems slightly misplaced. IIUC,
the "unless slot has been reused" really is referring to the earlier
"slot->in_use". This whole comment may be better put immediately above
the 'for' loop as a short summary of the whole logic.
======
src/include/postmaster/bgworker.h
3.
+/*
+ * This flag means the bgworker must be exit when the connecting database is
+ * being dropped or moved.
+ * It requires both BGWORKER_SHMEM_ACCESS and
+ * BGWORKER_BACKEND_DATABASE_CONNECTION were passed too.
+ */
Not English. Needs rewording. Consider something like this:
/*
* Exit the bgworker when its database is dropped, renamed, or moved.
* Requires BGWORKER_SHMEM_ACCESS and BGWORKER_BACKEND_DATABASE_CONNECTION.
*/
======
Kind Regards,
Peter Smith.
Fujitsu Australia
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-08 12:26 Aya Iwata (Fujitsu) <[email protected]>
parent: Peter Smith <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2025-10-08 12:26 UTC (permalink / raw)
To: 'Peter Smith' <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: Michael Paquier <[email protected]>; pgsql-hackers
Hi Kuroda-san, Peter-san,
Thank you for your review comments! I updated patch to v0004.
> -----Original Message-----
> From: Kuroda, Hayato/黒田 隼人 <[email protected]>
> Sent: Tuesday, October 7, 2025 8:49 PM
> ```
> + /* 2nd, compare databaseId. */
> + if (proc && proc->databaseId == databaseId)
> ```
>
> Here should describes what are you trying to do. How about something like:
> Checks the connecting database of the worker, and instruct the postmaster to
> terminate it if needed
This comment has been removed. We can know code's intent without comments.
> ```
> + /*
> + * Cancel background workers by admin commands.
> + */
> + CancelBackgroundWorkers(databaseId);
> ```
>
> Since we removed the flag, the comment is outdated.
Thank you. I changed this comment:
"if set the bgw_flags, cancel background workers."
> ```
> -
> typedef void (*bgworker_main_type) (Datum main_arg);
> ```
>
> This change is not related with this patch.
I removed this change.
> ```
> @@ -361,7 +361,8 @@ _PG_init(void)
> /* set up common data for all our workers */
> memset(&worker, 0, sizeof(worker));
> worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
> - BGWORKER_BACKEND_DATABASE_CONNECTION;
> + BGWORKER_BACKEND_DATABASE_CONNECTION |
> + BGWORKER_EXIT_AT_DATABASE_DROP;
> ```
>
> The new flag was added to both static and dynamic background workers. So,
> how about
> testing both? I think it is enough to use one of case, like ALTER DATABASE SET
> TABLESPACE.
I removed this BGWORKER_EXIT_AT_DATABASE_DROP from the patch.
> -----Original Message-----
> From: Peter Smith <[email protected]>
> ======
> doc/src/sgml/bgworker.sgml
>
> 1.
> Here is a reworded version of that for your consideration
> (AI-generated -- pls verify for correctness!):
>
> <para>
>
> <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_DROP</primary>
> </indexterm>
> Requests termination of the background worker when the database it is
> connected to undergoes significant changes. The postmaster will send a
> termination signal to the background worker when any of the following
> commands are executed: <command>DROP DATABASE</command>,
> <command>ALTER DATABASE RENAME TO</command>, or
> <command>ALTER DATABASE SET TABLESPACE</command>.
> </para>
Thank you. I checked and replaced the doc.
> ======
>
>
> 2.
> IMO, most of those comments do not have any benefit because they only
> repeat what is already obvious from the code.
>
> 2a.
> + /* Check worker slot. */
> + if (!slot->in_use)
> Remove that one. It is the same as the code.
I removed this comment.
> 2b.
> + /* 1st, check cancel flags. */
> + if (slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_DROP)
> Remove that one. It is the same as the code
I removed this comment, too.
> 2c.
> + /* 2nd, compare databaseId. */
> + if (proc && proc->databaseId == databaseId)
> Remove that one. It is the same as the code.
I removed this comment, too.
> 2d.
> + /*
> + * Set terminate flag in shared memory, unless slot has
> + * been reused.
> + */
>
> This comment is a bit strange -- It seems slightly misplaced. IIUC,
> the "unless slot has been reused" really is referring to the earlier
> "slot->in_use". This whole comment may be better put immediately above
> the 'for' loop as a short summary of the whole logic.
I put this comment to before the for 'loop'.
And I changed comment like this:
/*
* Set terminate flag in shared memory, unless slot has
* been used.
*/
> ======
> src/include/postmaster/bgworker.h
>
> 3.
> +/*
> + * This flag means the bgworker must be exit when the connecting database
> is
> + * being dropped or moved.
> + * It requires both BGWORKER_SHMEM_ACCESS and
> + * BGWORKER_BACKEND_DATABASE_CONNECTION were passed too.
> + */
>
> Not English. Needs rewording. Consider something like this:
>
> /*
> * Exit the bgworker when its database is dropped, renamed, or moved.
> * Requires BGWORKER_SHMEM_ACCESS and
> BGWORKER_BACKEND_DATABASE_CONNECTION.
> */
Thank you. I changed the source code comments based on this comment.
Regards,
Aya Iwata
Fujitsu Limited
Attachments:
[application/octet-stream] v0004-0001-Allow-background-workers-to-be-terminated.patch (8.5K, 2-v0004-0001-Allow-background-workers-to-be-terminated.patch)
download | inline diff:
From 3c8ffb8eebccdb0516ed8656b540524fa33b6efb Mon Sep 17 00:00:00 2001
From: "iwata.aya" <[email protected]>
Date: Thu, 11 Sep 2025 21:16:51 +0900
Subject: [PATCH v0004] Allow background workers to be terminated at DROP
DATABASE
---
doc/src/sgml/bgworker.sgml | 15 ++++
src/backend/postmaster/bgworker.c | 43 ++++++++++
src/backend/storage/ipc/procarray.c | 6 ++
src/include/postmaster/bgworker.h | 9 +++
src/test/modules/worker_spi/meson.build | 1 +
.../worker_spi/t/002_worker_terminate.pl | 80 +++++++++++++++++++
src/test/modules/worker_spi/worker_spi.c | 3 +-
7 files changed, 156 insertions(+), 1 deletion(-)
create mode 100644 src/test/modules/worker_spi/t/002_worker_terminate.pl
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index 2c393385a91..9882e779baa 100644
--- a/doc/src/sgml/bgworker.sgml
+++ b/doc/src/sgml/bgworker.sgml
@@ -108,6 +108,21 @@ typedef struct BackgroundWorker
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>BGWORKER_EXIT_AT_DATABASE_DROP</literal></term>
+ <listitem>
+ <para>
+ <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_DROP</primary></indexterm>
+ Requests termination of the background worker when the database it is
+ connected to undergoes significant changes. The postmaster will send a
+ termination signal to the background worker when any of the following
+ commands are executed: <command>DROP DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>, or
+ <command>ALTER DATABASE SET TABLESPACE</command>.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 1ad65c237c3..186a3b39629 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -26,6 +26,7 @@
#include "storage/lwlock.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
+#include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/shmem.h"
#include "tcop/tcopprot.h"
@@ -1396,3 +1397,45 @@ GetBackgroundWorkerTypeByPid(pid_t pid)
return result;
}
+
+
+/*
+ * Cancel background workers.
+ */
+void
+CancelBackgroundWorkers(Oid databaseId)
+{
+ bool signal_postmaster = false;
+
+ LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
+
+ /*
+ * Set terminate flag in shared memory, unless slot has
+ * been used.
+ */
+ for (int slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
+ {
+ PGPROC *proc;
+ BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
+
+ if (!slot->in_use)
+ continue;
+
+ if (!(slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_DROP))
+ continue;
+
+ proc = BackendPidGetProc(slot->pid);
+
+ if (proc && proc->databaseId == databaseId)
+ {
+ slot->terminate = true;
+ signal_postmaster = true;
+ }
+ }
+
+ LWLockRelease(BackgroundWorkerLock);
+
+ /* Make sure the postmaster notices the change to shared memory. */
+ if (signal_postmaster)
+ SendPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE);
+}
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 200f72c6e25..15641c89ce5 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -56,6 +56,7 @@
#include "catalog/pg_authid.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/bgworker.h"
#include "port/pg_lfind.h"
#include "storage/proc.h"
#include "storage/procarray.h"
@@ -3768,6 +3769,11 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
for (index = 0; index < nautovacs; index++)
(void) kill(autovac_pids[index], SIGTERM); /* ignore any error */
+ /*
+ * if set the bgw_flags, cancel background workers.
+ */
+ CancelBackgroundWorkers(databaseId);
+
/* sleep, then try again */
pg_usleep(100 * 1000L); /* 100ms */
}
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index 058667a47a0..dadb5b02ce7 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -59,6 +59,12 @@
*/
#define BGWORKER_BACKEND_DATABASE_CONNECTION 0x0002
+/*
+ * Exit the bgworker when its database is dropped, renamed, or moved.
+ * Requires BGWORKER_SHMEM_ACCESS and BGWORKER_BACKEND_DATABASE_CONNECTION.
+ */
+#define BGWORKER_EXIT_AT_DATABASE_DROP 0x0004
+
/*
* This class is used internally for parallel queries, to keep track of the
* number of active parallel workers and make sure we never launch more than
@@ -161,4 +167,7 @@ extern void BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, ui
extern void BackgroundWorkerBlockSignals(void);
extern void BackgroundWorkerUnblockSignals(void);
+/* Cancel background workers. */
+extern void CancelBackgroundWorkers(Oid databaseId);
+
#endif /* BGWORKER_H */
diff --git a/src/test/modules/worker_spi/meson.build b/src/test/modules/worker_spi/meson.build
index d673ece48a0..1d30048aec8 100644
--- a/src/test/modules/worker_spi/meson.build
+++ b/src/test/modules/worker_spi/meson.build
@@ -28,6 +28,7 @@ tests += {
'tap': {
'tests': [
't/001_worker_spi.pl',
+ 't/002_worker_terminate.pl'
],
},
}
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
new file mode 100644
index 00000000000..78dcd40ae34
--- /dev/null
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -0,0 +1,80 @@
+# Copyright (c) 2023-2025, PostgreSQL Global Development Group
+
+# Test background workers can be terminated when ALTER/DROP DATABASE happens
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Ensure the worker_spi dynamic worker is launched on the specified database
+sub launch_bgworker
+{
+ my ($node, $database) = @_;
+
+ # Launch a background worker on the given database
+ my $result = $node->safe_psql(
+ $database, qq(
+ SELECT worker_spi_launch(4, oid) IS NOT NULL
+ FROM pg_database WHERE datname = '$database';
+ ));
+ is($result, 't', "dynamic bgworker launched");
+
+ # Check the worker is running correctly
+ $result = $node->safe_psql('postgres',
+ "SELECT count(1) FROM pg_stat_activity WHERE datname = '$database' AND backend_type = 'worker_spi dynamic';"
+ );
+ is($result, 1, 'dynamic bgworker has exist');
+}
+
+# Run the given query and verify the background worker can be terminated
+sub run_db_command
+{
+ my ($node, $command, $testname) = @_;
+ my $offset = -s $node->logfile;
+
+ $node->safe_psql('postgres', $command);
+ ok( $node->log_contains(
+ "terminating background worker \"worker_spi dynamic\" due to administrator command",
+ $offset),
+ "background worker can be terminated at $testname");
+}
+
+my $node = PostgreSQL::Test::Cluster->new('mynode');
+$node->init;
+$node->start;
+
+# Create another database. "postgres" is still used to run db commands and
+# checks the pg_stat_activity.
+$node->safe_psql('postgres', 'CREATE DATABASE testdb');
+$node->safe_psql('testdb', 'CREATE EXTENSION worker_spi;');
+
+launch_bgworker($node, 'testdb');
+
+# Testcase 1: ALTER DATABASE SET TABLESPACE
+run_db_command(
+ $node,
+ "ALTER DATABASE testdb RENAME TO renameddb",
+ "ALTER DATABASE SET TABLESPACE");
+
+# Preparation for the next test; create another tablespace
+my $basedir = $node->basedir();
+my $tablespace = "$basedir/tablespace";
+mkdir($tablespace);
+$node->safe_psql('postgres',
+ "CREATE TABLESPACE test_tablespace LOCATION '$tablespace'");
+launch_bgworker($node, 'renameddb');
+
+# Testcase 2: ALTER DATABASE SET TABLESPACE
+run_db_command(
+ $node,
+ "ALTER DATABASE renameddb SET TABLESPACE test_tablespace",
+ "ALTER DATABASE SET TABLESPACE");
+
+launch_bgworker($node, 'renameddb');
+
+# Testcase 2: DROP DATABASE
+run_db_command($node, "DROP DATABASE renameddb", "DROP DATABASE");
+
+done_testing();
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index bea8339f464..d6174c715e5 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -407,7 +407,8 @@ worker_spi_launch(PG_FUNCTION_ARGS)
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
- BGWORKER_BACKEND_DATABASE_CONNECTION;
+ BGWORKER_BACKEND_DATABASE_CONNECTION |
+ BGWORKER_EXIT_AT_DATABASE_DROP;
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART;
sprintf(worker.bgw_library_name, "worker_spi");
--
2.39.3
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-08 22:18 Peter Smith <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Peter Smith @ 2025-10-08 22:18 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: Hayato Kuroda (Fujitsu) <[email protected]>; Michael Paquier <[email protected]>; pgsql-hackers
Hi Iwata-San,
Some v4 comments.
======
src/backend/postmaster/bgworker.c
1.
+ /*
+ * Set terminate flag in shared memory, unless slot has
+ * been used.
+ */
+ for (int slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
+ {
+ PGPROC *proc;
+ BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
+
+ if (!slot->in_use)
+ continue;
+
+ if (!(slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_DROP))
+ continue;
+
+ proc = BackendPidGetProc(slot->pid);
+
+ if (proc && proc->databaseId == databaseId)
+ {
+ slot->terminate = true;
+ signal_postmaster = true;
+ }
+ }
1a.
It's not clear to me what you were trying to convey by saying "unless
slot has been used" in the comment. Maybe you meant "unless slot is
not in use", but is that useful even to say? Anyway, the comment as-is
seems incorrect.
~
1b.
Sorry for wavering on this, but now that I see the resulting v4 code,
I feel we don't really need any of those 'continues', and more if
conditions can be combined. It becomes simpler. See if you agree.
SUGGESTION:
for (int slotno ...)
{
if (slot->in_use && (slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_DROP))
{
PGPROC *proc = BackendPidGetProc(slot->pid);
if (proc && proc->databaseId == databaseId)
{
slot->terminate = true;
signal_postmaster = true;
}
}
}
======
src/backend/storage/ipc/procarray.c
2.
+ /*
+ * if set the bgw_flags, cancel background workers.
+ */
+ CancelBackgroundWorkers(databaseId);
+
I was wondering about this function name "CancelXXX" -- do you
"cancel" a worker, or do you "terminate" it?
Isn't it better to name this new function more like the
existing/similar TerminateBackgroundWorker() function?
E.g. consider the following:
/*
* Terminate all background workers for this database, if
* they had requested it (BGWORKER_EXIT_AT_DATABASE_DROP).
*/
TerminateBackgroundWorkersForDB(databaseId);
======
Kind Regards,
Peter Smith.
Fujitsu Australia
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-09 02:05 Hayato Kuroda (Fujitsu) <[email protected]>
parent: Peter Smith <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2025-10-09 02:05 UTC (permalink / raw)
To: 'Peter Smith' <[email protected]>; Aya Iwata (Fujitsu) <[email protected]>; +Cc: Michael Paquier <[email protected]>; pgsql-hackers
Dear Peter, Iwata-san,
> 1a.
> It's not clear to me what you were trying to convey by saying "unless
> slot has been used" in the comment. Maybe you meant "unless slot is
> not in use", but is that useful even to say? Anyway, the comment as-is
> seems incorrect.
Agreed to update the comment. How about:
Iterate through slots, looking for workers who connects to the given database.
> 1b.
> Sorry for wavering on this, but now that I see the resulting v4 code,
> I feel we don't really need any of those 'continues', and more if
> conditions can be combined. It becomes simpler. See if you agree.
Ether way is fine for me.
> /*
> * Terminate all background workers for this database, if
> * they had requested it (BGWORKER_EXIT_AT_DATABASE_DROP).
> */
> TerminateBackgroundWorkersForDB(databaseId);
The code comment looks OK. Regarding the function name, I want to propose
an alternative - TerminateBackgroundWorkersByOid().
Core codes have already had several xxxByOid() functions.
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-09 02:28 Hayato Kuroda (Fujitsu) <[email protected]>
parent: Hayato Kuroda (Fujitsu) <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2025-10-09 02:28 UTC (permalink / raw)
To: 'Peter Smith' <[email protected]>; Aya Iwata (Fujitsu) <[email protected]>; +Cc: Michael Paquier <[email protected]>; pgsql-hackers
Dear Iwata-san,
Sorry for posting many times. I noticed that CountOtherDBBackends() can be called
while creating the database. Should we also mention and test the case?
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-09 03:33 Michael Paquier <[email protected]>
parent: Hayato Kuroda (Fujitsu) <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Michael Paquier @ 2025-10-09 03:33 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: 'Peter Smith' <[email protected]>; Aya Iwata (Fujitsu) <[email protected]>; pgsql-hackers
On Thu, Oct 09, 2025 at 02:28:56AM +0000, Hayato Kuroda (Fujitsu) wrote:
> Sorry for posting many times. I noticed that CountOtherDBBackends() can be called
> while creating the database. Should we also mention and test the case?
How would you test that? A bgworker would not be able to connect to
the database that's being created.
The implementation done in v4 is a nice simplification compared to the
original proposal, nice. I've gone through the patch and here are
some comments.
+my $basedir = $node->basedir();
+my $tablespace = "$basedir/tablespace";
We could use a temporary folder for the tablespaces. I've always
prefered this practice. That's a bit, feel free to ignore this one,
what you are doing is not wrong, either.
+ <term><literal>BGWORKER_EXIT_AT_DATABASE_DROP</literal></term>
Perhaps BGWORKER_EXIT_AT_DATABASE_CHANGE? DROP is incorrect, as the
database could be renamed or moved, as well.
+ * Exit the bgworker when its database is dropped, renamed, or moved.
+ * Requires BGWORKER_SHMEM_ACCESS and BGWORKER_BACKEND_DATABASE_CONNECTION.
+ */
+#define BGWORKER_EXIT_AT_DATABASE_DROP 0x0004
We could enforce this rule with an elog(ERROR) or an assert, perhaps?
@@ -407,7 +407,8 @@ worker_spi_launch(PG_FUNCTION_ARGS)
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
- BGWORKER_BACKEND_DATABASE_CONNECTION;
+ BGWORKER_BACKEND_DATABASE_CONNECTION |
+ BGWORKER_EXIT_AT_DATABASE_DROP;
I would suggest to make this part optional, with an argument that
uses a default value to false (as in "do-not-set the flag") that can
be given by the callers of worker_spi_launch(). Let's also add one
bgworker that's used in your series of tests, and check that it is
*not* cancelled when the flag is not set.
+# Ensure the worker_spi dynamic worker is launched on the specified database
+sub launch_bgworker
+{
+ my ($node, $database) = @_;
+
+ # Launch a background worker on the given database
+ my $result = $node->safe_psql(
+ $database, qq(
+ SELECT worker_spi_launch(4, oid) IS NOT NULL
I'd recommend to make the worker number an argument of this function,
and also do things so as the log_contains() call is able to check that
the worker with the matching number is loged, rather than rely on
"worker_spi dynamic" for all the comparisons. This is relevant to be
able to mix multiple workers at the same time in the tests.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, 2-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-09 04:00 Hayato Kuroda (Fujitsu) <[email protected]>
parent: Michael Paquier <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2025-10-09 04:00 UTC (permalink / raw)
To: 'Michael Paquier' <[email protected]>; +Cc: 'Peter Smith' <[email protected]>; Aya Iwata (Fujitsu) <[email protected]>; pgsql-hackers
Dear Michael,
> > Sorry for posting many times. I noticed that CountOtherDBBackends() can be
> called
> > while creating the database. Should we also mention and test the case?
>
> How would you test that? A bgworker would not be able to connect to
> the database that's being created.
>
Sorry for missing words. Per my understanding, CountOtherDBBackends() in createdb()
ensures that there are no active connections of the source database. If there is
a connection to a database, we cannot create another database with TEMPALATE clause:
```
postgres=# CREATE DATABASE new TEMPLATE postgres ;
ERROR: source database "postgres" is being accessed by other users
DETAIL: There is 1 other session using the database.
```
Based on that, I imagined that we could launch a bgworker and create another database
by using template. Or is it already handled?
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-09 04:37 Michael Paquier <[email protected]>
parent: Hayato Kuroda (Fujitsu) <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Michael Paquier @ 2025-10-09 04:37 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: 'Peter Smith' <[email protected]>; Aya Iwata (Fujitsu) <[email protected]>; pgsql-hackers
On Thu, Oct 09, 2025 at 04:00:24AM +0000, Hayato Kuroda (Fujitsu) wrote:
> Based on that, I imagined that we could launch a bgworker and create
> another database by using template. Or is it already handled?
Oh, I was not following you here. Yes, we should have something with
a bgworker connected to a source and make sure that it disconnects
when create a new database with this source as template.
However, could there be more to consider here? Contrary to DROP
DATABASE, where we require the drop to be done by the owner of the
database (or a superuser), CREATE DATABASE has less requirements: it
is fine for a role to create a database if they have the CREATEDB
rights. If we allow bgworkers to be cancelled when the database they
are connected to is used as a source, that may be disruptive, so we
had better document precisely the behavior of the flag and what users
should expect from it when set. We are already killing autovacuum
workers when they are connected to a database used as a source anyway
(since 4abd7b49f1e9 from 2008), so I am worrying for nothing and it is
actually better to apply the same rule across the board. Let's just
document very clearly how the bgworker will react when the cancel flag
is set in all these cases.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, 2-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-09 05:47 Hayato Kuroda (Fujitsu) <[email protected]>
parent: Michael Paquier <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2025-10-09 05:47 UTC (permalink / raw)
To: 'Michael Paquier' <[email protected]>; +Cc: 'Peter Smith' <[email protected]>; Aya Iwata (Fujitsu) <[email protected]>; pgsql-hackers
Dear Michael,
> However, could there be more to consider here? Contrary to DROP
> DATABASE, where we require the drop to be done by the owner of the
> database (or a superuser), CREATE DATABASE has less requirements: it
> is fine for a role to create a database if they have the CREATEDB
> rights. If we allow bgworkers to be cancelled when the database they
> are connected to is used as a source, that may be disruptive, so we
> had better document precisely the behavior of the flag and what users
> should expect from it when set.
Actually, if the database is not marked as the template one, the user must be
owner of the source or superuser. Not sure there is a real case that template
database has dedicated workers, but anyway I do agree to note down this behavior.
It is surprising that creating other databases lead the process terminations.
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-09 13:09 Aya Iwata (Fujitsu) <[email protected]>
parent: Hayato Kuroda (Fujitsu) <[email protected]>
0 siblings, 2 replies; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2025-10-09 13:09 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; 'Michael Paquier' <[email protected]>; 'Peter Smith' <[email protected]>; +Cc: pgsql-hackers
Hi,
I updated the patch to v0005.
> -----Original Message-----
> From: Peter Smith <[email protected]>
> Sent: Thursday, October 9, 2025 7:18 AM
> src/backend/postmaster/bgworker.c
>
> 1.
> 1a.
> It's not clear to me what you were trying to convey by saying "unless
> slot has been used" in the comment. Maybe you meant "unless slot is
> not in use", but is that useful even to say? Anyway, the comment as-is
> seems incorrect.
I changed this comment. I use Kuroda-san's comment. Thank you. )
"Iterate through slots, looking for workers who connects to the given database."
> 1b.
> Sorry for wavering on this, but now that I see the resulting v4 code,
> I feel we don't really need any of those 'continues', and more if
> conditions can be combined. It becomes simpler. See if you agree.
I changed if condition code.
> src/backend/storage/ipc/procarray.c
>
> 2.
> I was wondering about this function name "CancelXXX" -- do you
> "cancel" a worker, or do you "terminate" it?
>
> Isn't it better to name this new function more like the
> existing/similar TerminateBackgroundWorker() function?
>
> E.g. consider the following:
>
> /*
> * Terminate all background workers for this database, if
> * they had requested it (BGWORKER_EXIT_AT_DATABASE_DROP).
> */
> TerminateBackgroundWorkersForDB(databaseId);
I changed this name to "TerminateBackgroudWorkerByOid"
> -----Original Message-----
> From: Michael Paquier <[email protected]>
> Sent: Thursday, October 9, 2025 12:34 PM
> > Sorry for posting many times. I noticed that CountOtherDBBackends() can be
> called
> > while creating the database. Should we also mention and test the case?
>
> How would you test that? A bgworker would not be able to connect to
> the database that's being created.
> +my $basedir = $node->basedir();
> +my $tablespace = "$basedir/tablespace";
>
> We could use a temporary folder for the tablespaces. I've always
> prefered this practice. That's a bit, feel free to ignore this one,
> what you are doing is not wrong, either.
I use tempdir to create directory for the tablespace.
> +
> <term><literal>BGWORKER_EXIT_AT_DATABASE_DROP</literal></term>
>
> Perhaps BGWORKER_EXIT_AT_DATABASE_CHANGE? DROP is incorrect, as
> the
> database could be renamed or moved, as well.
>
> + * Exit the bgworker when its database is dropped, renamed, or moved.
> + * Requires BGWORKER_SHMEM_ACCESS and
> BGWORKER_BACKEND_DATABASE_CONNECTION.
> + */
> +#define BGWORKER_EXIT_AT_DATABASE_DROP
> 0x0004
>
> We could enforce this rule with an elog(ERROR) or an assert, perhaps?
I started think it does not a strict condition.
If the worker does not connect to databases,
the BGWORKER_EXIT_AT_DATABASE_DROP(CHANGE) flag will be never checked.
Therefore, I have changed this comment as follows:
" No-op if BGWORKER_BACKEND_DATABASE_CONNECTION are not specified."
> @@ -407,7 +407,8 @@ worker_spi_launch(PG_FUNCTION_ARGS)
> memset(&worker, 0, sizeof(worker));
> worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
> - BGWORKER_BACKEND_DATABASE_CONNECTION;
> + BGWORKER_BACKEND_DATABASE_CONNECTION |
> + BGWORKER_EXIT_AT_DATABASE_DROP;
>
> I would suggest to make this part optional, with an argument that
> uses a default value to false (as in "do-not-set the flag") that can
> be given by the callers of worker_spi_launch(). Let's also add one
> bgworker that's used in your series of tests, and check that it is
> *not* cancelled when the flag is not set.
I fixed this to added allow_termination flag and *not* canceled test.
However this test takes at least 5 sec. because of "for loop" in CountOtherDBBackends().
> +# Ensure the worker_spi dynamic worker is launched on the specified
> database
> +sub launch_bgworker
> +{
> + my ($node, $database) = @_;
> +
> + # Launch a background worker on the given database
> + my $result = $node->safe_psql(
> + $database, qq(
> + SELECT worker_spi_launch(4, oid) IS NOT NULL
>
> I'd recommend to make the worker number an argument of this function,
> and also do things so as the log_contains() call is able to check that
> the worker with the matching number is loged, rather than rely on
> "worker_spi dynamic" for all the comparisons. This is relevant to be
> able to mix multiple workers at the same time in the tests.
I fixed test code. Thank you for your advice.
It is better to used wait_for_log because it takes time for the log to output.
And we added test for "CREATE DATABASE TEMPLATE " command too.
Regards,
Aya Iwata
Fujitsu Limited
Attachments:
[application/octet-stream] v0005-0001-Allow-background-workers-to-be-terminated.patch (10.5K, 2-v0005-0001-Allow-background-workers-to-be-terminated.patch)
download | inline diff:
From 52040754e974fe2adad7966b1ecca3f44957556b Mon Sep 17 00:00:00 2001
From: "iwata.aya" <[email protected]>
Date: Thu, 11 Sep 2025 21:16:51 +0900
Subject: [PATCH v0005] Allow background workers to be terminated at DROP
DATABASE
---
doc/src/sgml/bgworker.sgml | 20 ++++
src/backend/postmaster/bgworker.c | 39 +++++++
src/backend/storage/ipc/procarray.c | 7 ++
src/include/postmaster/bgworker.h | 9 ++
src/test/modules/worker_spi/meson.build | 1 +
.../worker_spi/t/002_worker_terminate.pl | 106 ++++++++++++++++++
.../modules/worker_spi/worker_spi--1.0.sql | 3 +-
src/test/modules/worker_spi/worker_spi.c | 5 +
8 files changed, 189 insertions(+), 1 deletion(-)
create mode 100644 src/test/modules/worker_spi/t/002_worker_terminate.pl
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index 2c393385a91..01ceb3864e7 100644
--- a/doc/src/sgml/bgworker.sgml
+++ b/doc/src/sgml/bgworker.sgml
@@ -108,6 +108,26 @@ typedef struct BackgroundWorker
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>BGWORKER_EXIT_AT_DATABASE_CHANGE</literal></term>
+ <listitem>
+ <para>
+ <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primary></indexterm>
+ Requests termination of the background worker when the database it is
+ connected to undergoes significant changes. The postmaster will send a
+ termination signal to the background worker when any of the following
+ commands are executed: <command>DROP DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>, or
+ <command>ALTER DATABASE SET TABLESPACE</command>.
+ When <command>CREATE DATABASE TEMPLATE</command> command is executed,
+ background workers which connected to target template database are terminated.
+ If <literal>BGWORKER_SHMEM_ACCESS</literal> and
+ <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal> are not using,
+ nothing happens.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 1ad65c237c3..7ab48c245ea 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -26,6 +26,7 @@
#include "storage/lwlock.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
+#include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/shmem.h"
#include "tcop/tcopprot.h"
@@ -1396,3 +1397,41 @@ GetBackgroundWorkerTypeByPid(pid_t pid)
return result;
}
+
+
+/*
+ * Cancel background workers.
+ */
+void
+TerminateBackgroundWorkersByOid(Oid databaseId)
+{
+ bool signal_postmaster = false;
+
+ LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
+
+ /*
+ * Iterate through slots, looking for workers
+ * who connects to the given database.
+ */
+ for (int slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
+ {
+ BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
+
+ if (slot->in_use && (slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_CHANGE))
+ {
+ PGPROC *proc = BackendPidGetProc(slot->pid);
+
+ if (proc && proc->databaseId == databaseId)
+ {
+ slot->terminate = true;
+ signal_postmaster = true;
+ }
+ }
+ }
+
+ LWLockRelease(BackgroundWorkerLock);
+
+ /* Make sure the postmaster notices the change to shared memory. */
+ if (signal_postmaster)
+ SendPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE);
+}
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 200f72c6e25..f612e776dbf 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -56,6 +56,7 @@
#include "catalog/pg_authid.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/bgworker.h"
#include "port/pg_lfind.h"
#include "storage/proc.h"
#include "storage/procarray.h"
@@ -3768,6 +3769,12 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
for (index = 0; index < nautovacs; index++)
(void) kill(autovac_pids[index], SIGTERM); /* ignore any error */
+ /*
+ * Terminate all background workers for this database, if
+ * they had requested it (BGWORKER_EXIT_AT_DATABASE_DROP)
+ */
+ TerminateBackgroundWorkersByOid(databaseId);
+
/* sleep, then try again */
pg_usleep(100 * 1000L); /* 100ms */
}
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index 058667a47a0..57741d74987 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -59,6 +59,12 @@
*/
#define BGWORKER_BACKEND_DATABASE_CONNECTION 0x0002
+/*
+ * Exit the bgworker when its database is dropped, renamed, or moved.
+ * No-op if BGWORKER_BACKEND_DATABASE_CONNECTION is not specified.
+ */
+#define BGWORKER_EXIT_AT_DATABASE_CHANGE 0x0004
+
/*
* This class is used internally for parallel queries, to keep track of the
* number of active parallel workers and make sure we never launch more than
@@ -161,4 +167,7 @@ extern void BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, ui
extern void BackgroundWorkerBlockSignals(void);
extern void BackgroundWorkerUnblockSignals(void);
+/* Cancel background workers. */
+extern void TerminateBackgroundWorkersByOid(Oid databaseId);
+
#endif /* BGWORKER_H */
diff --git a/src/test/modules/worker_spi/meson.build b/src/test/modules/worker_spi/meson.build
index d673ece48a0..1d30048aec8 100644
--- a/src/test/modules/worker_spi/meson.build
+++ b/src/test/modules/worker_spi/meson.build
@@ -28,6 +28,7 @@ tests += {
'tap': {
'tests': [
't/001_worker_spi.pl',
+ 't/002_worker_terminate.pl'
],
},
}
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
new file mode 100644
index 00000000000..5d361c017d3
--- /dev/null
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -0,0 +1,106 @@
+# Copyright (c) 2023-2025, PostgreSQL Global Development Group
+
+# Test background workers can be terminated
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Ensure the worker_spi dynamic worker is launched on the specified database
+sub launch_bgworker
+{
+ my ($node, $database, $testcase, $allow_terminate) = @_;
+ my $offset = -s $node->logfile;
+
+ # Launch a background worker on the given database
+ my $result = $node->safe_psql(
+ $database, qq(
+ SELECT worker_spi_launch($testcase, oid, 0, '{}', $allow_terminate) IS NOT NULL
+ FROM pg_database WHERE datname = '$database';
+ ));
+ is($result, 't', "dynamic bgworker launched");
+
+ # Check the worker is surely initialized
+ $node->wait_for_log(
+ qr/LOG: worker_spi dynamic worker $testcase initialized with .*\..*/,
+ $offset);
+}
+
+# Run the given query and verify the background worker can be terminated
+sub run_db_command
+{
+ my ($node, $command, $testname) = @_;
+ my $offset = -s $node->logfile;
+
+ $node->safe_psql('postgres', $command);
+ ok( $node->log_contains(
+ "terminating background worker \"worker_spi dynamic\" due to administrator command",
+ $offset),
+ "background worker can be terminated at $testname");
+}
+
+my $node = PostgreSQL::Test::Cluster->new('mynode');
+$node->init;
+$node->start;
+
+$node->safe_psql('postgres', 'CREATE EXTENSION worker_spi;');
+
+# Launch a background worker without BGWORKER_EXIT_AT_DATABASE_CHANGE
+launch_bgworker($node, 'postgres', 0, "false");
+
+# Ensure CREATE DATABASE WITH TEMPLATE fails because background worker retains
+#
+# XXX This spends more than 5 seconds because the backend retries counting
+# number of connecting processes 50 times. See CountOtherDBBackends().
+my $stderr;
+
+$node->psql(
+ 'postgres',
+ "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ stderr => \$stderr);
+ok( $stderr =~
+ "source database \"postgres\" is being accessed by other users",
+ "background worker blocked the database creation");
+
+# Terminate the background worker for upcoming tests
+$node->safe_psql(
+ "postgres", qq(
+ SELECT pg_terminate_backend(pid)
+ FROM pg_stat_activity WHERE backend_type = 'worker_spi dynamic';));
+
+# Ensure BGWORKER_EXIT_AT_DATABASE_CHANGE allows background workers to be
+# terminated at some database manipulations.
+#
+# Testcase 1: CREATE DATABASE WITH TEMPLATE
+launch_bgworker($node, 'postgres', 1, "true");
+run_db_command(
+ $node,
+ "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ "CREATE DATABASE WITH TEMPLATE");
+
+# Testcase 2: ALTER DATABASE RENAME
+launch_bgworker($node, 'testdb', 2, "true");
+run_db_command(
+ $node,
+ "ALTER DATABASE testdb RENAME TO renameddb",
+ "ALTER DATABASE RENAME");
+
+# Preparation for the next test; create another tablespace
+my $tablespace = PostgreSQL::Test::Utils::tempdir;
+$node->safe_psql('postgres',
+ "CREATE TABLESPACE test_tablespace LOCATION '$tablespace'");
+
+# Testcase 3: ALTER DATABASE SET TABLESPACE
+launch_bgworker($node, 'renameddb', 3, "true");
+run_db_command(
+ $node,
+ "ALTER DATABASE renameddb SET TABLESPACE test_tablespace",
+ "ALTER DATABASE SET TABLESPACE");
+
+# Testcase 4: DROP DATABASE
+launch_bgworker($node, 'renameddb', 4, "true");
+run_db_command($node, "DROP DATABASE renameddb", "DROP DATABASE");
+
+done_testing();
diff --git a/src/test/modules/worker_spi/worker_spi--1.0.sql b/src/test/modules/worker_spi/worker_spi--1.0.sql
index 84deb6199f6..d29eee12d1f 100644
--- a/src/test/modules/worker_spi/worker_spi--1.0.sql
+++ b/src/test/modules/worker_spi/worker_spi--1.0.sql
@@ -7,7 +7,8 @@
CREATE FUNCTION worker_spi_launch(index int4,
dboid oid DEFAULT 0,
roleoid oid DEFAULT 0,
- flags text[] DEFAULT '{}')
+ flags text[] DEFAULT '{}',
+ allow_termination boolean DEFAULT false)
RETURNS pg_catalog.int4 STRICT
AS 'MODULE_PATHNAME'
LANGUAGE C;
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index bea8339f464..e0d0e8ab861 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -404,10 +404,15 @@ worker_spi_launch(PG_FUNCTION_ARGS)
Size ndim;
int nelems;
Datum *datum_flags;
+ bool allow_termination = PG_GETARG_BOOL(4);
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
BGWORKER_BACKEND_DATABASE_CONNECTION;
+
+ if (allow_termination)
+ worker.bgw_flags |= BGWORKER_EXIT_AT_DATABASE_CHANGE;
+
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART;
sprintf(worker.bgw_library_name, "worker_spi");
--
2.39.3
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-09 22:56 Peter Smith <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
1 sibling, 0 replies; 67+ messages in thread
From: Peter Smith @ 2025-10-09 22:56 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: Hayato Kuroda (Fujitsu) <[email protected]>; Michael Paquier <[email protected]>; pgsql-hackers
Hi Iwata-San,
Some v5 comments.
======
doc/src/sgml/bgworker.sgml
1.
+ <varlistentry>
+ <term><literal>BGWORKER_EXIT_AT_DATABASE_CHANGE</literal></term>
+ <listitem>
+ <para>
+ <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primary></indexterm>
+ Requests termination of the background worker when the database it is
+ connected to undergoes significant changes. The postmaster will send a
+ termination signal to the background worker when any of the following
+ commands are executed: <command>DROP DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>, or
+ <command>ALTER DATABASE SET TABLESPACE</command>.
+ When <command>CREATE DATABASE TEMPLATE</command> command is executed,
+ background workers which connected to target template database
are terminated.
+ If <literal>BGWORKER_SHMEM_ACCESS</literal> and
+ <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal> are not using,
+ nothing happens.
+ </para>
+ </listitem>
+ </varlistentry>
+
1a.
The newly added part in v5 needs some brush-up.
SUGGESTION:
<para>
<indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primary></indexterm>
Requests termination of the background worker when its connected database
undergoes significant changes. The postmaster sends a termination signal
when any of these commands affect the worker's database:
<command>DROP DATABASE</command>,
<command>ALTER DATABASE RENAME TO</command>,
<command>ALTER DATABASE SET TABLESPACE</command>, or
<command>CREATE DATABASE</command> (when the worker is connected to the
template database).
This flag requires both <literal>BGWORKER_SHMEM_ACCESS</literal> and
<literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal>.
</para>
~
1b.
Elsewhere on this docs page, there are detailed discussions about
process termination (e.g. search "terminate"), as well as referring to
the function TerminateBackgroundWorker().
You only documented the new flag, but do we also need to make changes
in the rest of this page to mention the new way for process
termination?
======
src/backend/postmaster/bgworker.c
TerminateBackgroundWorkersByOid:
2.
+/*
+ * Cancel background workers.
+ */
+void
+TerminateBackgroundWorkersByOid(Oid databaseId)
Let's also remove that word "Cancel" from the function comment and add
some more details.
SUGGESTION:
Terminate all background workers connected to the given database, if
they had requested it.
~~~
3.
+ /*
+ * Iterate through slots, looking for workers
+ * who connects to the given database.
+ */
"workers who connects" (??)
SUGGESTION
Iterate through slots, looking for workers connected to the given database.
======
src/backend/storage/ipc/procarray.c
4.
+ /*
+ * Terminate all background workers for this database, if
+ * they had requested it (BGWORKER_EXIT_AT_DATABASE_DROP)
+ */
+ TerminateBackgroundWorkersByOid(databaseId);
+
The comment is out-of-date now, because the flag name was changed to
BGWORKER_EXIT_AT_DATABASE_CHANGE.
======
src/include/postmaster/bgworker.h
5.
+/* Cancel background workers. */
+extern void TerminateBackgroundWorkersByOid(Oid databaseId);
There is already a commented extern for the
TerminateBackgroundWorker() function. IMO, just put this extern
alongside that one. You don't need a new comment.
~~~
6. The header comment in this file refers to the
TerminateBackgroundWorker() function. Probably, you need to check that
carefully to see if this new function should also be mentioned there.
======
FYI, I attached a v5 top-up diff for (some of) my above review
comments in case it helps.
======
Kind Regards,
Peter Smith.
Fujitsu Australia
Attachments:
[application/octet-stream] PS_v5_topup.diff (3.9K, 2-PS_v5_topup.diff)
download | inline diff:
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index 01ceb38..d6e37b5 100644
--- a/doc/src/sgml/bgworker.sgml
+++ b/doc/src/sgml/bgworker.sgml
@@ -113,17 +113,16 @@ typedef struct BackgroundWorker
<listitem>
<para>
<indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primary></indexterm>
- Requests termination of the background worker when the database it is
- connected to undergoes significant changes. The postmaster will send a
- termination signal to the background worker when any of the following
- commands are executed: <command>DROP DATABASE</command>,
- <command>ALTER DATABASE RENAME TO</command>, or
- <command>ALTER DATABASE SET TABLESPACE</command>.
- When <command>CREATE DATABASE TEMPLATE</command> command is executed,
- background workers which connected to target template database are terminated.
- If <literal>BGWORKER_SHMEM_ACCESS</literal> and
- <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal> are not using,
- nothing happens.
+ Requests termination of the background worker when its connected database
+ undergoes significant changes. The postmaster sends a termination signal
+ when any of these commands affect the worker's database:
+ <command>DROP DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>,
+ <command>ALTER DATABASE SET TABLESPACE</command>, or
+ <command>CREATE DATABASE</command> (when the worker is connected to the
+ template database).
+ This flag requires both <literal>BGWORKER_SHMEM_ACCESS</literal> and
+ <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal>.
</para>
</listitem>
</varlistentry>
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 7ab48c2..e0cfdbf 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -1400,7 +1400,8 @@ GetBackgroundWorkerTypeByPid(pid_t pid)
/*
- * Cancel background workers.
+ * Terminate all background workers connected to the given database, if they
+ * had requested it.
*/
void
TerminateBackgroundWorkersByOid(Oid databaseId)
@@ -1410,8 +1411,8 @@ TerminateBackgroundWorkersByOid(Oid databaseId)
LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
/*
- * Iterate through slots, looking for workers
- * who connects to the given database.
+ * Iterate through slots, looking for workers connected to the given
+ * database.
*/
for (int slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
{
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index f612e77..1ed641d 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3771,7 +3771,7 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
/*
* Terminate all background workers for this database, if
- * they had requested it (BGWORKER_EXIT_AT_DATABASE_DROP)
+ * they had requested it (BGWORKER_EXIT_AT_DATABASE_CHANGE)
*/
TerminateBackgroundWorkersByOid(databaseId);
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index 57741d7..3a2fc35 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -134,6 +134,7 @@ extern const char *GetBackgroundWorkerTypeByPid(pid_t pid);
/* Terminate a bgworker */
extern void TerminateBackgroundWorker(BackgroundWorkerHandle *handle);
+extern void TerminateBackgroundWorkersByOid(Oid databaseId);
/* This is valid in a running worker */
extern PGDLLIMPORT BackgroundWorker *MyBgworkerEntry;
@@ -167,7 +168,4 @@ extern void BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, ui
extern void BackgroundWorkerBlockSignals(void);
extern void BackgroundWorkerUnblockSignals(void);
-/* Cancel background workers. */
-extern void TerminateBackgroundWorkersByOid(Oid databaseId);
-
#endif /* BGWORKER_H */
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-10 02:03 Chao Li <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
1 sibling, 1 reply; 67+ messages in thread
From: Chao Li @ 2025-10-10 02:03 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: Hayato Kuroda (Fujitsu) <[email protected]>; Michael Paquier <[email protected]>; Peter Smith <[email protected]>; pgsql-hackers
Hi Iwata-san,
A few comments:
> On Oct 9, 2025, at 21:09, Aya Iwata (Fujitsu) <[email protected]> wrote:
>
> Hi,
>
> I updated the patch to v0005.
>
> Regards,
> Aya Iwata
> Fujitsu Limited
> <v0005-0001-Allow-background-workers-to-be-terminated.patch>
1 - bgworker.sgml
```
+ <varlistentry>
+ <term><literal>BGWORKER_EXIT_AT_DATABASE_CHANGE</literal></term>
+ <listitem>
+ <para>
+ <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primary></indexterm>
+ Requests termination of the background worker when the database it is
+ connected to undergoes significant changes. The postmaster will send a
+ termination signal to the background worker when any of the following
+ commands are executed: <command>DROP DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>, or
+ <command>ALTER DATABASE SET TABLESPACE</command>.
+ When <command>CREATE DATABASE TEMPLATE</command> command is executed,
+ background workers which connected to target template database are terminated.
+ If <literal>BGWORKER_SHMEM_ACCESS</literal> and
+ <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal> are not using,
+ nothing happens.
+ </para>
+ </listitem>
+ </varlistentry>
```
This paragraph has several English problems:
* “Undergoes significant changes” sounds vague, better to say “is dropped, renamed or moved to a different tablespace”.
* “When CREATE DATABASE TEMPLATE command is executed” - missing articles.
* “background workers which connected to target template database” - wrong tense/relative pronoun.
* “are not using” should be “are not used” or “are not set”
Suggested revision:
```
<indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primary></indexterm>
Requests termination of the background worker when the database it is
connected to is dropped, renamed, or moved to a different tablespace.
In these cases, the postmaster will send a termination signal to the
background worker when any of the following commands are executed:
<command>DROP DATABASE</command>, <command>ALTER DATABASE RENAME TO</command>,
or <command>ALTER DATABASE SET TABLESPACE</command>.
When a <command>CREATE DATABASE ... TEMPLATE ...</command> command is executed,
background workers connected to the template database used as the source are
also terminated.
If neither <literal>BGWORKER_SHMEM_ACCESS</literal> nor
<literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal> is set, this action
has no effect.
```
2 - bgworker.h
```
+#define BGWORKER_EXIT_AT_DATABASE_CHANGE 0x0004
```
You are using white-spaces between the macro name and value, that’s why 0x0004 looks not aligned in my IDE. I think you should use a couple tabs between them.
3 - bgworker.h
```
+extern void TerminateBackgroundWorkersByOid(Oid databaseId);
```
An OID can represent a lot of things. So, instead of suggesting the OID type by parameter name, I wonder if it is better do that with the function name, like TerminateBgWorkersByDbOid(Oid oid)
4 - procarray.c
```
+ /*
+ * Terminate all background workers for this database, if
+ * they had requested it (BGWORKER_EXIT_AT_DATABASE_DROP)
+ */
+ TerminateBackgroundWorkersByOid(databaseId);
```
I wonder if the correct parameter should be BGWORKER_EXIT_AT_DATABASE_CHANGE in the comment, as you are adding BGWORKER_EXIT_AT_DATABASE_CHANGE with this patch.
5 - bgworker.c
```
+/*
+ * Cancel background workers.
+ */
+void
+TerminateBackgroundWorkersByOid(Oid databaseId)
```
I think the function name is more descriptive than the function comment. So, please either remove function comment or enhance it.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-10 09:04 Aya Iwata (Fujitsu) <[email protected]>
parent: Chao Li <[email protected]>
0 siblings, 3 replies; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2025-10-10 09:04 UTC (permalink / raw)
To: 'Chao Li' <[email protected]>; Peter Smith <[email protected]>; +Cc: Hayato Kuroda (Fujitsu) <[email protected]>; Michael Paquier <[email protected]>; pgsql-hackers
Hi,
Thank you for your comments. I updated patch to v0006.
> -----Original Message-----
> From: Peter Smith mailto:[email protected]
> Sent: Friday, October 10, 2025 7:57 AM
> FYI, I attached a v5 top-up diff for (some of) my above review
> comments in case it helps.
Thank you very much. I've updated the patch using the attached diff file.
> From: Chao Li <[email protected]>
> Sent: Friday, October 10, 2025 11:03 AM
> 1 - bgworker.sgml
> This paragraph has several English problems:
> * “Undergoes significant changes” sounds vague, better to say “is dropped, renamed or moved to a different tablespace”.
> * “When CREATE DATABASE TEMPLATE command is executed” - missing articles.
> * “background workers which connected to target template database” - wrong tense/relative pronoun.
> * “are not using” should be “are not used” or “are not set”
Thank you for your comment an suggested revision. I changed .sgml documentation.
>2 - bgworker.h
>```
>+#define BGWORKER_EXIT_AT_DATABASE_CHANGE 0x0004
>```
>
>You are using white-spaces between the macro name and value, that’s why 0x0004 looks not aligned in my IDE. I think you should use a couple tabs between them.
Thank you. I fixed this white-spaces (using pgindent).
>3 - bgworker.h
>```
>+extern void TerminateBackgroundWorkersByOid(Oid databaseId);
>```
>
> An OID can represent a lot of things. So, instead of suggesting the OID type by parameter name, I wonder if it is better do that with the function name, like TerminateBgWorkersByDbOid(Oid oid)
After receiving your comment, I checked other functions and there is no other examples like XXOid function in the code.
If this function use only here, original code is using databaseId in argument and it clear what Oid is.
I think original name is fine because it's not a function that's called much elsewhere.
> 4 - procarray.c
> ```
> + /*
> + * Terminate all background workers for this database, if
> + * they had requested it (BGWORKER_EXIT_AT_DATABASE_DROP)
> + */
> + TerminateBackgroundWorkersByOid(databaseId);
> ```
>
> I wonder if the correct parameter should be BGWORKER_EXIT_AT_DATABASE_CHANGE in the comment, as you are adding BGWORKER_EXIT_AT_DATABASE_CHANGE with this patch.
Thank you. I fixed this comment.
> 5 - bgworker.c
> ```
> +/*
> + * Cancel background workers.
> + */
> +void
> +TerminateBackgroundWorkersByOid(Oid databaseId)
> ```
>
> I think the function name is more descriptive than the function comment. So, please either remove function comment or enhance it.
I changed this function comment:
" Terminate all background workers connected to the given database, if they had requested it."
Regards,
Aya Iwata
Fujitsu Limited.
Attachments:
[application/octet-stream] v0006-0001-Allow-background-workers-to-be-terminated.patch (10.6K, 2-v0006-0001-Allow-background-workers-to-be-terminated.patch)
download | inline diff:
From 6567d1bf3b71b316f20382162dbd58f05f538ada Mon Sep 17 00:00:00 2001
From: "iwata.aya" <[email protected]>
Date: Thu, 11 Sep 2025 21:16:51 +0900
Subject: [PATCH v0006] Allow background workers to be terminated at DROP
DATABASE
---
doc/src/sgml/bgworker.sgml | 20 ++++
src/backend/postmaster/bgworker.c | 41 +++++++
src/backend/storage/ipc/procarray.c | 7 ++
src/include/postmaster/bgworker.h | 7 ++
src/test/modules/worker_spi/meson.build | 1 +
.../worker_spi/t/002_worker_terminate.pl | 106 ++++++++++++++++++
.../modules/worker_spi/worker_spi--1.0.sql | 3 +-
src/test/modules/worker_spi/worker_spi.c | 5 +
8 files changed, 189 insertions(+), 1 deletion(-)
create mode 100644 src/test/modules/worker_spi/t/002_worker_terminate.pl
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index 2c393385a91..5a098e0c0ee 100644
--- a/doc/src/sgml/bgworker.sgml
+++ b/doc/src/sgml/bgworker.sgml
@@ -108,6 +108,26 @@ typedef struct BackgroundWorker
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>BGWORKER_EXIT_AT_DATABASE_CHANGE</literal></term>
+ <listitem>
+ <para>
+ <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primary></indexterm>
+ Requests termination of the background worker when its connected database
+ is dropped, renamed, or moved to a different tablespace.
+ In these cases, the postmaster will send a termination signal to the
+ background worker when any of the following commands are executed:
+ <command>DROP DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>,
+ <command>ALTER DATABASE SET TABLESPACE</command>, or
+ <command>CREATE DATABASE</command> (when the worker is connected to the
+ template database).
+ This flag requires both <literal>BGWORKER_SHMEM_ACCESS</literal> and
+ <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 1ad65c237c3..ea1379ccc24 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -26,6 +26,7 @@
#include "storage/lwlock.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
+#include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/shmem.h"
#include "tcop/tcopprot.h"
@@ -1396,3 +1397,43 @@ GetBackgroundWorkerTypeByPid(pid_t pid)
return result;
}
+
+
+/*
+ * Terminate all background workers connected to the given database, if they
+ * had requested it.
+ */
+void
+TerminateBackgroundWorkersByOid(Oid databaseId)
+{
+ bool signal_postmaster = false;
+
+ LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
+
+ /*
+ * Iterate through slots, looking for workers connected to the given
+ * database.
+ */
+ for (int slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
+ {
+ BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
+
+ if (slot->in_use &&
+ (slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_CHANGE))
+ {
+ PGPROC *proc = BackendPidGetProc(slot->pid);
+
+ if (proc && proc->databaseId == databaseId)
+ {
+ slot->terminate = true;
+ signal_postmaster = true;
+ }
+ }
+ }
+
+ LWLockRelease(BackgroundWorkerLock);
+
+ /* Make sure the postmaster notices the change to shared memory. */
+ if (signal_postmaster)
+ SendPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE);
+}
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 200f72c6e25..2d31c71fd8e 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -56,6 +56,7 @@
#include "catalog/pg_authid.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/bgworker.h"
#include "port/pg_lfind.h"
#include "storage/proc.h"
#include "storage/procarray.h"
@@ -3768,6 +3769,12 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
for (index = 0; index < nautovacs; index++)
(void) kill(autovac_pids[index], SIGTERM); /* ignore any error */
+ /*
+ * Terminate all background workers for this database, if they had
+ * requested it (BGWORKER_EXIT_AT_DATABASE_CHANGE)
+ */
+ TerminateBackgroundWorkersByOid(databaseId);
+
/* sleep, then try again */
pg_usleep(100 * 1000L); /* 100ms */
}
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index 058667a47a0..48eaf7af8d5 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -59,6 +59,12 @@
*/
#define BGWORKER_BACKEND_DATABASE_CONNECTION 0x0002
+/*
+ * Exit the bgworker when its database is dropped, renamed, or moved.
+ * No-op if BGWORKER_BACKEND_DATABASE_CONNECTION is not specified.
+ */
+#define BGWORKER_EXIT_AT_DATABASE_CHANGE 0x0004
+
/*
* This class is used internally for parallel queries, to keep track of the
* number of active parallel workers and make sure we never launch more than
@@ -128,6 +134,7 @@ extern const char *GetBackgroundWorkerTypeByPid(pid_t pid);
/* Terminate a bgworker */
extern void TerminateBackgroundWorker(BackgroundWorkerHandle *handle);
+extern void TerminateBackgroundWorkersByOid(Oid databaseId);
/* This is valid in a running worker */
extern PGDLLIMPORT BackgroundWorker *MyBgworkerEntry;
diff --git a/src/test/modules/worker_spi/meson.build b/src/test/modules/worker_spi/meson.build
index d673ece48a0..1d30048aec8 100644
--- a/src/test/modules/worker_spi/meson.build
+++ b/src/test/modules/worker_spi/meson.build
@@ -28,6 +28,7 @@ tests += {
'tap': {
'tests': [
't/001_worker_spi.pl',
+ 't/002_worker_terminate.pl'
],
},
}
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
new file mode 100644
index 00000000000..5d361c017d3
--- /dev/null
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -0,0 +1,106 @@
+# Copyright (c) 2023-2025, PostgreSQL Global Development Group
+
+# Test background workers can be terminated
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Ensure the worker_spi dynamic worker is launched on the specified database
+sub launch_bgworker
+{
+ my ($node, $database, $testcase, $allow_terminate) = @_;
+ my $offset = -s $node->logfile;
+
+ # Launch a background worker on the given database
+ my $result = $node->safe_psql(
+ $database, qq(
+ SELECT worker_spi_launch($testcase, oid, 0, '{}', $allow_terminate) IS NOT NULL
+ FROM pg_database WHERE datname = '$database';
+ ));
+ is($result, 't', "dynamic bgworker launched");
+
+ # Check the worker is surely initialized
+ $node->wait_for_log(
+ qr/LOG: worker_spi dynamic worker $testcase initialized with .*\..*/,
+ $offset);
+}
+
+# Run the given query and verify the background worker can be terminated
+sub run_db_command
+{
+ my ($node, $command, $testname) = @_;
+ my $offset = -s $node->logfile;
+
+ $node->safe_psql('postgres', $command);
+ ok( $node->log_contains(
+ "terminating background worker \"worker_spi dynamic\" due to administrator command",
+ $offset),
+ "background worker can be terminated at $testname");
+}
+
+my $node = PostgreSQL::Test::Cluster->new('mynode');
+$node->init;
+$node->start;
+
+$node->safe_psql('postgres', 'CREATE EXTENSION worker_spi;');
+
+# Launch a background worker without BGWORKER_EXIT_AT_DATABASE_CHANGE
+launch_bgworker($node, 'postgres', 0, "false");
+
+# Ensure CREATE DATABASE WITH TEMPLATE fails because background worker retains
+#
+# XXX This spends more than 5 seconds because the backend retries counting
+# number of connecting processes 50 times. See CountOtherDBBackends().
+my $stderr;
+
+$node->psql(
+ 'postgres',
+ "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ stderr => \$stderr);
+ok( $stderr =~
+ "source database \"postgres\" is being accessed by other users",
+ "background worker blocked the database creation");
+
+# Terminate the background worker for upcoming tests
+$node->safe_psql(
+ "postgres", qq(
+ SELECT pg_terminate_backend(pid)
+ FROM pg_stat_activity WHERE backend_type = 'worker_spi dynamic';));
+
+# Ensure BGWORKER_EXIT_AT_DATABASE_CHANGE allows background workers to be
+# terminated at some database manipulations.
+#
+# Testcase 1: CREATE DATABASE WITH TEMPLATE
+launch_bgworker($node, 'postgres', 1, "true");
+run_db_command(
+ $node,
+ "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ "CREATE DATABASE WITH TEMPLATE");
+
+# Testcase 2: ALTER DATABASE RENAME
+launch_bgworker($node, 'testdb', 2, "true");
+run_db_command(
+ $node,
+ "ALTER DATABASE testdb RENAME TO renameddb",
+ "ALTER DATABASE RENAME");
+
+# Preparation for the next test; create another tablespace
+my $tablespace = PostgreSQL::Test::Utils::tempdir;
+$node->safe_psql('postgres',
+ "CREATE TABLESPACE test_tablespace LOCATION '$tablespace'");
+
+# Testcase 3: ALTER DATABASE SET TABLESPACE
+launch_bgworker($node, 'renameddb', 3, "true");
+run_db_command(
+ $node,
+ "ALTER DATABASE renameddb SET TABLESPACE test_tablespace",
+ "ALTER DATABASE SET TABLESPACE");
+
+# Testcase 4: DROP DATABASE
+launch_bgworker($node, 'renameddb', 4, "true");
+run_db_command($node, "DROP DATABASE renameddb", "DROP DATABASE");
+
+done_testing();
diff --git a/src/test/modules/worker_spi/worker_spi--1.0.sql b/src/test/modules/worker_spi/worker_spi--1.0.sql
index 84deb6199f6..d29eee12d1f 100644
--- a/src/test/modules/worker_spi/worker_spi--1.0.sql
+++ b/src/test/modules/worker_spi/worker_spi--1.0.sql
@@ -7,7 +7,8 @@
CREATE FUNCTION worker_spi_launch(index int4,
dboid oid DEFAULT 0,
roleoid oid DEFAULT 0,
- flags text[] DEFAULT '{}')
+ flags text[] DEFAULT '{}',
+ allow_termination boolean DEFAULT false)
RETURNS pg_catalog.int4 STRICT
AS 'MODULE_PATHNAME'
LANGUAGE C;
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index bea8339f464..22cce154d69 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -404,10 +404,15 @@ worker_spi_launch(PG_FUNCTION_ARGS)
Size ndim;
int nelems;
Datum *datum_flags;
+ bool allow_termination = PG_GETARG_BOOL(4);
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
BGWORKER_BACKEND_DATABASE_CONNECTION;
+
+ if (allow_termination)
+ worker.bgw_flags |= BGWORKER_EXIT_AT_DATABASE_CHANGE;
+
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART;
sprintf(worker.bgw_library_name, "worker_spi");
--
2.39.3
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-10 11:05 Andrei Lepikhov <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
2 siblings, 1 reply; 67+ messages in thread
From: Andrei Lepikhov @ 2025-10-10 11:05 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; 'Chao Li' <[email protected]>; Peter Smith <[email protected]>; +Cc: Hayato Kuroda (Fujitsu) <[email protected]>; Michael Paquier <[email protected]>; pgsql-hackers
On 10/10/2025 11:04, Aya Iwata (Fujitsu) wrote:
> Thank you for your comments. I updated patch to v0006.My company also employs a large number of background workers. I also
reviewed your patch.
It looks nice to be committed. The only question I have is whether to
make sending a termination signal a default behaviour and let the flag
deactivate it.
--
regards, Andrei Lepikhov,
pgEdge
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-12 22:28 Peter Smith <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
2 siblings, 1 reply; 67+ messages in thread
From: Peter Smith @ 2025-10-12 22:28 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; Michael Paquier <[email protected]>; pgsql-hackers
Hi Iwata-San,
Some v6 comments.
======
doc/src/sgml/bgworker.sgml
1.
+ <para>
+ <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primary></indexterm>
+ Requests termination of the background worker when its
connected database
+ is dropped, renamed, or moved to a different tablespace.
+ In these cases, the postmaster will send a termination signal to the
+ background worker when any of the following commands are executed:
+ <command>DROP DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>,
+ <command>ALTER DATABASE SET TABLESPACE</command>, or
+ <command>CREATE DATABASE</command> (when the worker is connected to the
+ template database).
+ This flag requires both <literal>BGWORKER_SHMEM_ACCESS</literal> and
+ <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal>.
+ </para>
IMO, below is an improved wording for this:
<para>
<indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primary></indexterm>
Requests termination of the background worker when its connected database is
dropped, renamed, moved to a different tablespace, or used as a template for
<command>CREATE DATABASE</command>. Specifically, the postmaster sends a
termination signal when any of these commands affect the worker's database:
<command>DROP DATABASE</command>,
<command>ALTER DATABASE RENAME TO</command>,
<command>ALTER DATABASE SET TABLESPACE</command>, or
<command>CREATE DATABASE</command>.
Requires both <literal>BGWORKER_SHMEM_ACCESS</literal> and
<literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal>.
</para>
======
src/backend/postmaster/bgworker.c
+
+
+/*
+ * Terminate all background workers connected to the given database, if they
+ * had requested it.
+ */
+void
+TerminateBackgroundWorkersByOid(Oid databaseId)
Only 1 blank line is needed here.
======
src/include/postmaster/bgworker.h
+/*
+ * Exit the bgworker when its database is dropped, renamed, or moved.
+ * No-op if BGWORKER_BACKEND_DATABASE_CONNECTION is not specified.
+ */
+#define BGWORKER_EXIT_AT_DATABASE_CHANGE 0x0004
+
That double-negative comment seems awkward. IMO, positive statements
are clearer. Also, do you think you should mention
BGWORKER_SHMEM_ACCESS, or was that deliberately omitted because
BGWORKER_BACKEND_DATABASE_CONNECTION requires that?
e.g. The suggested comment below is more closely aligned with the documentation.
SUGGESTION:
/*
* Exit the bgworker when its database is dropped, renamed, moved to a
* different tablespace, or used as a template for CREATE DATABASE.
* Requires BGWORKER_SHMEM_ACCESS and BGWORKER_BACKEND_DATABASE_CONNECTION.
*/
======
src/test/modules/worker_spi/t/002_worker_terminate.pl
+sub launch_bgworker
+{
+ my ($node, $database, $testcase, $allow_terminate) = @_;
+ my $offset = -s $node->logfile;
Would '$request_terminate' be a more correct name for the $allow_terminate var?
======
src/test/modules/worker_spi/worker_spi.c
+ bool allow_termination = PG_GETARG_BOOL(4);
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
BGWORKER_BACKEND_DATABASE_CONNECTION;
+
+ if (allow_termination)
+ worker.bgw_flags |= BGWORKER_EXIT_AT_DATABASE_CHANGE;
+
Would 'request_termination' be a more correct name for this new var?
======
Kind Regards,
Peter Smith.
Fujitsu Australia
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-13 01:20 Peter Smith <[email protected]>
parent: Peter Smith <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Peter Smith @ 2025-10-13 01:20 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; Michael Paquier <[email protected]>; pgsql-hackers
On Mon, Oct 13, 2025 at 9:28 AM Peter Smith <[email protected]> wrote:
>
> Hi Iwata-San,
>
> Some v6 comments.
>
> ======
> doc/src/sgml/bgworker.sgml
>
> 1.
> + <para>
> + <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primary></indexterm>
> + Requests termination of the background worker when its
> connected database
> + is dropped, renamed, or moved to a different tablespace.
> + In these cases, the postmaster will send a termination signal to the
> + background worker when any of the following commands are executed:
> + <command>DROP DATABASE</command>,
> + <command>ALTER DATABASE RENAME TO</command>,
> + <command>ALTER DATABASE SET TABLESPACE</command>, or
> + <command>CREATE DATABASE</command> (when the worker is connected to the
> + template database).
> + This flag requires both <literal>BGWORKER_SHMEM_ACCESS</literal> and
> + <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal>.
> + </para>
>
>
> IMO, below is an improved wording for this:
>
> <para>
> <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primary></indexterm>
> Requests termination of the background worker when its connected database is
> dropped, renamed, moved to a different tablespace, or used as a template for
> <command>CREATE DATABASE</command>. Specifically, the postmaster sends a
> termination signal when any of these commands affect the worker's database:
> <command>DROP DATABASE</command>,
> <command>ALTER DATABASE RENAME TO</command>,
> <command>ALTER DATABASE SET TABLESPACE</command>, or
> <command>CREATE DATABASE</command>.
> Requires both <literal>BGWORKER_SHMEM_ACCESS</literal> and
> <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal>.
> </para>
>
> ======
> src/backend/postmaster/bgworker.c
>
> +
> +
> +/*
> + * Terminate all background workers connected to the given database, if they
> + * had requested it.
> + */
> +void
> +TerminateBackgroundWorkersByOid(Oid databaseId)
>
> Only 1 blank line is needed here.
>
> ======
> src/include/postmaster/bgworker.h
>
> +/*
> + * Exit the bgworker when its database is dropped, renamed, or moved.
> + * No-op if BGWORKER_BACKEND_DATABASE_CONNECTION is not specified.
> + */
> +#define BGWORKER_EXIT_AT_DATABASE_CHANGE 0x0004
> +
>
> That double-negative comment seems awkward. IMO, positive statements
> are clearer. Also, do you think you should mention
> BGWORKER_SHMEM_ACCESS, or was that deliberately omitted because
> BGWORKER_BACKEND_DATABASE_CONNECTION requires that?
>
> e.g. The suggested comment below is more closely aligned with the documentation.
>
> SUGGESTION:
> /*
> * Exit the bgworker when its database is dropped, renamed, moved to a
> * different tablespace, or used as a template for CREATE DATABASE.
> * Requires BGWORKER_SHMEM_ACCESS and BGWORKER_BACKEND_DATABASE_CONNECTION.
> */
>
> ======
> src/test/modules/worker_spi/t/002_worker_terminate.pl
>
> +sub launch_bgworker
> +{
> + my ($node, $database, $testcase, $allow_terminate) = @_;
> + my $offset = -s $node->logfile;
>
> Would '$request_terminate' be a more correct name for the $allow_terminate var?
>
> ======
> src/test/modules/worker_spi/worker_spi.c
>
> + bool allow_termination = PG_GETARG_BOOL(4);
>
> memset(&worker, 0, sizeof(worker));
> worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
> BGWORKER_BACKEND_DATABASE_CONNECTION;
> +
> + if (allow_termination)
> + worker.bgw_flags |= BGWORKER_EXIT_AT_DATABASE_CHANGE;
> +
>
> Would 'request_termination' be a more correct name for this new var?
>
There's another similar parameter name that I missed in the last post.
See /src/test/modules/worker_spi/worker_spi--1.0.sql
" allow_termination boolean DEFAULT false)"
======
Kind Regards,
Peter Smith.
Fujitsu Australia.
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-14 04:36 Hayato Kuroda (Fujitsu) <[email protected]>
parent: Andrei Lepikhov <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2025-10-14 04:36 UTC (permalink / raw)
To: 'Andrei Lepikhov' <[email protected]>; +Cc: Michael Paquier <[email protected]>; pgsql-hackers; Aya Iwata (Fujitsu) <[email protected]>; 'Chao Li' <[email protected]>; Peter Smith <[email protected]>
Dear Andrei,
> The only question I have is whether to
> make sending a termination signal a default behaviour and let the flag
> deactivate it.
I think it can be done in future enhancement.
We can change the default behavior based on the feedback from developers.
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-15 02:48 Aya Iwata (Fujitsu) <[email protected]>
parent: Peter Smith <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2025-10-15 02:48 UTC (permalink / raw)
To: 'Peter Smith' <[email protected]>; +Cc: Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; Michael Paquier <[email protected]>; pgsql-hackers
Hi Peter san,
Thank you for your comments. I updated this patch to v0007.
> -----Original Message-----
> From: Peter Smith <[email protected]>
> Sent: Monday, October 13, 2025 10:20 AM
> To: Iwata, Aya/岩田 彩 <[email protected]>
> Cc: Chao Li <[email protected]>; Kuroda, Hayato/黒田 隼人
> <[email protected]>; Michael Paquier <[email protected]>;
> pgsql-hackers <[email protected]>
> Subject: Re: [PROPOSAL] Termination of Background Workers for
> ALTER/DROP DATABASE
>
> On Mon, Oct 13, 2025 at 9:28 AM Peter Smith <[email protected]>
> wrote:
> >
> > Hi Iwata-San,
> >
> > Some v6 comments.
> >
> > ======
> > doc/src/sgml/bgworker.sgml
> >
> > 1.
> > + <para>
> > +
> <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primar
> y></indexterm>
> > + Requests termination of the background worker when its
> > connected database
> > + is dropped, renamed, or moved to a different tablespace.
> > + In these cases, the postmaster will send a termination signal to the
> > + background worker when any of the following commands are
> executed:
> > + <command>DROP DATABASE</command>,
> > + <command>ALTER DATABASE RENAME TO</command>,
> > + <command>ALTER DATABASE SET TABLESPACE</command>,
> or
> > + <command>CREATE DATABASE</command> (when the worker
> is connected to the
> > + template database).
> > + This flag requires both
> <literal>BGWORKER_SHMEM_ACCESS</literal> and
> > +
> <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal>.
> > + </para>
> >
> >
> > IMO, below is an improved wording for this:
> >
> > <para>
> >
> <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primar
> y></indexterm>
> > Requests termination of the background worker when its connected
> database is
> > dropped, renamed, moved to a different tablespace, or used as a template
> for
> > <command>CREATE DATABASE</command>. Specifically, the
> postmaster sends a
> > termination signal when any of these commands affect the worker's
> database:
> > <command>DROP DATABASE</command>,
> > <command>ALTER DATABASE RENAME TO</command>,
> > <command>ALTER DATABASE SET TABLESPACE</command>, or
> > <command>CREATE DATABASE</command>.
> > Requires both <literal>BGWORKER_SHMEM_ACCESS</literal> and
> > <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal>.
> > </para>
I updated this .sgml file. Thank you for your advice!
> > ======
> > src/backend/postmaster/bgworker.c
> >
> > +
> > +
> > +/*
> > + * Terminate all background workers connected to the given database, if
> they
> > + * had requested it.
> > + */
> > +void
> > +TerminateBackgroundWorkersByOid(Oid databaseId)
> >
> > Only 1 blank line is needed here.
I added 1 blank here.
> > ======
> > src/include/postmaster/bgworker.h
> >
> > +/*
> > + * Exit the bgworker when its database is dropped, renamed, or moved.
> > + * No-op if BGWORKER_BACKEND_DATABASE_CONNECTION is not
> specified.
> > + */
> > +#define BGWORKER_EXIT_AT_DATABASE_CHANGE 0x0004
> > +
> >
> > That double-negative comment seems awkward. IMO, positive statements
> > are clearer. Also, do you think you should mention
> > BGWORKER_SHMEM_ACCESS, or was that deliberately omitted because
> > BGWORKER_BACKEND_DATABASE_CONNECTION requires that?
> >
> > e.g. The suggested comment below is more closely aligned with the
> documentation.
> >
> > SUGGESTION:
> > /*
> > * Exit the bgworker when its database is dropped, renamed, moved to a
> > * different tablespace, or used as a template for CREATE DATABASE.
> > * Requires BGWORKER_SHMEM_ACCESS and
> BGWORKER_BACKEND_DATABASE_CONNECTION.
> > */
I have replaced this code comment.
> > ======
> > src/test/modules/worker_spi/t/002_worker_terminate.pl
> >
> > +sub launch_bgworker
> > +{
> > + my ($node, $database, $testcase, $allow_terminate) = @_;
> > + my $offset = -s $node->logfile;
> >
> > Would '$request_terminate' be a more correct name for the $allow_terminate
> var?
> >
> > ======
> > src/test/modules/worker_spi/worker_spi.c
> >
> > + bool allow_termination = PG_GETARG_BOOL(4);
> >
> > memset(&worker, 0, sizeof(worker));
> > worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
> > BGWORKER_BACKEND_DATABASE_CONNECTION;
> > +
> > + if (allow_termination)
> > + worker.bgw_flags |= BGWORKER_EXIT_AT_DATABASE_CHANGE;
> > +
> >
> > Would 'request_termination' be a more correct name for this new var?
> >
>
> There's another similar parameter name that I missed in the last post.
> See /src/test/modules/worker_spi/worker_spi--1.0.sql
>
> " allow_termination boolean DEFAULT false)"
I changed these parameter's name to "request_termination".
> On Mon, Oct 13, 2025 at 9:28 AM Peter Smith <[email protected]>
> wrote:
...
> There's another similar parameter name that I missed in the last post.
> See /src/test/modules/worker_spi/worker_spi--1.0.sql
>
> " allow_termination boolean DEFAULT false)"
I also fixed this, too.
Best regards,
Aya Iwata
Fujitsu Limited
Attachments:
[application/octet-stream] v0007-0001-Allow-background-workers-to-be-terminated.patch (10.6K, 2-v0007-0001-Allow-background-workers-to-be-terminated.patch)
download | inline diff:
From b37cba70dc39ebd2a44aa36247dd6a77dec7342d Mon Sep 17 00:00:00 2001
From: "iwata.aya" <[email protected]>
Date: Thu, 11 Sep 2025 21:16:51 +0900
Subject: [PATCH v0007] Allow background workers to be terminated at DROP
DATABASE
---
doc/src/sgml/bgworker.sgml | 19 ++++
src/backend/postmaster/bgworker.c | 41 +++++++
src/backend/storage/ipc/procarray.c | 7 ++
src/include/postmaster/bgworker.h | 8 ++
src/test/modules/worker_spi/meson.build | 1 +
.../worker_spi/t/002_worker_terminate.pl | 106 ++++++++++++++++++
.../modules/worker_spi/worker_spi--1.0.sql | 3 +-
src/test/modules/worker_spi/worker_spi.c | 5 +
8 files changed, 189 insertions(+), 1 deletion(-)
create mode 100644 src/test/modules/worker_spi/t/002_worker_terminate.pl
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index 2c393385a91..6f4fc57e3d9 100644
--- a/doc/src/sgml/bgworker.sgml
+++ b/doc/src/sgml/bgworker.sgml
@@ -108,6 +108,25 @@ typedef struct BackgroundWorker
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>BGWORKER_EXIT_AT_DATABASE_CHANGE</literal></term>
+ <listitem>
+ <para>
+ <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primary></indexterm>
+ Requests termination of the background worker when its connected database is
+ dropped, renamed, moved to a different tablespace, or used as a template for
+ <command>CREATE DATABASE</command>. Specifically, the postmaster sends a
+ termination signal when any of these commands affect the worker's database:
+ <command>DROP DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>,
+ <command>ALTER DATABASE SET TABLESPACE</command>, or
+ <command>CREATE DATABASE</command>.
+ Requires both <literal>BGWORKER_SHMEM_ACCESS</literal> and
+ <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 1ad65c237c3..92600e59011 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -26,6 +26,7 @@
#include "storage/lwlock.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
+#include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/shmem.h"
#include "tcop/tcopprot.h"
@@ -1396,3 +1397,43 @@ GetBackgroundWorkerTypeByPid(pid_t pid)
return result;
}
+
+/*
+ * Terminate all background workers connected to the given database, if they
+ * had requested it.
+ */
+
+void
+TerminateBackgroundWorkersByOid(Oid databaseId)
+{
+ bool signal_postmaster = false;
+
+ LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
+
+ /*
+ * Iterate through slots, looking for workers connected to the given
+ * database.
+ */
+ for (int slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
+ {
+ BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
+
+ if (slot->in_use &&
+ (slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_CHANGE))
+ {
+ PGPROC *proc = BackendPidGetProc(slot->pid);
+
+ if (proc && proc->databaseId == databaseId)
+ {
+ slot->terminate = true;
+ signal_postmaster = true;
+ }
+ }
+ }
+
+ LWLockRelease(BackgroundWorkerLock);
+
+ /* Make sure the postmaster notices the change to shared memory. */
+ if (signal_postmaster)
+ SendPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE);
+}
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 200f72c6e25..2d31c71fd8e 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -56,6 +56,7 @@
#include "catalog/pg_authid.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/bgworker.h"
#include "port/pg_lfind.h"
#include "storage/proc.h"
#include "storage/procarray.h"
@@ -3768,6 +3769,12 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
for (index = 0; index < nautovacs; index++)
(void) kill(autovac_pids[index], SIGTERM); /* ignore any error */
+ /*
+ * Terminate all background workers for this database, if they had
+ * requested it (BGWORKER_EXIT_AT_DATABASE_CHANGE)
+ */
+ TerminateBackgroundWorkersByOid(databaseId);
+
/* sleep, then try again */
pg_usleep(100 * 1000L); /* 100ms */
}
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index 058667a47a0..a392f190bd4 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -59,6 +59,13 @@
*/
#define BGWORKER_BACKEND_DATABASE_CONNECTION 0x0002
+/*
+ * Exit the bgworker when its database is dropped, renamed, moved to a
+ * different tablespace, or used as a template for CREATE DATABASE.
+ * Requires BGWORKER_SHMEM_ACCESS and BGWORKER_BACKEND_DATABASE_CONNECTION.
+ */
+#define BGWORKER_EXIT_AT_DATABASE_CHANGE 0x0004
+
/*
* This class is used internally for parallel queries, to keep track of the
* number of active parallel workers and make sure we never launch more than
@@ -128,6 +135,7 @@ extern const char *GetBackgroundWorkerTypeByPid(pid_t pid);
/* Terminate a bgworker */
extern void TerminateBackgroundWorker(BackgroundWorkerHandle *handle);
+extern void TerminateBackgroundWorkersByOid(Oid databaseId);
/* This is valid in a running worker */
extern PGDLLIMPORT BackgroundWorker *MyBgworkerEntry;
diff --git a/src/test/modules/worker_spi/meson.build b/src/test/modules/worker_spi/meson.build
index d673ece48a0..1d30048aec8 100644
--- a/src/test/modules/worker_spi/meson.build
+++ b/src/test/modules/worker_spi/meson.build
@@ -28,6 +28,7 @@ tests += {
'tap': {
'tests': [
't/001_worker_spi.pl',
+ 't/002_worker_terminate.pl'
],
},
}
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
new file mode 100644
index 00000000000..979ad80651c
--- /dev/null
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -0,0 +1,106 @@
+# Copyright (c) 2023-2025, PostgreSQL Global Development Group
+
+# Test background workers can be terminated
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Ensure the worker_spi dynamic worker is launched on the specified database
+sub launch_bgworker
+{
+ my ($node, $database, $testcase, $request_terminate) = @_;
+ my $offset = -s $node->logfile;
+
+ # Launch a background worker on the given database
+ my $result = $node->safe_psql(
+ $database, qq(
+ SELECT worker_spi_launch($testcase, oid, 0, '{}', $request_terminate) IS NOT NULL
+ FROM pg_database WHERE datname = '$database';
+ ));
+ is($result, 't', "dynamic bgworker launched");
+
+ # Check the worker is surely initialized
+ $node->wait_for_log(
+ qr/LOG: worker_spi dynamic worker $testcase initialized with .*\..*/,
+ $offset);
+}
+
+# Run the given query and verify the background worker can be terminated
+sub run_db_command
+{
+ my ($node, $command, $testname) = @_;
+ my $offset = -s $node->logfile;
+
+ $node->safe_psql('postgres', $command);
+ ok( $node->log_contains(
+ "terminating background worker \"worker_spi dynamic\" due to administrator command",
+ $offset),
+ "background worker can be terminated at $testname");
+}
+
+my $node = PostgreSQL::Test::Cluster->new('mynode');
+$node->init;
+$node->start;
+
+$node->safe_psql('postgres', 'CREATE EXTENSION worker_spi;');
+
+# Launch a background worker without BGWORKER_EXIT_AT_DATABASE_CHANGE
+launch_bgworker($node, 'postgres', 0, "false");
+
+# Ensure CREATE DATABASE WITH TEMPLATE fails because background worker retains
+#
+# XXX This spends more than 5 seconds because the backend retries counting
+# number of connecting processes 50 times. See CountOtherDBBackends().
+my $stderr;
+
+$node->psql(
+ 'postgres',
+ "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ stderr => \$stderr);
+ok( $stderr =~
+ "source database \"postgres\" is being accessed by other users",
+ "background worker blocked the database creation");
+
+# Terminate the background worker for upcoming tests
+$node->safe_psql(
+ "postgres", qq(
+ SELECT pg_terminate_backend(pid)
+ FROM pg_stat_activity WHERE backend_type = 'worker_spi dynamic';));
+
+# Ensure BGWORKER_EXIT_AT_DATABASE_CHANGE allows background workers to be
+# terminated at some database manipulations.
+#
+# Testcase 1: CREATE DATABASE WITH TEMPLATE
+launch_bgworker($node, 'postgres', 1, "true");
+run_db_command(
+ $node,
+ "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ "CREATE DATABASE WITH TEMPLATE");
+
+# Testcase 2: ALTER DATABASE RENAME
+launch_bgworker($node, 'testdb', 2, "true");
+run_db_command(
+ $node,
+ "ALTER DATABASE testdb RENAME TO renameddb",
+ "ALTER DATABASE RENAME");
+
+# Preparation for the next test; create another tablespace
+my $tablespace = PostgreSQL::Test::Utils::tempdir;
+$node->safe_psql('postgres',
+ "CREATE TABLESPACE test_tablespace LOCATION '$tablespace'");
+
+# Testcase 3: ALTER DATABASE SET TABLESPACE
+launch_bgworker($node, 'renameddb', 3, "true");
+run_db_command(
+ $node,
+ "ALTER DATABASE renameddb SET TABLESPACE test_tablespace",
+ "ALTER DATABASE SET TABLESPACE");
+
+# Testcase 4: DROP DATABASE
+launch_bgworker($node, 'renameddb', 4, "true");
+run_db_command($node, "DROP DATABASE renameddb", "DROP DATABASE");
+
+done_testing();
diff --git a/src/test/modules/worker_spi/worker_spi--1.0.sql b/src/test/modules/worker_spi/worker_spi--1.0.sql
index 84deb6199f6..3d12de37bea 100644
--- a/src/test/modules/worker_spi/worker_spi--1.0.sql
+++ b/src/test/modules/worker_spi/worker_spi--1.0.sql
@@ -7,7 +7,8 @@
CREATE FUNCTION worker_spi_launch(index int4,
dboid oid DEFAULT 0,
roleoid oid DEFAULT 0,
- flags text[] DEFAULT '{}')
+ flags text[] DEFAULT '{}',
+ request_termination boolean DEFAULT false)
RETURNS pg_catalog.int4 STRICT
AS 'MODULE_PATHNAME'
LANGUAGE C;
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index bea8339f464..2912abe6cce 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -404,10 +404,15 @@ worker_spi_launch(PG_FUNCTION_ARGS)
Size ndim;
int nelems;
Datum *datum_flags;
+ bool request_termination = PG_GETARG_BOOL(4);
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
BGWORKER_BACKEND_DATABASE_CONNECTION;
+
+ if (request_termination)
+ worker.bgw_flags |= BGWORKER_EXIT_AT_DATABASE_CHANGE;
+
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART;
sprintf(worker.bgw_library_name, "worker_spi");
--
2.39.3
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-15 03:37 Chao Li <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
2 siblings, 0 replies; 67+ messages in thread
From: Chao Li @ 2025-10-15 03:37 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: Peter Smith <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; Michael Paquier <[email protected]>; pgsql-hackers
> On Oct 10, 2025, at 17:04, Aya Iwata (Fujitsu) <[email protected]> wrote:
>
>> 3 - bgworker.h
>> ```
>> +extern void TerminateBackgroundWorkersByOid(Oid databaseId);
>> ```
>>
>> An OID can represent a lot of things. So, instead of suggesting the OID type by parameter name, I wonder if it is better do that with the function name, like TerminateBgWorkersByDbOid(Oid oid)
>
> After receiving your comment, I checked other functions and there is no other examples like XXOid function in the code.
> If this function use only here, original code is using databaseId in argument and it clear what Oid is.
> I think original name is fine because it's not a function that's called much elsewhere.
By searching for “ByOid”, we can get some existing examples:
ObjectAddress
RefreshMatViewByOid(Oid matviewOid, bool is_create, bool skipData,
bool concurrent, const char *queryString,
QueryCompletion *qc)
The function name clearly tells refresh MatView by Oid, so the oid in parameter is an old of mat view.
ResultRelInfo *
ExecLookupResultRelByOid(ModifyTableState *node, Oid resultoid,
bool missing_ok, bool update_cache)
The function name indicates ResultRel, so the oid is a result oid.
AccessMethodInfo *
findAccessMethodByOid(Oid oid)
The function name tells to find access method, the the oid is an access method’s OID.
You can find more …
But in this patch, the function name only indeeds “terminate background workers”, while the oid is a database oid. Maybe we can rename the function to “TerminateDatabaseBgWorkersByOid()”.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-16 03:37 Michael Paquier <[email protected]>
parent: Hayato Kuroda (Fujitsu) <[email protected]>
0 siblings, 0 replies; 67+ messages in thread
From: Michael Paquier @ 2025-10-16 03:37 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: 'Andrei Lepikhov' <[email protected]>; pgsql-hackers; Aya Iwata (Fujitsu) <[email protected]>; 'Chao Li' <[email protected]>; Peter Smith <[email protected]>
On Tue, Oct 14, 2025 at 04:36:01AM +0000, Hayato Kuroda (Fujitsu) wrote:
>> The only question I have is whether to
>> make sending a termination signal a default behaviour and let the flag
>> deactivate it.
>
> I think it can be done in future enhancement.
> We can change the default behavior based on the feedback from developers.
Yeah, I cannot agree with a change in the default behavior to cancel
the workers on a database touched by a database command. This is a
behavior that exists since bgworkers are supported in tree in 9.3. If
one is interested in making the workers more responsive, they could
just flip the flag switch.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, 2-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-16 03:54 Michael Paquier <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
0 siblings, 2 replies; 67+ messages in thread
From: Michael Paquier @ 2025-10-16 03:54 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: 'Peter Smith' <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
On Wed, Oct 15, 2025 at 02:48:43AM +0000, Aya Iwata (Fujitsu) wrote:
> Thank you for your comments. I updated this patch to v0007.
+ * Exit the bgworker when its database is dropped, renamed, moved to a
+ * different tablespace, or used as a template for CREATE DATABASE.
I don't think that we need to list all these operations in details
here. We could just say "if its database is involved in a CREATE,
ALTER or DROP database command". The docs should provide these
details, of course.
+#define BGWORKER_EXIT_AT_DATABASE_CHANGE 0x0004
Flag name works here.
# XXX This spends more than 5 seconds because the backend retries counting
# number of connecting processes 50 times. See CountOtherDBBackends().
And that's annoying. Let's activate what I call the cheat mode for
this one: an injection point that, if defined, enforces a lower number
of tries when we loop over the workers to stop. That would make the
test much faster when using a worker that should not be stopped,
without impacting the coverage.
I suspect that your new test 002_worker_terminate.pl has a race
condition in run_db_command(): are you sure that the bgworker has
enough time to be reported as stopped in the server logs once
safe_psql() finishes to run the database command given by the caller?
On very slow and/or loaded machines, particularly, that could hurt the
stability. It seems to me that this should use a wait_for_log()
instead of a log_contains(), waiting for the worker to be reported as
stopped depending on the command executed.
Shouldn't this test also check that worker 0 (the one that does not
have the flag set) is still running at the end of the test? I assume
that querying pg_stat_activity would be enough at the end of the
script.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, 2-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-16 15:04 Aya Iwata (Fujitsu) <[email protected]>
parent: Michael Paquier <[email protected]>
1 sibling, 1 reply; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2025-10-16 15:04 UTC (permalink / raw)
To: 'Michael Paquier' <[email protected]>; Chao Li <[email protected]>; +Cc: 'Peter Smith' <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
Hi Chao-san, Michael san
Thank you for your comments! To accept your comment, I updated patch to v0008.
> From: Chao Li <[email protected]>
> Sent: Wednesday, October 15, 2025 12:37 PM
...
> By searching for “ByOid”, we can get some existing examples:
>
> ObjectAddress
> RefreshMatViewByOid(Oid matviewOid, bool is_create, bool skipData,
> bool concurrent, const char *queryString,
> QueryCompletion *qc)
>
> The function name clearly tells refresh MatView by Oid, so the oid in parameter is an old of mat view.
>
> ResultRelInfo *
> ExecLookupResultRelByOid(ModifyTableState *node, Oid resultoid,
> bool missing_ok, bool update_cache)
>
> The function name indicates ResultRel, so the oid is a result oid.
>
> AccessMethodInfo *
> findAccessMethodByOid(Oid oid)
>
> The function name tells to find access method, the the oid is an access method’s OID.
>
> You can find more …
>
> But in this patch, the function name only indeeds “terminate background workers”, while the oid is a database oid. Maybe we can rename the
> function to “TerminateDatabaseBgWorkersByOid()”.
Thank you. I changed the function name to "'TerminateBgWorkersByDbOid".
I prefer this name because there are not official terminology "Database background worker" and it's shorter.
> -----Original Message-----
> From: Michael Paquier <[email protected]>
> Sent: Thursday, October 16, 2025 12:55 PM
...
> On Wed, Oct 15, 2025 at 02:48:43AM +0000, Aya Iwata (Fujitsu) wrote:
> + * Exit the bgworker when its database is dropped, renamed, moved to a
> + * different tablespace, or used as a template for CREATE DATABASE.
>
> I don't think that we need to list all these operations in details
> here. We could just say "if its database is involved in a CREATE,
> ALTER or DROP database command". The docs should provide these
> details, of course.
Thank you. I fixed this .h file comment.
>
> +#define BGWORKER_EXIT_AT_DATABASE_CHANGE 0x0004
>
> Flag name works here.
Sorry, I cannot follow. Please tell me more details about this comment.
> # XXX This spends more than 5 seconds because the backend retries counting
> # number of connecting processes 50 times. See CountOtherDBBackends().
>
> And that's annoying. Let's activate what I call the cheat mode for
> this one: an injection point that, if defined, enforces a lower number
> of tries when we loop over the workers to stop. That would make the
> test much faster when using a worker that should not be stopped,
> without impacting the coverage.
I tried to implement your idea. Thanks Kuroda-san to help it.
> I suspect that your new test 002_worker_terminate.pl has a race
> condition in run_db_command(): are you sure that the bgworker has
> enough time to be reported as stopped in the server logs once
> safe_psql() finishes to run the database command given by the caller?
> On very slow and/or loaded machines, particularly, that could hurt the
> stability. It seems to me that this should use a wait_for_log()
> instead of a log_contains(), waiting for the worker to be reported as
> stopped depending on the command executed.
I fixed this test to use wait_for_log() instead of log_contains().
> Shouldn't this test also check that worker 0 (the one that does not
> have the flag set) is still running at the end of the test? I assume
> that querying pg_stat_activity would be enough at the end of the
> script.
Added. I cannot find a good way to clarify the worker is "worker 0" from the pg_stat_activity,
backend_type does not have the information. Thus I used the string "worker_spi dynamic" as the key.
Best regards,
Aya Iwata
Fujitsu Limited
Attachments:
[application/octet-stream] v0008-0001-Allow-background-workers-to-be-terminated.patch (13.1K, 2-v0008-0001-Allow-background-workers-to-be-terminated.patch)
download | inline diff:
From bf6e3f9d70621017e52bda41ddee5d0a33895639 Mon Sep 17 00:00:00 2001
From: "iwata.aya" <[email protected]>
Date: Thu, 11 Sep 2025 21:16:51 +0900
Subject: [PATCH v0008] Allow background workers to be terminated at DROP
DATABASE
---
doc/src/sgml/bgworker.sgml | 19 +++
src/backend/postmaster/bgworker.c | 41 ++++++
src/backend/storage/ipc/procarray.c | 23 ++-
src/include/postmaster/bgworker.h | 8 +
src/test/modules/worker_spi/Makefile | 4 +
src/test/modules/worker_spi/meson.build | 4 +
.../worker_spi/t/002_worker_terminate.pl | 139 ++++++++++++++++++
.../modules/worker_spi/worker_spi--1.0.sql | 3 +-
src/test/modules/worker_spi/worker_spi.c | 5 +
9 files changed, 243 insertions(+), 3 deletions(-)
create mode 100644 src/test/modules/worker_spi/t/002_worker_terminate.pl
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index 2c393385a91..6f4fc57e3d9 100644
--- a/doc/src/sgml/bgworker.sgml
+++ b/doc/src/sgml/bgworker.sgml
@@ -108,6 +108,25 @@ typedef struct BackgroundWorker
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>BGWORKER_EXIT_AT_DATABASE_CHANGE</literal></term>
+ <listitem>
+ <para>
+ <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primary></indexterm>
+ Requests termination of the background worker when its connected database is
+ dropped, renamed, moved to a different tablespace, or used as a template for
+ <command>CREATE DATABASE</command>. Specifically, the postmaster sends a
+ termination signal when any of these commands affect the worker's database:
+ <command>DROP DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>,
+ <command>ALTER DATABASE SET TABLESPACE</command>, or
+ <command>CREATE DATABASE</command>.
+ Requires both <literal>BGWORKER_SHMEM_ACCESS</literal> and
+ <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 1ad65c237c3..73618a47991 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -26,6 +26,7 @@
#include "storage/lwlock.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
+#include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/shmem.h"
#include "tcop/tcopprot.h"
@@ -1396,3 +1397,43 @@ GetBackgroundWorkerTypeByPid(pid_t pid)
return result;
}
+
+/*
+ * Terminate all background workers connected to the given database, if they
+ * had requested it.
+ */
+
+void
+TerminateBgWorkersByDbOid(Oid oid)
+{
+ bool signal_postmaster = false;
+
+ LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
+
+ /*
+ * Iterate through slots, looking for workers connected to the given
+ * database.
+ */
+ for (int slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
+ {
+ BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
+
+ if (slot->in_use &&
+ (slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_CHANGE))
+ {
+ PGPROC *proc = BackendPidGetProc(slot->pid);
+
+ if (proc && proc->databaseId == oid)
+ {
+ slot->terminate = true;
+ signal_postmaster = true;
+ }
+ }
+ }
+
+ LWLockRelease(BackgroundWorkerLock);
+
+ /* Make sure the postmaster notices the change to shared memory. */
+ if (signal_postmaster)
+ SendPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE);
+}
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 200f72c6e25..397c59b3f5f 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -56,11 +56,13 @@
#include "catalog/pg_authid.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/bgworker.h"
#include "port/pg_lfind.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/injection_point.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
@@ -3717,8 +3719,19 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
int autovac_pids[MAXAUTOVACPIDS];
int tries;
- /* 50 tries with 100ms sleep between tries makes 5 sec total wait */
- for (tries = 0; tries < 50; tries++)
+ /*
+ * Usually, we try 50 times with 100ms sleep between tries, making 5 sec
+ * total wait. If requested, it would be reduced to 10 times to shorten the
+ * test time.
+ */
+ int ntries = 50;
+
+#ifdef USE_INJECTION_POINTS
+ if (IS_INJECTION_POINT_ATTACHED("reduce-ncounts"))
+ ntries = 10;
+#endif
+
+ for (tries = 0; tries < ntries; tries++)
{
int nautovacs = 0;
bool found = false;
@@ -3768,6 +3781,12 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
for (index = 0; index < nautovacs; index++)
(void) kill(autovac_pids[index], SIGTERM); /* ignore any error */
+ /*
+ * Terminate all background workers for this database, if they had
+ * requested it (BGWORKER_EXIT_AT_DATABASE_CHANGE)
+ */
+ TerminateBgWorkersByDbOid(databaseId);
+
/* sleep, then try again */
pg_usleep(100 * 1000L); /* 100ms */
}
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index 058667a47a0..4bedb057fec 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -59,6 +59,13 @@
*/
#define BGWORKER_BACKEND_DATABASE_CONNECTION 0x0002
+/*
+ * Exit the bgworker if its database is involved in a CREATE, ALTER or DROP
+ * database command.
+ * Requires BGWORKER_SHMEM_ACCESS and BGWORKER_BACKEND_DATABASE_CONNECTION.
+ */
+#define BGWORKER_EXIT_AT_DATABASE_CHANGE 0x0004
+
/*
* This class is used internally for parallel queries, to keep track of the
* number of active parallel workers and make sure we never launch more than
@@ -128,6 +135,7 @@ extern const char *GetBackgroundWorkerTypeByPid(pid_t pid);
/* Terminate a bgworker */
extern void TerminateBackgroundWorker(BackgroundWorkerHandle *handle);
+extern void TerminateBgWorkersByDbOid(Oid oid);
/* This is valid in a running worker */
extern PGDLLIMPORT BackgroundWorker *MyBgworkerEntry;
diff --git a/src/test/modules/worker_spi/Makefile b/src/test/modules/worker_spi/Makefile
index 024b34cdbb3..e7c5c059e32 100644
--- a/src/test/modules/worker_spi/Makefile
+++ b/src/test/modules/worker_spi/Makefile
@@ -6,6 +6,10 @@ EXTENSION = worker_spi
DATA = worker_spi--1.0.sql
PGFILEDESC = "worker_spi - background worker example"
+EXTRA_INSTALL = src/test/modules/injection_points
+
+export enable_injection_points
+
TAP_TESTS = 1
ifdef USE_PGXS
diff --git a/src/test/modules/worker_spi/meson.build b/src/test/modules/worker_spi/meson.build
index d673ece48a0..5ba66051396 100644
--- a/src/test/modules/worker_spi/meson.build
+++ b/src/test/modules/worker_spi/meson.build
@@ -26,8 +26,12 @@ tests += {
'sd': meson.current_source_dir(),
'bd': meson.current_build_dir(),
'tap': {
+ 'env': {
+ 'enable_injection_points': get_option('injection_points') ? 'yes' : 'no',
+ },
'tests': [
't/001_worker_spi.pl',
+ 't/002_worker_terminate.pl'
],
},
}
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
new file mode 100644
index 00000000000..20987ff3a3b
--- /dev/null
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -0,0 +1,139 @@
+# Copyright (c) 2025, PostgreSQL Global Development Group
+
+# Test background workers can be terminated by db commands
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# This test depends on injection points to detect whether background workers
+# remain.
+if ($ENV{enable_injection_points} ne 'yes')
+{
+ plan skip_all => 'Injection points not supported by this build';
+}
+
+# Ensure the worker_spi dynamic worker is launched on the specified database
+sub launch_bgworker
+{
+ my ($node, $database, $testcase, $request_terminate) = @_;
+ my $offset = -s $node->logfile;
+
+ # Launch a background worker on the given database
+ my $result = $node->safe_psql(
+ $database, qq(
+ SELECT worker_spi_launch($testcase, oid, 0, '{}', $request_terminate) IS NOT NULL
+ FROM pg_database WHERE datname = '$database';
+ ));
+ is($result, 't', "dynamic bgworker launched");
+
+ # Check the worker is surely initialized
+ $node->wait_for_log(
+ qr/LOG: worker_spi dynamic worker $testcase initialized with .*\..*/,
+ $offset);
+}
+
+# Run the given query and verify the background worker can be terminated
+sub run_db_command
+{
+ my ($node, $command, $testname) = @_;
+ my $offset = -s $node->logfile;
+
+ $node->safe_psql('postgres', $command);
+
+ $node->wait_for_log(
+ qr/terminating background worker \"worker_spi dynamic\" due to administrator command/,
+ $offset);
+
+ note("background worker can be terminated at $testname");
+}
+
+my $node = PostgreSQL::Test::Cluster->new('mynode');
+$node->init;
+$node->start;
+
+# Check if the extension injection_points is available, as it may be
+# possible that this script is run with installcheck, where the module
+# would not be installed by default.
+if (!$node->check_extension('injection_points'))
+{
+ plan skip_all => 'Extension injection_points not installed';
+}
+
+$node->safe_psql('postgres', 'CREATE EXTENSION worker_spi;');
+
+# Launch a background worker without BGWORKER_EXIT_AT_DATABASE_CHANGE
+launch_bgworker($node, 'postgres', 0, "false");
+
+# Ensure CREATE DATABASE WITH TEMPLATE fails because background worker retains
+
+# Firstly register an injection point to make the test faster. Normally, it
+# spends more than 5 seconds because the backend retries, counting the number
+# of connecting processes 50 times, but now the counting would be done only 10
+# times. See CountOtherDBBackends().
+$node->safe_psql('postgres', "CREATE EXTENSION injection_points;");
+$node->safe_psql('postgres',
+ "SELECT injection_points_attach('reduce-ncounts', 'error');");
+
+my $stderr;
+
+$node->psql(
+ 'postgres',
+ "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ stderr => \$stderr);
+ok( $stderr =~
+ "source database \"postgres\" is being accessed by other users",
+ "background worker blocked the database creation");
+
+# Confirm a background worker is still running
+$node->safe_psql(
+ "postgres", qq(
+ SELECT count(1) FROM pg_stat_activity
+ WHERE backend_type = 'worker_spi dynamic';));
+
+# Terminate the worker for upcoming tests
+$node->safe_psql(
+ "postgres", qq(
+ SELECT pg_terminate_backend(pid)
+ FROM pg_stat_activity WHERE backend_type = 'worker_spi dynamic';));
+
+# The injection point won't be used anymore, release it.
+$node->safe_psql('postgres',
+ "SELECT injection_points_detach('reduce-ncounts');");
+
+# Ensure BGWORKER_EXIT_AT_DATABASE_CHANGE allows background workers to be
+# terminated at some database manipulations.
+#
+# Testcase 1: CREATE DATABASE WITH TEMPLATE
+launch_bgworker($node, 'postgres', 1, "true");
+run_db_command(
+ $node,
+ "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ "CREATE DATABASE WITH TEMPLATE");
+
+# Testcase 2: ALTER DATABASE RENAME
+launch_bgworker($node, 'testdb', 2, "true");
+run_db_command(
+ $node,
+ "ALTER DATABASE testdb RENAME TO renameddb",
+ "ALTER DATABASE RENAME");
+
+# Preparation for the next test; create another tablespace
+my $tablespace = PostgreSQL::Test::Utils::tempdir;
+$node->safe_psql('postgres',
+ "CREATE TABLESPACE test_tablespace LOCATION '$tablespace'");
+
+# Testcase 3: ALTER DATABASE SET TABLESPACE
+launch_bgworker($node, 'renameddb', 3, "true");
+run_db_command(
+ $node,
+ "ALTER DATABASE renameddb SET TABLESPACE test_tablespace",
+ "ALTER DATABASE SET TABLESPACE");
+
+# Testcase 4: DROP DATABASE
+launch_bgworker($node, 'renameddb', 4, "true");
+run_db_command($node, "DROP DATABASE renameddb", "DROP DATABASE");
+
+done_testing();
diff --git a/src/test/modules/worker_spi/worker_spi--1.0.sql b/src/test/modules/worker_spi/worker_spi--1.0.sql
index 84deb6199f6..3d12de37bea 100644
--- a/src/test/modules/worker_spi/worker_spi--1.0.sql
+++ b/src/test/modules/worker_spi/worker_spi--1.0.sql
@@ -7,7 +7,8 @@
CREATE FUNCTION worker_spi_launch(index int4,
dboid oid DEFAULT 0,
roleoid oid DEFAULT 0,
- flags text[] DEFAULT '{}')
+ flags text[] DEFAULT '{}',
+ request_termination boolean DEFAULT false)
RETURNS pg_catalog.int4 STRICT
AS 'MODULE_PATHNAME'
LANGUAGE C;
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index bea8339f464..2912abe6cce 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -404,10 +404,15 @@ worker_spi_launch(PG_FUNCTION_ARGS)
Size ndim;
int nelems;
Datum *datum_flags;
+ bool request_termination = PG_GETARG_BOOL(4);
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
BGWORKER_BACKEND_DATABASE_CONNECTION;
+
+ if (request_termination)
+ worker.bgw_flags |= BGWORKER_EXIT_AT_DATABASE_CHANGE;
+
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART;
sprintf(worker.bgw_library_name, "worker_spi");
--
2.39.3
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-20 02:01 Peter Smith <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
0 siblings, 2 replies; 67+ messages in thread
From: Peter Smith @ 2025-10-20 02:01 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: Michael Paquier <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
Hi Iwata-San,
Some comments for the latest v8 patch.
======
src/backend/postmaster/bgworker.c
TerminateBgWorkersByBbOid:
1.
+void
+TerminateBgWorkersByDbOid(Oid oid)
Now the function name is more explicit, but that is not a good reason
to make the parameter name more vague.
IMO the parameter should still be "dbOid" or "databaseId" instead of
just "oid". (ditto for the extern in bgworker.h)
======
src/backend/storage/ipc/procarray.c
CountOtherDBBackends:
2.
+ /*
+ * Usually, we try 50 times with 100ms sleep between tries, making 5 sec
+ * total wait. If requested, it would be reduced to 10 times to shorten the
+ * test time.
+ */
The comment seemed vague to me. How about more like:
/*
* Retry up to 50 times with 100ms between attempts (max 5s total).
* Can be reduced to 10 attempts (max 1s total) to speed up tests.
*/
~~~
3.
+ for (tries = 0; tries < ntries; tries++)
'tries' can be declared as a for-loop variable.
~~~
4.
Something feels strange about this function name
(CountOtherDBBackends) which suggests it is just for counting stuff,
but in reality is more about exiting/terminating the workers. In fact
retuns a boolean, not a count. Compare this with this similarly named
"CountUserBackends" which really *is* doing what it says.
Can we give this function a better name, or is that out of scope for this patch?
======
src/test/modules/worker_spi/t/002_worker_terminate.pl
5.
+# Firstly register an injection point to make the test faster. Normally, it
+# spends more than 5 seconds because the backend retries, counting the number
+# of connecting processes 50 times, but now the counting would be done only 10
+# times. See CountOtherDBBackends().
+$node->safe_psql('postgres', "CREATE EXTENSION injection_points;");
+$node->safe_psql('postgres',
+ "SELECT injection_points_attach('reduce-ncounts', 'error');");
+
It seemed overkill to give details about what "normally" happens. I
think it is enough to have a simple comment here:
SUGGESTION
The injection point 'reduce-ncounts' reduces the number of backend
retries, allowing for shorter test runs. See CountOtherDBBackends().
======
Kind Regards,
Peter Smith.
Fujitsu Australia
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-20 04:32 Michael Paquier <[email protected]>
parent: Peter Smith <[email protected]>
1 sibling, 1 reply; 67+ messages in thread
From: Michael Paquier @ 2025-10-20 04:32 UTC (permalink / raw)
To: Peter Smith <[email protected]>; +Cc: Aya Iwata (Fujitsu) <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
On Mon, Oct 20, 2025 at 01:01:31PM +1100, Peter Smith wrote:
> Some comments for the latest v8 patch.
The comments of Peter apply to comments and parameters. I am not
going down to these details in this message, these can be infinitely
tuned.
The injection point integration looks correct. You are checking the
compile flag and if the extension is available in the installation
path, which should be enough.
+ if (IS_INJECTION_POINT_ATTACHED("reduce-ncounts"))
+ ntries = 10;
1s is much faster than the default of 5s, still I am wondering if this
cannot be brought down a bit more. Dropping the worker still around
after the first test with CREATE DATABASE works here.
+# Confirm a background worker is still running
+$node->safe_psql(
+ "postgres", qq(
+ SELECT count(1) FROM pg_stat_activity
+ WHERE backend_type = 'worker_spi dynamic';));
This does not check that the worker that does not have the flag set is
still running: you are not feeding the output of this query to an is()
test.
+ is($result, 't', "dynamic bgworker launched");
In launch_bgworker(), this uses the same test description for all the
callers of this subroutine. Let's prefix it with $testcase.
+void
+TerminateBgWorkersByDbOid(Oid oid)
FWIW, while reading this code, I was wondering about one improvement
that could show benefits for more extension code than only what we are
discussing here because external code has no access to
BackgroundWorkerSlot while holding the LWLock BackgroundWorkerLock in
a single loop, by rewriting this new routine with something like that:
void TerminateBackgroundWorkerMatchin(
bool (*do_terminate) (int pid, BackgroundWorker *, Datum))
Then the per-database termination would be a custom routine, defined
also in bgworker.c. Other extension code could define their own
filtering callback routine. Just an idea in passing, to let extension
code take more actions on bgworker slots in use-based on a PGPROC
entry, like a role ID for example, or it could be a different factor.
Feel free to dislike such a funky idea if you do not like it and say
so, of course.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, 2-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-10-21 14:14 Aya Iwata (Fujitsu) <[email protected]>
parent: Peter Smith <[email protected]>
1 sibling, 1 reply; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2025-10-21 14:14 UTC (permalink / raw)
To: 'Peter Smith' <[email protected]>; Michael Paquier <[email protected]>; +Cc: Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
Hi Peter-san, Michael-san,
Thank you for your comments.
I updated patch to v0009. Please review attached patch.
> -----Original Message-----
> From: Peter Smith <[email protected]>
> Sent: Monday, October 20, 2025 11:02 AM
> Some comments for the latest v8 patch.
>
> ======
> src/backend/postmaster/bgworker.c
>
> TerminateBgWorkersByBbOid:
>
> 1.
> +void
> +TerminateBgWorkersByDbOid(Oid oid)
>
> Now the function name is more explicit, but that is not a good reason
> to make the parameter name more vague.
>
> IMO the parameter should still be "dbOid" or "databaseId" instead of
> just "oid". (ditto for the extern in bgworker.h)
I agree with you. I reverted parameter name to databaseId.
> ======
> src/backend/storage/ipc/procarray.c
>
>
> CountOtherDBBackends:
>
> 2.
> + /*
> + * Usually, we try 50 times with 100ms sleep between tries, making 5 sec
> + * total wait. If requested, it would be reduced to 10 times to shorten the
> + * test time.
> + */
>
>
> The comment seemed vague to me. How about more like:
>
> /*
> * Retry up to 50 times with 100ms between attempts (max 5s total).
> * Can be reduced to 10 attempts (max 1s total) to speed up tests.
> */
Thank you. I updated this comment.
> 3.
> + for (tries = 0; tries < ntries; tries++)
>
> 'tries' can be declared as a for-loop variable.
I have declared "int" within the for-loop.
> ~~~
>
> 4.
> Something feels strange about this function name
> (CountOtherDBBackends) which suggests it is just for counting stuff,
> but in reality is more about exiting/terminating the workers. In fact
> retuns a boolean, not a count. Compare this with this similarly named
> "CountUserBackends" which really *is* doing what it says.
>
> Can we give this function a better name, or is that out of scope for this patch?
I think this is out of scope because existing code have terminated autovacuum process by SIGTERM.
It can be discussed separately.
I just added a comment to this function "background workers would also be terminated".
> ======
> src/test/modules/worker_spi/t/002_worker_terminate.pl
>
> 5.
> +# Firstly register an injection point to make the test faster. Normally, it
> +# spends more than 5 seconds because the backend retries, counting the
> number
> +# of connecting processes 50 times, but now the counting would be done only
> 10
> +# times. See CountOtherDBBackends().
> +$node->safe_psql('postgres', "CREATE EXTENSION injection_points;");
> +$node->safe_psql('postgres',
> + "SELECT injection_points_attach('reduce-ncounts', 'error');");
> +
>
> It seemed overkill to give details about what "normally" happens. I
> think it is enough to have a simple comment here:
>
> SUGGESTION
> The injection point 'reduce-ncounts' reduces the number of backend
> retries, allowing for shorter test runs. See CountOtherDBBackends().
Thank you for your suggestion. I updated this comment.
> -----Original Message-----
> From: Michael Paquier <[email protected]>
> Sent: Monday, October 20, 2025 1:33 PM
> On Mon, Oct 20, 2025 at 01:01:31PM +1100, Peter Smith wrote:
> > Some comments for the latest v8 patch.
>
> The comments of Peter apply to comments and parameters. I am not
> going down to these details in this message, these can be infinitely
> tuned.
>
> The injection point integration looks correct. You are checking the
> compile flag and if the extension is available in the installation
> path, which should be enough.
>
> + if (IS_INJECTION_POINT_ATTACHED("reduce-ncounts"))
> + ntries = 10;
>
> 1s is much faster than the default of 5s, still I am wondering if this
> cannot be brought down a bit more. Dropping the worker still around
> after the first test with CREATE DATABASE works here.
Thank you. I updated ntries to 3.
> +# Confirm a background worker is still running
> +$node->safe_psql(
> + "postgres", qq(
> + SELECT count(1) FROM pg_stat_activity
> + WHERE backend_type = 'worker_spi dynamic';));
>
> This does not check that the worker that does not have the flag set is
> still running: you are not feeding the output of this query to an is()
> test.
>
> + is($result, 't', "dynamic bgworker launched");
>
> In launch_bgworker(), this uses the same test description for all the
> callers of this subroutine. Let's prefix it with $testcase.
I added $testcase. Is it same as your expectations?
> +void
> +TerminateBgWorkersByDbOid(Oid oid)
>
> FWIW, while reading this code, I was wondering about one improvement
> that could show benefits for more extension code than only what we are
> discussing here because external code has no access to
> BackgroundWorkerSlot while holding the LWLock BackgroundWorkerLock in
> a single loop, by rewriting this new routine with something like that:
> void TerminateBackgroundWorkerMatchin(
> bool (*do_terminate) (int pid, BackgroundWorker *, Datum))
>
> Then the per-database termination would be a custom routine, defined
> also in bgworker.c. Other extension code could define their own
> filtering callback routine. Just an idea in passing, to let extension
> code take more actions on bgworker slots in use-based on a PGPROC
> entry, like a role ID for example, or it could be a different factor.
> Feel free to dislike such a funky idea if you do not like it and say
> so, of course.
Thank you for your advice.
I'd like to address that, but I couldn't figure out how to do it on my own.
Could you please describe it more?
Regards,
Aya Iwata
Fujitsu Limited
Attachments:
[application/octet-stream] v0009-0001-Allow-background-workers-to-be-terminated.patch (13.8K, 2-v0009-0001-Allow-background-workers-to-be-terminated.patch)
download | inline diff:
From 4616732872339c0a39f0647b9455617f4d3ab719 Mon Sep 17 00:00:00 2001
From: "iwata.aya" <[email protected]>
Date: Thu, 11 Sep 2025 21:16:51 +0900
Subject: [PATCH v0009] Allow background workers to be terminated at DROP
DATABASE
---
doc/src/sgml/bgworker.sgml | 19 +++
src/backend/postmaster/bgworker.c | 40 +++++
src/backend/storage/ipc/procarray.c | 28 +++-
src/include/postmaster/bgworker.h | 8 +
src/test/modules/worker_spi/Makefile | 4 +
src/test/modules/worker_spi/meson.build | 4 +
.../worker_spi/t/002_worker_terminate.pl | 140 ++++++++++++++++++
.../modules/worker_spi/worker_spi--1.0.sql | 3 +-
src/test/modules/worker_spi/worker_spi.c | 5 +
9 files changed, 245 insertions(+), 6 deletions(-)
create mode 100644 src/test/modules/worker_spi/t/002_worker_terminate.pl
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index 2c393385a91..6f4fc57e3d9 100644
--- a/doc/src/sgml/bgworker.sgml
+++ b/doc/src/sgml/bgworker.sgml
@@ -108,6 +108,25 @@ typedef struct BackgroundWorker
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>BGWORKER_EXIT_AT_DATABASE_CHANGE</literal></term>
+ <listitem>
+ <para>
+ <indexterm><primary>BGWORKER_EXIT_AT_DATABASE_CHANGE</primary></indexterm>
+ Requests termination of the background worker when its connected database is
+ dropped, renamed, moved to a different tablespace, or used as a template for
+ <command>CREATE DATABASE</command>. Specifically, the postmaster sends a
+ termination signal when any of these commands affect the worker's database:
+ <command>DROP DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>,
+ <command>ALTER DATABASE SET TABLESPACE</command>, or
+ <command>CREATE DATABASE</command>.
+ Requires both <literal>BGWORKER_SHMEM_ACCESS</literal> and
+ <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 1ad65c237c3..462ac749c76 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -26,6 +26,7 @@
#include "storage/lwlock.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
+#include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/shmem.h"
#include "tcop/tcopprot.h"
@@ -1396,3 +1397,42 @@ GetBackgroundWorkerTypeByPid(pid_t pid)
return result;
}
+
+/*
+ * Terminate all background workers connected to the given database, if they
+ * had requested it.
+ */
+void
+TerminateBgWorkersByDbOid(Oid databaseId)
+{
+ bool signal_postmaster = false;
+
+ LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
+
+ /*
+ * Iterate through slots, looking for workers connected to the given
+ * database.
+ */
+ for (int slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
+ {
+ BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
+
+ if (slot->in_use &&
+ (slot->worker.bgw_flags & BGWORKER_EXIT_AT_DATABASE_CHANGE))
+ {
+ PGPROC *proc = BackendPidGetProc(slot->pid);
+
+ if (proc && proc->databaseId == databaseId)
+ {
+ slot->terminate = true;
+ signal_postmaster = true;
+ }
+ }
+ }
+
+ LWLockRelease(BackgroundWorkerLock);
+
+ /* Make sure the postmaster notices the change to shared memory. */
+ if (signal_postmaster)
+ SendPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE);
+}
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 200f72c6e25..0044f8cbdf6 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -56,11 +56,13 @@
#include "catalog/pg_authid.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/bgworker.h"
#include "port/pg_lfind.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/injection_point.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
@@ -3689,8 +3691,9 @@ CountUserBackends(Oid roleid)
* CountOtherDBBackends -- check for other backends running in the given DB
*
* If there are other backends in the DB, we will wait a maximum of 5 seconds
- * for them to exit. Autovacuum backends are encouraged to exit early by
- * sending them SIGTERM, but normal user backends are just waited for.
+ * for them to exit. Autovacuum backends and background workers are encouraged
+ * to exit early by sending them SIGTERM, but normal user backends are just
+ * waited for.
*
* The current backend is always ignored; it is caller's responsibility to
* check whether the current backend uses the given DB, if it's important.
@@ -3715,10 +3718,19 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
#define MAXAUTOVACPIDS 10 /* max autovacs to SIGTERM per iteration */
int autovac_pids[MAXAUTOVACPIDS];
- int tries;
- /* 50 tries with 100ms sleep between tries makes 5 sec total wait */
- for (tries = 0; tries < 50; tries++)
+ /*
+ * Retry up to 50 times with 100ms between attempts (max 5s total). Can be
+ * reduced to 3 attempts (max 0.3s total) to speed up tests.
+ */
+ int ntries = 50;
+
+#ifdef USE_INJECTION_POINTS
+ if (IS_INJECTION_POINT_ATTACHED("reduce-ncounts"))
+ ntries = 3;
+#endif
+
+ for (int tries = 0; tries < ntries; tries++)
{
int nautovacs = 0;
bool found = false;
@@ -3768,6 +3780,12 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
for (index = 0; index < nautovacs; index++)
(void) kill(autovac_pids[index], SIGTERM); /* ignore any error */
+ /*
+ * Terminate all background workers for this database, if they had
+ * requested it (BGWORKER_EXIT_AT_DATABASE_CHANGE)
+ */
+ TerminateBgWorkersByDbOid(databaseId);
+
/* sleep, then try again */
pg_usleep(100 * 1000L); /* 100ms */
}
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index 058667a47a0..8c69df432a5 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -59,6 +59,13 @@
*/
#define BGWORKER_BACKEND_DATABASE_CONNECTION 0x0002
+/*
+ * Exit the bgworker if its database is involved in a CREATE, ALTER or DROP
+ * database command.
+ * Requires BGWORKER_SHMEM_ACCESS and BGWORKER_BACKEND_DATABASE_CONNECTION.
+ */
+#define BGWORKER_EXIT_AT_DATABASE_CHANGE 0x0004
+
/*
* This class is used internally for parallel queries, to keep track of the
* number of active parallel workers and make sure we never launch more than
@@ -128,6 +135,7 @@ extern const char *GetBackgroundWorkerTypeByPid(pid_t pid);
/* Terminate a bgworker */
extern void TerminateBackgroundWorker(BackgroundWorkerHandle *handle);
+extern void TerminateBgWorkersByDbOid(Oid databaseId);
/* This is valid in a running worker */
extern PGDLLIMPORT BackgroundWorker *MyBgworkerEntry;
diff --git a/src/test/modules/worker_spi/Makefile b/src/test/modules/worker_spi/Makefile
index 024b34cdbb3..e7c5c059e32 100644
--- a/src/test/modules/worker_spi/Makefile
+++ b/src/test/modules/worker_spi/Makefile
@@ -6,6 +6,10 @@ EXTENSION = worker_spi
DATA = worker_spi--1.0.sql
PGFILEDESC = "worker_spi - background worker example"
+EXTRA_INSTALL = src/test/modules/injection_points
+
+export enable_injection_points
+
TAP_TESTS = 1
ifdef USE_PGXS
diff --git a/src/test/modules/worker_spi/meson.build b/src/test/modules/worker_spi/meson.build
index d673ece48a0..5ba66051396 100644
--- a/src/test/modules/worker_spi/meson.build
+++ b/src/test/modules/worker_spi/meson.build
@@ -26,8 +26,12 @@ tests += {
'sd': meson.current_source_dir(),
'bd': meson.current_build_dir(),
'tap': {
+ 'env': {
+ 'enable_injection_points': get_option('injection_points') ? 'yes' : 'no',
+ },
'tests': [
't/001_worker_spi.pl',
+ 't/002_worker_terminate.pl'
],
},
}
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
new file mode 100644
index 00000000000..9671f7e5770
--- /dev/null
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -0,0 +1,140 @@
+# Copyright (c) 2025, PostgreSQL Global Development Group
+
+# Test background workers can be terminated by db commands
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# This test depends on injection points to detect whether background workers
+# remain.
+if ($ENV{enable_injection_points} ne 'yes')
+{
+ plan skip_all => 'Injection points not supported by this build';
+}
+
+# Ensure the worker_spi dynamic worker is launched on the specified database
+sub launch_bgworker
+{
+ my ($node, $database, $testcase, $request_terminate) = @_;
+ my $offset = -s $node->logfile;
+
+ # Launch a background worker on the given database
+ my $result = $node->safe_psql(
+ $database, qq(
+ SELECT worker_spi_launch($testcase, oid, 0, '{}', $request_terminate) IS NOT NULL
+ FROM pg_database WHERE datname = '$database';
+ ));
+ is($result, 't', "dynamic bgworker $testcase launched");
+
+ # Check the worker is surely initialized
+ $node->wait_for_log(
+ qr/LOG: worker_spi dynamic worker $testcase initialized with .*\..*/,
+ $offset);
+}
+
+# Run the given query and verify the background worker can be terminated
+sub run_db_command
+{
+ my ($node, $command, $testname) = @_;
+ my $offset = -s $node->logfile;
+
+ $node->safe_psql('postgres', $command);
+
+ $node->wait_for_log(
+ qr/terminating background worker \"worker_spi dynamic\" due to administrator command/,
+ $offset);
+
+ note("background worker can be terminated at $testname");
+}
+
+my $node = PostgreSQL::Test::Cluster->new('mynode');
+$node->init;
+$node->start;
+
+# Check if the extension injection_points is available, as it may be
+# possible that this script is run with installcheck, where the module
+# would not be installed by default.
+if (!$node->check_extension('injection_points'))
+{
+ plan skip_all => 'Extension injection_points not installed';
+}
+
+$node->safe_psql('postgres', 'CREATE EXTENSION worker_spi;');
+
+# Launch a background worker without BGWORKER_EXIT_AT_DATABASE_CHANGE
+launch_bgworker($node, 'postgres', 0, "false");
+
+# Ensure CREATE DATABASE WITH TEMPLATE fails because background worker retains
+
+# The injection point 'reduce-ncounts' reduces the number of backend
+# retries, allowing for shorter test runs. See CountOtherDBBackends().
+$node->safe_psql('postgres', "CREATE EXTENSION injection_points;");
+$node->safe_psql('postgres',
+ "SELECT injection_points_attach('reduce-ncounts', 'error');");
+
+my $stderr;
+
+$node->psql(
+ 'postgres',
+ "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ stderr => \$stderr);
+ok( $stderr =~
+ "source database \"postgres\" is being accessed by other users",
+ "background worker blocked the database creation");
+
+# Confirm a background worker is still running
+my $result = $node->safe_psql(
+ "postgres", qq(
+ SELECT count(1) FROM pg_stat_activity
+ WHERE backend_type = 'worker_spi dynamic';));
+
+is($result, '1',
+ "background worker is still running after CREATE DATABASE WITH TEMPLATE");
+
+# Terminate the worker for upcoming tests
+$node->safe_psql(
+ "postgres", qq(
+ SELECT pg_terminate_backend(pid)
+ FROM pg_stat_activity WHERE backend_type = 'worker_spi dynamic';));
+
+# The injection point won't be used anymore, release it.
+$node->safe_psql('postgres',
+ "SELECT injection_points_detach('reduce-ncounts');");
+
+# Ensure BGWORKER_EXIT_AT_DATABASE_CHANGE allows background workers to be
+# terminated at some database manipulations.
+#
+# Testcase 1: CREATE DATABASE WITH TEMPLATE
+launch_bgworker($node, 'postgres', 1, "true");
+run_db_command(
+ $node,
+ "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ "CREATE DATABASE WITH TEMPLATE");
+
+# Testcase 2: ALTER DATABASE RENAME
+launch_bgworker($node, 'testdb', 2, "true");
+run_db_command(
+ $node,
+ "ALTER DATABASE testdb RENAME TO renameddb",
+ "ALTER DATABASE RENAME");
+
+# Preparation for the next test; create another tablespace
+my $tablespace = PostgreSQL::Test::Utils::tempdir;
+$node->safe_psql('postgres',
+ "CREATE TABLESPACE test_tablespace LOCATION '$tablespace'");
+
+# Testcase 3: ALTER DATABASE SET TABLESPACE
+launch_bgworker($node, 'renameddb', 3, "true");
+run_db_command(
+ $node,
+ "ALTER DATABASE renameddb SET TABLESPACE test_tablespace",
+ "ALTER DATABASE SET TABLESPACE");
+
+# Testcase 4: DROP DATABASE
+launch_bgworker($node, 'renameddb', 4, "true");
+run_db_command($node, "DROP DATABASE renameddb", "DROP DATABASE");
+
+done_testing();
diff --git a/src/test/modules/worker_spi/worker_spi--1.0.sql b/src/test/modules/worker_spi/worker_spi--1.0.sql
index 84deb6199f6..3d12de37bea 100644
--- a/src/test/modules/worker_spi/worker_spi--1.0.sql
+++ b/src/test/modules/worker_spi/worker_spi--1.0.sql
@@ -7,7 +7,8 @@
CREATE FUNCTION worker_spi_launch(index int4,
dboid oid DEFAULT 0,
roleoid oid DEFAULT 0,
- flags text[] DEFAULT '{}')
+ flags text[] DEFAULT '{}',
+ request_termination boolean DEFAULT false)
RETURNS pg_catalog.int4 STRICT
AS 'MODULE_PATHNAME'
LANGUAGE C;
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index bea8339f464..2912abe6cce 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -404,10 +404,15 @@ worker_spi_launch(PG_FUNCTION_ARGS)
Size ndim;
int nelems;
Datum *datum_flags;
+ bool request_termination = PG_GETARG_BOOL(4);
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
BGWORKER_BACKEND_DATABASE_CONNECTION;
+
+ if (request_termination)
+ worker.bgw_flags |= BGWORKER_EXIT_AT_DATABASE_CHANGE;
+
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART;
sprintf(worker.bgw_library_name, "worker_spi");
--
2.39.3
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-11-20 06:57 Aya Iwata (Fujitsu) <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
0 siblings, 0 replies; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2025-11-20 06:57 UTC (permalink / raw)
To: 'Peter Smith' <[email protected]>; Michael Paquier <[email protected]>; +Cc: Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
Hi
Perhaps you haven't seen my previous post.
> > FWIW, while reading this code, I was wondering about one improvement
> > that could show benefits for more extension code than only what we are
> > discussing here because external code has no access to
> > BackgroundWorkerSlot while holding the LWLock BackgroundWorkerLock in
> > a single loop, by rewriting this new routine with something like that:
> > void TerminateBackgroundWorkerMatchin(
> > bool (*do_terminate) (int pid, BackgroundWorker *, Datum))
> >
> > Then the per-database termination would be a custom routine, defined
> > also in bgworker.c. Other extension code could define their own
> > filtering callback routine. Just an idea in passing, to let extension
> > code take more actions on bgworker slots in use-based on a PGPROC
> > entry, like a role ID for example, or it could be a different factor.
> > Feel free to dislike such a funky idea if you do not like it and say
> > so, of course.
>
> Thank you for your advice.
> I'd like to address that, but I couldn't figure out how to do it on my own.
> Could you please describe it more?
I'd like to adapt the patch for this, so could you tell me with the details?
Regards,
Aya Iwata
Fujitsu Limited
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-12-14 07:40 Pavel Stehule <[email protected]>
parent: Michael Paquier <[email protected]>
1 sibling, 1 reply; 67+ messages in thread
From: Pavel Stehule @ 2025-12-14 07:40 UTC (permalink / raw)
To: Michael Paquier <[email protected]>; +Cc: Aya Iwata (Fujitsu) <[email protected]>; Peter Smith <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
Hi
ne 14. 12. 2025 v 8:26 odesílatel Michael Paquier <[email protected]>
napsal:
> On Wed, Oct 15, 2025 at 02:48:43AM +0000, Aya Iwata (Fujitsu) wrote:
> > Thank you for your comments. I updated this patch to v0007.
>
> + * Exit the bgworker when its database is dropped, renamed, moved to a
> + * different tablespace, or used as a template for CREATE DATABASE.
>
> I don't think that we need to list all these operations in details
> here. We could just say "if its database is involved in a CREATE,
> ALTER or DROP database command". The docs should provide these
> details, of course.
>
> +#define BGWORKER_EXIT_AT_DATABASE_CHANGE 0x0004
>
>
I am checking this patch, and I think so used names can be little bit
confusing
BGWORKER_EXIT_AT_DATABASE_CHANGE - it is used for disconnecting workers on
the template database, and this database is not changing.
TerminateBgWorkersByDbOid - it doesn't terminate all workers, but only
workers with some special flags
Maybe BGWORKER_INTERRUPTABLE and TerminateInterruptableBgWorkersByDbOid ?
Another question is if this cancellation should be implicit and should not
require some special flag.
When I want to disconnect connections to database when I do drop, I have to
use FORCE flag
So maybe there should be ALTER DATABASE ... RENAME ... FORCE - or if FORCE
can terminare all workers (without special FLAG) ?
Regards
Pavel
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-12-15 12:56 Aya Iwata (Fujitsu) <[email protected]>
parent: Pavel Stehule <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2025-12-15 12:56 UTC (permalink / raw)
To: 'Pavel Stehule' <[email protected]>; Michael Paquier <[email protected]>; +Cc: Peter Smith <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
Hi
Thank you for your review.
> From: Pavel Stehule <[email protected]>
> Sent: Sunday, December 14, 2025 4:40 PM
> To: Michael Paquier <[email protected]>
> Cc: Iwata, Aya/岩田 彩 <[email protected]>; Peter Smith <[email protected]>; Chao Li <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; pgsql-hackers <[email protected]>
> Subject: Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
>
> +#define BGWORKER_EXIT_AT_DATABASE_CHANGE 0x0004
>
> I am checking this patch, and I think so used names can be little bit confusing
>
> BGWORKER_EXIT_AT_DATABASE_CHANGE - it is used for disconnecting workers on the template database, and this database is not changing.
>
> TerminateBgWorkersByDbOid - it doesn't terminate all workers, but only workers with some special flags
>
> Maybe BGWORKER_INTERRUPTABLE and TerminateInterruptableBgWorkersByDbOid ?
Thank you for your advice.
I changed the name of a function and a flag.
> Another question is if this cancellation should be implicit and should not require some special flag.
>
> When I want to disconnect connections to database when I do drop, I have to use FORCE flag
>
> So maybe there should be ALTER DATABASE ... RENAME ... FORCE - or if FORCE can terminare all workers (without special FLAG) ?
For the proposed feature, we've added a flag allowing each extension developer to decide whether to terminate it via DROP/ALTER DATABASE.
Adding a FORCE option to ALTER to let database definition modifiers decide whether to force termination of background workers might be better discussed in a separate thread.
Best Regards,
Aya Iwata
Attachments:
[application/octet-stream] v0010-0001-Allow-background-workers-to-be-terminated.patch (13.8K, 2-v0010-0001-Allow-background-workers-to-be-terminated.patch)
download | inline diff:
From e1751b849e555c1e67a2b51a588157bfff1c79a3 Mon Sep 17 00:00:00 2001
From: "iwata.aya" <[email protected]>
Date: Thu, 11 Sep 2025 21:16:51 +0900
Subject: [PATCH v0010] Allow background workers to be terminated at DROP
DATABASE
---
doc/src/sgml/bgworker.sgml | 19 +++
src/backend/postmaster/bgworker.c | 40 +++++
src/backend/storage/ipc/procarray.c | 28 +++-
src/include/postmaster/bgworker.h | 8 +
src/test/modules/worker_spi/Makefile | 4 +
src/test/modules/worker_spi/meson.build | 4 +
.../worker_spi/t/002_worker_terminate.pl | 140 ++++++++++++++++++
.../modules/worker_spi/worker_spi--1.0.sql | 3 +-
src/test/modules/worker_spi/worker_spi.c | 5 +
9 files changed, 245 insertions(+), 6 deletions(-)
create mode 100644 src/test/modules/worker_spi/t/002_worker_terminate.pl
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index 2c393385a91..a7c238750fc 100644
--- a/doc/src/sgml/bgworker.sgml
+++ b/doc/src/sgml/bgworker.sgml
@@ -108,6 +108,25 @@ typedef struct BackgroundWorker
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>BGWORKER_INTERRUPTABLE</literal></term>
+ <listitem>
+ <para>
+ <indexterm><primary>BGWORKER_INTERRUPTABLE</primary></indexterm>
+ Requests termination of the background worker when its connected database is
+ dropped, renamed, moved to a different tablespace, or used as a template for
+ <command>CREATE DATABASE</command>. Specifically, the postmaster sends a
+ termination signal when any of these commands affect the worker's database:
+ <command>DROP DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>,
+ <command>ALTER DATABASE SET TABLESPACE</command>, or
+ <command>CREATE DATABASE</command>.
+ Requires both <literal>BGWORKER_SHMEM_ACCESS</literal> and
+ <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 8e1068969ae..8334b75548c 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -26,6 +26,7 @@
#include "storage/lwlock.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
+#include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/shmem.h"
#include "tcop/tcopprot.h"
@@ -1399,3 +1400,42 @@ GetBackgroundWorkerTypeByPid(pid_t pid)
return result;
}
+
+/*
+ * Terminate all background workers connected to the given database, if they
+ * had requested it.
+ */
+void
+TerminateInterruptableBgWorkersByDbOid(Oid databaseId)
+{
+ bool signal_postmaster = false;
+
+ LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
+
+ /*
+ * Iterate through slots, looking for workers connected to the given
+ * database.
+ */
+ for (int slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
+ {
+ BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
+
+ if (slot->in_use &&
+ (slot->worker.bgw_flags & BGWORKER_INTERRUPTABLE))
+ {
+ PGPROC *proc = BackendPidGetProc(slot->pid);
+
+ if (proc && proc->databaseId == databaseId)
+ {
+ slot->terminate = true;
+ signal_postmaster = true;
+ }
+ }
+ }
+
+ LWLockRelease(BackgroundWorkerLock);
+
+ /* Make sure the postmaster notices the change to shared memory. */
+ if (signal_postmaster)
+ SendPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE);
+}
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index f3a1603204e..de80422caea 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -56,11 +56,13 @@
#include "catalog/pg_authid.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/bgworker.h"
#include "port/pg_lfind.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/injection_point.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
@@ -3687,8 +3689,9 @@ CountUserBackends(Oid roleid)
* CountOtherDBBackends -- check for other backends running in the given DB
*
* If there are other backends in the DB, we will wait a maximum of 5 seconds
- * for them to exit. Autovacuum backends are encouraged to exit early by
- * sending them SIGTERM, but normal user backends are just waited for.
+ * for them to exit. Autovacuum backends and background workers are encouraged
+ * to exit early by sending them SIGTERM, but normal user backends are just
+ * waited for.
*
* The current backend is always ignored; it is caller's responsibility to
* check whether the current backend uses the given DB, if it's important.
@@ -3713,10 +3716,19 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
#define MAXAUTOVACPIDS 10 /* max autovacs to SIGTERM per iteration */
int autovac_pids[MAXAUTOVACPIDS];
- int tries;
- /* 50 tries with 100ms sleep between tries makes 5 sec total wait */
- for (tries = 0; tries < 50; tries++)
+ /*
+ * Retry up to 50 times with 100ms between attempts (max 5s total). Can be
+ * reduced to 3 attempts (max 0.3s total) to speed up tests.
+ */
+ int ntries = 50;
+
+#ifdef USE_INJECTION_POINTS
+ if (IS_INJECTION_POINT_ATTACHED("reduce-ncounts"))
+ ntries = 3;
+#endif
+
+ for (int tries = 0; tries < ntries; tries++)
{
int nautovacs = 0;
bool found = false;
@@ -3766,6 +3778,12 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
for (index = 0; index < nautovacs; index++)
(void) kill(autovac_pids[index], SIGTERM); /* ignore any error */
+ /*
+ * Terminate all background workers for this database, if they had
+ * requested it (BGWORKER_INTERRUPTABLE)
+ */
+ TerminateInterruptableBgWorkersByDbOid(databaseId);
+
/* sleep, then try again */
pg_usleep(100 * 1000L); /* 100ms */
}
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index 058667a47a0..e43881448d5 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -59,6 +59,13 @@
*/
#define BGWORKER_BACKEND_DATABASE_CONNECTION 0x0002
+/*
+ * Exit the bgworker if its database is involved in a CREATE, ALTER or DROP
+ * database command.
+ * Requires BGWORKER_SHMEM_ACCESS and BGWORKER_BACKEND_DATABASE_CONNECTION.
+ */
+#define BGWORKER_INTERRUPTABLE 0x0004
+
/*
* This class is used internally for parallel queries, to keep track of the
* number of active parallel workers and make sure we never launch more than
@@ -128,6 +135,7 @@ extern const char *GetBackgroundWorkerTypeByPid(pid_t pid);
/* Terminate a bgworker */
extern void TerminateBackgroundWorker(BackgroundWorkerHandle *handle);
+extern void TerminateInterruptableBgWorkersByDbOid(Oid databaseId);
/* This is valid in a running worker */
extern PGDLLIMPORT BackgroundWorker *MyBgworkerEntry;
diff --git a/src/test/modules/worker_spi/Makefile b/src/test/modules/worker_spi/Makefile
index 024b34cdbb3..e7c5c059e32 100644
--- a/src/test/modules/worker_spi/Makefile
+++ b/src/test/modules/worker_spi/Makefile
@@ -6,6 +6,10 @@ EXTENSION = worker_spi
DATA = worker_spi--1.0.sql
PGFILEDESC = "worker_spi - background worker example"
+EXTRA_INSTALL = src/test/modules/injection_points
+
+export enable_injection_points
+
TAP_TESTS = 1
ifdef USE_PGXS
diff --git a/src/test/modules/worker_spi/meson.build b/src/test/modules/worker_spi/meson.build
index d673ece48a0..5ba66051396 100644
--- a/src/test/modules/worker_spi/meson.build
+++ b/src/test/modules/worker_spi/meson.build
@@ -26,8 +26,12 @@ tests += {
'sd': meson.current_source_dir(),
'bd': meson.current_build_dir(),
'tap': {
+ 'env': {
+ 'enable_injection_points': get_option('injection_points') ? 'yes' : 'no',
+ },
'tests': [
't/001_worker_spi.pl',
+ 't/002_worker_terminate.pl'
],
},
}
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
new file mode 100644
index 00000000000..5467e423fd9
--- /dev/null
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -0,0 +1,140 @@
+# Copyright (c) 2025, PostgreSQL Global Development Group
+
+# Test background workers can be terminated by db commands
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# This test depends on injection points to detect whether background workers
+# remain.
+if ($ENV{enable_injection_points} ne 'yes')
+{
+ plan skip_all => 'Injection points not supported by this build';
+}
+
+# Ensure the worker_spi dynamic worker is launched on the specified database
+sub launch_bgworker
+{
+ my ($node, $database, $testcase, $request_terminate) = @_;
+ my $offset = -s $node->logfile;
+
+ # Launch a background worker on the given database
+ my $result = $node->safe_psql(
+ $database, qq(
+ SELECT worker_spi_launch($testcase, oid, 0, '{}', $request_terminate) IS NOT NULL
+ FROM pg_database WHERE datname = '$database';
+ ));
+ is($result, 't', "dynamic bgworker $testcase launched");
+
+ # Check the worker is surely initialized
+ $node->wait_for_log(
+ qr/LOG: worker_spi dynamic worker $testcase initialized with .*\..*/,
+ $offset);
+}
+
+# Run the given query and verify the background worker can be terminated
+sub run_db_command
+{
+ my ($node, $command, $testname) = @_;
+ my $offset = -s $node->logfile;
+
+ $node->safe_psql('postgres', $command);
+
+ $node->wait_for_log(
+ qr/terminating background worker \"worker_spi dynamic\" due to administrator command/,
+ $offset);
+
+ note("background worker can be terminated at $testname");
+}
+
+my $node = PostgreSQL::Test::Cluster->new('mynode');
+$node->init;
+$node->start;
+
+# Check if the extension injection_points is available, as it may be
+# possible that this script is run with installcheck, where the module
+# would not be installed by default.
+if (!$node->check_extension('injection_points'))
+{
+ plan skip_all => 'Extension injection_points not installed';
+}
+
+$node->safe_psql('postgres', 'CREATE EXTENSION worker_spi;');
+
+# Launch a background worker without BGWORKER_INTERRUPTABLE
+launch_bgworker($node, 'postgres', 0, "false");
+
+# Ensure CREATE DATABASE WITH TEMPLATE fails because background worker retains
+
+# The injection point 'reduce-ncounts' reduces the number of backend
+# retries, allowing for shorter test runs. See CountOtherDBBackends().
+$node->safe_psql('postgres', "CREATE EXTENSION injection_points;");
+$node->safe_psql('postgres',
+ "SELECT injection_points_attach('reduce-ncounts', 'error');");
+
+my $stderr;
+
+$node->psql(
+ 'postgres',
+ "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ stderr => \$stderr);
+ok( $stderr =~
+ "source database \"postgres\" is being accessed by other users",
+ "background worker blocked the database creation");
+
+# Confirm a background worker is still running
+my $result = $node->safe_psql(
+ "postgres", qq(
+ SELECT count(1) FROM pg_stat_activity
+ WHERE backend_type = 'worker_spi dynamic';));
+
+is($result, '1',
+ "background worker is still running after CREATE DATABASE WITH TEMPLATE");
+
+# Terminate the worker for upcoming tests
+$node->safe_psql(
+ "postgres", qq(
+ SELECT pg_terminate_backend(pid)
+ FROM pg_stat_activity WHERE backend_type = 'worker_spi dynamic';));
+
+# The injection point won't be used anymore, release it.
+$node->safe_psql('postgres',
+ "SELECT injection_points_detach('reduce-ncounts');");
+
+# Ensure BGWORKER_INTERRUPTABLE allows background workers to be
+# terminated at some database manipulations.
+#
+# Testcase 1: CREATE DATABASE WITH TEMPLATE
+launch_bgworker($node, 'postgres', 1, "true");
+run_db_command(
+ $node,
+ "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ "CREATE DATABASE WITH TEMPLATE");
+
+# Testcase 2: ALTER DATABASE RENAME
+launch_bgworker($node, 'testdb', 2, "true");
+run_db_command(
+ $node,
+ "ALTER DATABASE testdb RENAME TO renameddb",
+ "ALTER DATABASE RENAME");
+
+# Preparation for the next test; create another tablespace
+my $tablespace = PostgreSQL::Test::Utils::tempdir;
+$node->safe_psql('postgres',
+ "CREATE TABLESPACE test_tablespace LOCATION '$tablespace'");
+
+# Testcase 3: ALTER DATABASE SET TABLESPACE
+launch_bgworker($node, 'renameddb', 3, "true");
+run_db_command(
+ $node,
+ "ALTER DATABASE renameddb SET TABLESPACE test_tablespace",
+ "ALTER DATABASE SET TABLESPACE");
+
+# Testcase 4: DROP DATABASE
+launch_bgworker($node, 'renameddb', 4, "true");
+run_db_command($node, "DROP DATABASE renameddb", "DROP DATABASE");
+
+done_testing();
diff --git a/src/test/modules/worker_spi/worker_spi--1.0.sql b/src/test/modules/worker_spi/worker_spi--1.0.sql
index 84deb6199f6..3d12de37bea 100644
--- a/src/test/modules/worker_spi/worker_spi--1.0.sql
+++ b/src/test/modules/worker_spi/worker_spi--1.0.sql
@@ -7,7 +7,8 @@
CREATE FUNCTION worker_spi_launch(index int4,
dboid oid DEFAULT 0,
roleoid oid DEFAULT 0,
- flags text[] DEFAULT '{}')
+ flags text[] DEFAULT '{}',
+ request_termination boolean DEFAULT false)
RETURNS pg_catalog.int4 STRICT
AS 'MODULE_PATHNAME'
LANGUAGE C;
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 1a21b8c8876..50d16aaf9a8 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -404,10 +404,15 @@ worker_spi_launch(PG_FUNCTION_ARGS)
Size ndim;
int nelems;
Datum *datum_flags;
+ bool request_termination = PG_GETARG_BOOL(4);
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
BGWORKER_BACKEND_DATABASE_CONNECTION;
+
+ if (request_termination)
+ worker.bgw_flags |= BGWORKER_INTERRUPTABLE;
+
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART;
sprintf(worker.bgw_library_name, "worker_spi");
--
2.39.3
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-12-15 13:18 Pavel Stehule <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Pavel Stehule @ 2025-12-15 13:18 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: Michael Paquier <[email protected]>; Peter Smith <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
po 15. 12. 2025 v 13:56 odesílatel Aya Iwata (Fujitsu) <
[email protected]> napsal:
> Hi
>
> Thank you for your review.
>
> > From: Pavel Stehule <[email protected]>
> > Sent: Sunday, December 14, 2025 4:40 PM
> > To: Michael Paquier <[email protected]>
> > Cc: Iwata, Aya/岩田 彩 <[email protected]>; Peter Smith <
> [email protected]>; Chao Li <[email protected]>; Kuroda,
> Hayato/黒田 隼人 <[email protected]>; pgsql-hackers <
> [email protected]>
> > Subject: Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP
> DATABASE
> >
> > +#define BGWORKER_EXIT_AT_DATABASE_CHANGE 0x0004
> >
> > I am checking this patch, and I think so used names can be little bit
> confusing
> >
> > BGWORKER_EXIT_AT_DATABASE_CHANGE - it is used for disconnecting workers
> on the template database, and this database is not changing.
> >
> > TerminateBgWorkersByDbOid - it doesn't terminate all workers, but only
> workers with some special flags
> >
> > Maybe BGWORKER_INTERRUPTABLE and TerminateInterruptableBgWorkersByDbOid ?
>
> Thank you for your advice.
> I changed the name of a function and a flag.
>
> > Another question is if this cancellation should be implicit and should
> not require some special flag.
> >
> > When I want to disconnect connections to database when I do drop, I have
> to use FORCE flag
> >
> > So maybe there should be ALTER DATABASE ... RENAME ... FORCE - or if
> FORCE can terminare all workers (without special FLAG) ?
>
> For the proposed feature, we've added a flag allowing each extension
> developer to decide whether to terminate it via DROP/ALTER DATABASE.
> Adding a FORCE option to ALTER to let database definition modifiers decide
> whether to force termination of background workers might be better
> discussed in a separate thread.
>
When I thought about it - there can be a second alternative.
Introduce a pair of flags BGWORKER_INTERRUPTABLE and BGWORKER_PROTECTED
(the names can be enhanced or changed). BGWORKER_INTERRUPTABLE can be
default.
ALTER DATABASE RENAME and related commands can stop any non protected
workers. ALTER DATABASE RENAME FORCE can stop any workers (including
protected).
Is there any reason why BGWORKER_INTERRUPTABLE cannot be default? Probably
nobody would block some possibly common operations on database level
without strong reason.
Regards
Pavel
> Best Regards,
> Aya Iwata
>
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-12-17 13:31 Aya Iwata (Fujitsu) <[email protected]>
parent: Pavel Stehule <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2025-12-17 13:31 UTC (permalink / raw)
To: 'Pavel Stehule' <[email protected]>; +Cc: Michael Paquier <[email protected]>; Peter Smith <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
Hi Pavel-san,
>> So maybe there should be ALTER DATABASE ... RENAME ... FORCE - or if FORCE can terminare all workers (without special FLAG) ?
>
> For the proposed feature, we've added a flag allowing each extension developer to decide whether to terminate it via DROP/ALTER DATABASE.
> Adding a FORCE option to ALTER to let database definition modifiers decide whether to force termination of background workers might be better discussed in a separate thread.
>
> When I thought about it - there can be a second alternative.
>
> Introduce a pair of flags BGWORKER_INTERRUPTABLE and BGWORKER_PROTECTED (the names can be enhanced or changed). BGWORKER_INTERRUPTABLE can be default.
> ALTER DATABASE RENAME and related commands can stop any non protected workers. ALTER DATABASE RENAME FORCE can stop any workers (including protected).
I can't image any use cases for BGWORKER_PROTECTED. Do you have any idea?
Also, I think the parameter settings might get a complicated.
If we start discussing the "FORCE" option, it is better to think about this parameter.
> Is there any reason why BGWORKER_INTERRUPTABLE cannot be default? Probably nobody would block some possibly common operations on database level without strong reason.
As Michael-san mentioned in a previous email, this behavior has remained unchanged since bgworkers were introduced in v9.3.
I don't see a compelling reason to alter it now. Additionally, this specification can be modified later.
Best Regards,
Aya Iwata
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-12-17 15:33 Pavel Stehule <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Pavel Stehule @ 2025-12-17 15:33 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: Michael Paquier <[email protected]>; Peter Smith <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
Hi
st 17. 12. 2025 v 14:31 odesílatel Aya Iwata (Fujitsu) <
[email protected]> napsal:
> Hi Pavel-san,
>
> >> So maybe there should be ALTER DATABASE ... RENAME ... FORCE - or if
> FORCE can terminare all workers (without special FLAG) ?
> >
> > For the proposed feature, we've added a flag allowing each extension
> developer to decide whether to terminate it via DROP/ALTER DATABASE.
> > Adding a FORCE option to ALTER to let database definition modifiers
> decide whether to force termination of background workers might be better
> discussed in a separate thread.
> >
> > When I thought about it - there can be a second alternative.
> >
> > Introduce a pair of flags BGWORKER_INTERRUPTABLE and BGWORKER_PROTECTED
> (the names can be enhanced or changed). BGWORKER_INTERRUPTABLE can be
> default.
> > ALTER DATABASE RENAME and related commands can stop any non protected
> workers. ALTER DATABASE RENAME FORCE can stop any workers (including
> protected).
>
> I can't image any use cases for BGWORKER_PROTECTED. Do you have any idea?
> Also, I think the parameter settings might get a complicated.
> If we start discussing the "FORCE" option, it is better to think about
> this parameter.
>
> > Is there any reason why BGWORKER_INTERRUPTABLE cannot be default?
> Probably nobody would block some possibly common operations on database
> level without strong reason.
>
> As Michael-san mentioned in a previous email, this behavior has remained
> unchanged since bgworkers were introduced in v9.3.
> I don't see a compelling reason to alter it now. Additionally, this
> specification can be modified later.
>
I understand the request for unchanging behaviour - but I am not sure if
this concept is really helpful - or if the naming is best. I am afraid so
this feature without changing the workers code is useless (and maybe it is
wanted).
Any worker should be interruptable by sigterm. And then the
name BGWORKER_INTERRUPTABLE is little bit vague. Maybe some
like BGWORKER_CAREFREE_INTERRUPTABLE can be better (or some like this -
maybe BGWORKER_CANCELABLE)? This can be a signal from bgworker's authors -
it is ok to kill the worker anytime when it is necessary.
Some workers can have the flag BGW_NEVER_RESTART - cannot be used as signal
so this worker is protected, and others can be terminated safely, because
they will be restarted after 60 seconds?
Regards
Pavel
>
> Best Regards,
> Aya Iwata
>
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-12-18 11:47 Aya Iwata (Fujitsu) <[email protected]>
parent: Pavel Stehule <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2025-12-18 11:47 UTC (permalink / raw)
To: 'Pavel Stehule' <[email protected]>; +Cc: Michael Paquier <[email protected]>; Peter Smith <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
Hi Pavel-san
Thank you for your feedback!
> From: Pavel Stehule <[email protected]>
> Sent: Thursday, December 18, 2025 12:33 AM
> To: Iwata, Aya/岩田 彩 <[email protected]>
> Cc: Michael Paquier <[email protected]>; Peter Smith <[email protected]>; Chao Li <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; pgsql-hackers <[email protected]>
> Subject: Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
>> Hi Pavel-san,
>>
>> >> So maybe there should be ALTER DATABASE ... RENAME ... FORCE - or if FORCE can terminare all workers (without special FLAG) ?
>> >
>> > For the proposed feature, we've added a flag allowing each extension developer to decide whether to terminate it via DROP/ALTER DATABASE.
>> > Adding a FORCE option to ALTER to let database definition modifiers decide whether to force termination of background workers might be better discussed in a separate thread.
>> >
>> > When I thought about it - there can be a second alternative.
>> >
>> > Introduce a pair of flags BGWORKER_INTERRUPTABLE and BGWORKER_PROTECTED (the names can be enhanced or changed). BGWORKER_INTERRUPTABLE can be default.
>> > ALTER DATABASE RENAME and related commands can stop any non protected workers. ALTER DATABASE RENAME FORCE can stop any workers (including protected).
>>
>> I can't image any use cases for BGWORKER_PROTECTED. Do you have any idea?
>> Also, I think the parameter settings might get a complicated.
>> If we start discussing the "FORCE" option, it is better to think about this parameter.
>>
>> > Is there any reason why BGWORKER_INTERRUPTABLE cannot be default? Probably nobody would block some possibly common operations on database level without strong reason.
>>
>> As Michael-san mentioned in a previous email, this behavior has remained unchanged since bgworkers were introduced in v9.3.
>> I don't see a compelling reason to alter it now. Additionally, this specification can be modified later.
> I understand the request for unchanging behaviour - but I am not sure if this concept is really helpful - or if the naming is best. I am afraid so this feature without changing the workers code is useless (and maybe it is wanted).
It is our intention; this feature would enable when developer expressly set.
> Any worker should be interruptable by sigterm. And then the name BGWORKER_INTERRUPTABLE is little bit vague. Maybe some like BGWORKER_CAREFREE_INTERRUPTABLE can be better (or some like this - maybe BGWORKER_CANCELABLE)? This can be a signal from bgworker's authors - it is ok to kill the worker anytime when it is necessary.
That's right, "interruptable" may not be appropriate. This is because even bgworkers without this flag set can be interrupted by sigterm.
Hmm, I feel these ideas may not be clear what it does. Do someone have other idea?
> Some workers can have the flag BGW_NEVER_RESTART - cannot be used as signal so this worker is protected, and others can be terminated safely, because they will be restarted after 60 seconds?
In my understanding, you are suggesting that the bgworker which set bgw_restart_time as BGW_NEVER_RESTART is not terminated by DROP/ALTER, right?
Do you think what kind of use cases might there be?
Best Regards,
Aya Iwata
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-12-18 16:04 Pavel Stehule <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Pavel Stehule @ 2025-12-18 16:04 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: Michael Paquier <[email protected]>; Peter Smith <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
čt 18. 12. 2025 v 12:47 odesílatel Aya Iwata (Fujitsu) <
[email protected]> napsal:
> Hi Pavel-san
>
> Thank you for your feedback!
>
> > From: Pavel Stehule <[email protected]>
> > Sent: Thursday, December 18, 2025 12:33 AM
> > To: Iwata, Aya/岩田 彩 <[email protected]>
> > Cc: Michael Paquier <[email protected]>; Peter Smith <
> [email protected]>; Chao Li <[email protected]>; Kuroda,
> Hayato/黒田 隼人 <[email protected]>; pgsql-hackers <
> [email protected]>
> > Subject: Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP
> DATABASE
> >> Hi Pavel-san,
> >>
> >> >> So maybe there should be ALTER DATABASE ... RENAME ... FORCE - or if
> FORCE can terminare all workers (without special FLAG) ?
> >> >
> >> > For the proposed feature, we've added a flag allowing each extension
> developer to decide whether to terminate it via DROP/ALTER DATABASE.
> >> > Adding a FORCE option to ALTER to let database definition modifiers
> decide whether to force termination of background workers might be better
> discussed in a separate thread.
> >> >
> >> > When I thought about it - there can be a second alternative.
> >> >
> >> > Introduce a pair of flags BGWORKER_INTERRUPTABLE and
> BGWORKER_PROTECTED (the names can be enhanced or changed).
> BGWORKER_INTERRUPTABLE can be default.
> >> > ALTER DATABASE RENAME and related commands can stop any non protected
> workers. ALTER DATABASE RENAME FORCE can stop any workers (including
> protected).
> >>
> >> I can't image any use cases for BGWORKER_PROTECTED. Do you have any
> idea?
> >> Also, I think the parameter settings might get a complicated.
> >> If we start discussing the "FORCE" option, it is better to think about
> this parameter.
> >>
> >> > Is there any reason why BGWORKER_INTERRUPTABLE cannot be default?
> Probably nobody would block some possibly common operations on database
> level without strong reason.
> >>
> >> As Michael-san mentioned in a previous email, this behavior has
> remained unchanged since bgworkers were introduced in v9.3.
> >> I don't see a compelling reason to alter it now. Additionally, this
> specification can be modified later.
>
> > I understand the request for unchanging behaviour - but I am not sure if
> this concept is really helpful - or if the naming is best. I am afraid so
> this feature without changing the workers code is useless (and maybe it is
> wanted).
>
> It is our intention; this feature would enable when developer expressly
> set.
>
> > Any worker should be interruptable by sigterm. And then the name
> BGWORKER_INTERRUPTABLE is little bit vague. Maybe some like
> BGWORKER_CAREFREE_INTERRUPTABLE can be better (or some like this - maybe
> BGWORKER_CANCELABLE)? This can be a signal from bgworker's authors - it is
> ok to kill the worker anytime when it is necessary.
>
> That's right, "interruptable" may not be appropriate. This is because even
> bgworkers without this flag set can be interrupted by sigterm.
> Hmm, I feel these ideas may not be clear what it does. Do someone have
> other idea?
>
> > Some workers can have the flag BGW_NEVER_RESTART - cannot be used as
> signal so this worker is protected, and others can be terminated safely,
> because they will be restarted after 60 seconds?
>
> In my understanding, you are suggesting that the bgworker which set
> bgw_restart_time as BGW_NEVER_RESTART is not terminated by DROP/ALTER,
> right?
> Do you think what kind of use cases might there be?
>
I don't think about use cases - just workers that can be restarted can be
terminated with less risk than workers that shouldn't be restarted - and
maybe BGWORKER_CAREFREE_INTERRUPTABLE is just negation of BGW_NEVER_RESTART
in almost cases.
I afraid so BGWORKER_CAREFREE_INTERRUPTABLE (or
maybe BGWORKER_SAFELY_INTERRUPTABLE) will be source of bugs - this flag is
just "safeguard" for very special cases - and very common bug will be
omission of this flug until someone reports (after long time) "it block
database rename".
Second objection against this design is inconsistency with current CREATE,
DROP DATABASE commands. Without the FORCE flag, it terminates nothing. And
with the FORCE flag it terminates anything that is necessary. Proposed
design is safe - because only specially marked bgworkers are terminated,
but for users it can be unwanted surprise still - and at the end in next
versions - mostly bgworkers will have this flag, because nobody want to
block DDL
Regards
Pavel
> Best Regards,
> Aya Iwata
>
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-12-24 08:42 Aya Iwata (Fujitsu) <[email protected]>
parent: Michael Paquier <[email protected]>
0 siblings, 0 replies; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2025-12-24 08:42 UTC (permalink / raw)
To: 'Michael Paquier' <[email protected]>; +Cc: Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers; Peter Smith <[email protected]>
Hi
> FWIW, while reading this code, I was wondering about one improvement
> that could show benefits for more extension code than only what we are
> discussing here because external code has no access to
> BackgroundWorkerSlot while holding the LWLock BackgroundWorkerLock in
> a single loop, by rewriting this new routine with something like that:
> void TerminateBackgroundWorkerMatchin(
> bool (*do_terminate) (int pid, BackgroundWorker *, Datum))
>
> Then the per-database termination would be a custom routine, defined
> also in bgworker.c. Other extension code could define their own
> filtering callback routine. Just an idea in passing, to let extension
> code take more actions on bgworker slots in use-based on a PGPROC
> entry, like a role ID for example, or it could be a different factor.
> Feel free to dislike such a funky idea if you do not like it and say
> so, of course.
I'm sorry for the delayed response.
I tried implementing a callback function within the for loop to allow setting the termination
condition myself. However, I felt this method had few use-cases. Since LWlock exists in here, it can
only handle lightweight operations.
Therefore, I'd prefer not to include it at this time. If I later think of a useful scenario for it, I'll
reconsider about this.
Best Regards,
Aya Iwata
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-12-25 13:24 Aya Iwata (Fujitsu) <[email protected]>
parent: Pavel Stehule <[email protected]>
0 siblings, 2 replies; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2025-12-25 13:24 UTC (permalink / raw)
To: 'Pavel Stehule' <[email protected]>; Michael Paquier <[email protected]>; +Cc: Peter Smith <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
Hi
https://www.postgresql.org/message-id/aPBoXXW3XuwiIsHG%40paquier.xyz
Based on previous discussions, we had configured DROP/ALTER DATABASE to
terminate only background workers with specific parameters set.
To reconsider the default behavior, I created the following patches:
0001 is the patch for the existing implementation.
0002 is the patch modified based on Pavel-san's suggestion.
The 0002 patch changes the default behavior to terminate background workers
when DROP/ALTER DATABASE is executed. It also includes a test set
for this change.
For background workers that should not be terminated, setting
the BGWORKER_PROTECTED parameter prevents this.
The relationship with the FORCE option during DROP DATABASE is as follows:
FORCE option present, flag present... TERMINATE
FORCE option present, flag absent... TERMINATE
FORCE option absent, flag present... Does not TERMINATE due to the flag
FORCE option absent, flag absent... TERMINATE
I would like to know your comments on which implementation is preferable.
Regards,
Aya Iwata
Attachments:
[application/octet-stream] v0011-0001-Allow-background-workers-to-be-terminated.patch (13.8K, 2-v0011-0001-Allow-background-workers-to-be-terminated.patch)
download | inline diff:
From f4d30412bd9588051a434a80404b077096a4cc98 Mon Sep 17 00:00:00 2001
From: "iwata.aya" <[email protected]>
Date: Thu, 11 Sep 2025 21:16:51 +0900
Subject: [PATCH v0011 1/2] Allow background workers to be terminated at DROP
DATABASE
---
doc/src/sgml/bgworker.sgml | 19 +++
src/backend/postmaster/bgworker.c | 40 +++++
src/backend/storage/ipc/procarray.c | 28 +++-
src/include/postmaster/bgworker.h | 8 +
src/test/modules/worker_spi/Makefile | 4 +
src/test/modules/worker_spi/meson.build | 4 +
.../worker_spi/t/002_worker_terminate.pl | 140 ++++++++++++++++++
.../modules/worker_spi/worker_spi--1.0.sql | 3 +-
src/test/modules/worker_spi/worker_spi.c | 5 +
9 files changed, 245 insertions(+), 6 deletions(-)
create mode 100644 src/test/modules/worker_spi/t/002_worker_terminate.pl
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index 2c393385a91..a7c238750fc 100644
--- a/doc/src/sgml/bgworker.sgml
+++ b/doc/src/sgml/bgworker.sgml
@@ -108,6 +108,25 @@ typedef struct BackgroundWorker
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>BGWORKER_INTERRUPTABLE</literal></term>
+ <listitem>
+ <para>
+ <indexterm><primary>BGWORKER_INTERRUPTABLE</primary></indexterm>
+ Requests termination of the background worker when its connected database is
+ dropped, renamed, moved to a different tablespace, or used as a template for
+ <command>CREATE DATABASE</command>. Specifically, the postmaster sends a
+ termination signal when any of these commands affect the worker's database:
+ <command>DROP DATABASE</command>,
+ <command>ALTER DATABASE RENAME TO</command>,
+ <command>ALTER DATABASE SET TABLESPACE</command>, or
+ <command>CREATE DATABASE</command>.
+ Requires both <literal>BGWORKER_SHMEM_ACCESS</literal> and
+ <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 8e1068969ae..8334b75548c 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -26,6 +26,7 @@
#include "storage/lwlock.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
+#include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/shmem.h"
#include "tcop/tcopprot.h"
@@ -1399,3 +1400,42 @@ GetBackgroundWorkerTypeByPid(pid_t pid)
return result;
}
+
+/*
+ * Terminate all background workers connected to the given database, if they
+ * had requested it.
+ */
+void
+TerminateInterruptableBgWorkersByDbOid(Oid databaseId)
+{
+ bool signal_postmaster = false;
+
+ LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
+
+ /*
+ * Iterate through slots, looking for workers connected to the given
+ * database.
+ */
+ for (int slotno = 0; slotno < BackgroundWorkerData->total_slots; ++slotno)
+ {
+ BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
+
+ if (slot->in_use &&
+ (slot->worker.bgw_flags & BGWORKER_INTERRUPTABLE))
+ {
+ PGPROC *proc = BackendPidGetProc(slot->pid);
+
+ if (proc && proc->databaseId == databaseId)
+ {
+ slot->terminate = true;
+ signal_postmaster = true;
+ }
+ }
+ }
+
+ LWLockRelease(BackgroundWorkerLock);
+
+ /* Make sure the postmaster notices the change to shared memory. */
+ if (signal_postmaster)
+ SendPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE);
+}
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index f3a1603204e..de80422caea 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -56,11 +56,13 @@
#include "catalog/pg_authid.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "postmaster/bgworker.h"
#include "port/pg_lfind.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/injection_point.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
@@ -3687,8 +3689,9 @@ CountUserBackends(Oid roleid)
* CountOtherDBBackends -- check for other backends running in the given DB
*
* If there are other backends in the DB, we will wait a maximum of 5 seconds
- * for them to exit. Autovacuum backends are encouraged to exit early by
- * sending them SIGTERM, but normal user backends are just waited for.
+ * for them to exit. Autovacuum backends and background workers are encouraged
+ * to exit early by sending them SIGTERM, but normal user backends are just
+ * waited for.
*
* The current backend is always ignored; it is caller's responsibility to
* check whether the current backend uses the given DB, if it's important.
@@ -3713,10 +3716,19 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
#define MAXAUTOVACPIDS 10 /* max autovacs to SIGTERM per iteration */
int autovac_pids[MAXAUTOVACPIDS];
- int tries;
- /* 50 tries with 100ms sleep between tries makes 5 sec total wait */
- for (tries = 0; tries < 50; tries++)
+ /*
+ * Retry up to 50 times with 100ms between attempts (max 5s total). Can be
+ * reduced to 3 attempts (max 0.3s total) to speed up tests.
+ */
+ int ntries = 50;
+
+#ifdef USE_INJECTION_POINTS
+ if (IS_INJECTION_POINT_ATTACHED("reduce-ncounts"))
+ ntries = 3;
+#endif
+
+ for (int tries = 0; tries < ntries; tries++)
{
int nautovacs = 0;
bool found = false;
@@ -3766,6 +3778,12 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
for (index = 0; index < nautovacs; index++)
(void) kill(autovac_pids[index], SIGTERM); /* ignore any error */
+ /*
+ * Terminate all background workers for this database, if they had
+ * requested it (BGWORKER_INTERRUPTABLE)
+ */
+ TerminateInterruptableBgWorkersByDbOid(databaseId);
+
/* sleep, then try again */
pg_usleep(100 * 1000L); /* 100ms */
}
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index 058667a47a0..e43881448d5 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -59,6 +59,13 @@
*/
#define BGWORKER_BACKEND_DATABASE_CONNECTION 0x0002
+/*
+ * Exit the bgworker if its database is involved in a CREATE, ALTER or DROP
+ * database command.
+ * Requires BGWORKER_SHMEM_ACCESS and BGWORKER_BACKEND_DATABASE_CONNECTION.
+ */
+#define BGWORKER_INTERRUPTABLE 0x0004
+
/*
* This class is used internally for parallel queries, to keep track of the
* number of active parallel workers and make sure we never launch more than
@@ -128,6 +135,7 @@ extern const char *GetBackgroundWorkerTypeByPid(pid_t pid);
/* Terminate a bgworker */
extern void TerminateBackgroundWorker(BackgroundWorkerHandle *handle);
+extern void TerminateInterruptableBgWorkersByDbOid(Oid databaseId);
/* This is valid in a running worker */
extern PGDLLIMPORT BackgroundWorker *MyBgworkerEntry;
diff --git a/src/test/modules/worker_spi/Makefile b/src/test/modules/worker_spi/Makefile
index 024b34cdbb3..e7c5c059e32 100644
--- a/src/test/modules/worker_spi/Makefile
+++ b/src/test/modules/worker_spi/Makefile
@@ -6,6 +6,10 @@ EXTENSION = worker_spi
DATA = worker_spi--1.0.sql
PGFILEDESC = "worker_spi - background worker example"
+EXTRA_INSTALL = src/test/modules/injection_points
+
+export enable_injection_points
+
TAP_TESTS = 1
ifdef USE_PGXS
diff --git a/src/test/modules/worker_spi/meson.build b/src/test/modules/worker_spi/meson.build
index d673ece48a0..5ba66051396 100644
--- a/src/test/modules/worker_spi/meson.build
+++ b/src/test/modules/worker_spi/meson.build
@@ -26,8 +26,12 @@ tests += {
'sd': meson.current_source_dir(),
'bd': meson.current_build_dir(),
'tap': {
+ 'env': {
+ 'enable_injection_points': get_option('injection_points') ? 'yes' : 'no',
+ },
'tests': [
't/001_worker_spi.pl',
+ 't/002_worker_terminate.pl'
],
},
}
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
new file mode 100644
index 00000000000..5467e423fd9
--- /dev/null
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -0,0 +1,140 @@
+# Copyright (c) 2025, PostgreSQL Global Development Group
+
+# Test background workers can be terminated by db commands
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# This test depends on injection points to detect whether background workers
+# remain.
+if ($ENV{enable_injection_points} ne 'yes')
+{
+ plan skip_all => 'Injection points not supported by this build';
+}
+
+# Ensure the worker_spi dynamic worker is launched on the specified database
+sub launch_bgworker
+{
+ my ($node, $database, $testcase, $request_terminate) = @_;
+ my $offset = -s $node->logfile;
+
+ # Launch a background worker on the given database
+ my $result = $node->safe_psql(
+ $database, qq(
+ SELECT worker_spi_launch($testcase, oid, 0, '{}', $request_terminate) IS NOT NULL
+ FROM pg_database WHERE datname = '$database';
+ ));
+ is($result, 't', "dynamic bgworker $testcase launched");
+
+ # Check the worker is surely initialized
+ $node->wait_for_log(
+ qr/LOG: worker_spi dynamic worker $testcase initialized with .*\..*/,
+ $offset);
+}
+
+# Run the given query and verify the background worker can be terminated
+sub run_db_command
+{
+ my ($node, $command, $testname) = @_;
+ my $offset = -s $node->logfile;
+
+ $node->safe_psql('postgres', $command);
+
+ $node->wait_for_log(
+ qr/terminating background worker \"worker_spi dynamic\" due to administrator command/,
+ $offset);
+
+ note("background worker can be terminated at $testname");
+}
+
+my $node = PostgreSQL::Test::Cluster->new('mynode');
+$node->init;
+$node->start;
+
+# Check if the extension injection_points is available, as it may be
+# possible that this script is run with installcheck, where the module
+# would not be installed by default.
+if (!$node->check_extension('injection_points'))
+{
+ plan skip_all => 'Extension injection_points not installed';
+}
+
+$node->safe_psql('postgres', 'CREATE EXTENSION worker_spi;');
+
+# Launch a background worker without BGWORKER_INTERRUPTABLE
+launch_bgworker($node, 'postgres', 0, "false");
+
+# Ensure CREATE DATABASE WITH TEMPLATE fails because background worker retains
+
+# The injection point 'reduce-ncounts' reduces the number of backend
+# retries, allowing for shorter test runs. See CountOtherDBBackends().
+$node->safe_psql('postgres', "CREATE EXTENSION injection_points;");
+$node->safe_psql('postgres',
+ "SELECT injection_points_attach('reduce-ncounts', 'error');");
+
+my $stderr;
+
+$node->psql(
+ 'postgres',
+ "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ stderr => \$stderr);
+ok( $stderr =~
+ "source database \"postgres\" is being accessed by other users",
+ "background worker blocked the database creation");
+
+# Confirm a background worker is still running
+my $result = $node->safe_psql(
+ "postgres", qq(
+ SELECT count(1) FROM pg_stat_activity
+ WHERE backend_type = 'worker_spi dynamic';));
+
+is($result, '1',
+ "background worker is still running after CREATE DATABASE WITH TEMPLATE");
+
+# Terminate the worker for upcoming tests
+$node->safe_psql(
+ "postgres", qq(
+ SELECT pg_terminate_backend(pid)
+ FROM pg_stat_activity WHERE backend_type = 'worker_spi dynamic';));
+
+# The injection point won't be used anymore, release it.
+$node->safe_psql('postgres',
+ "SELECT injection_points_detach('reduce-ncounts');");
+
+# Ensure BGWORKER_INTERRUPTABLE allows background workers to be
+# terminated at some database manipulations.
+#
+# Testcase 1: CREATE DATABASE WITH TEMPLATE
+launch_bgworker($node, 'postgres', 1, "true");
+run_db_command(
+ $node,
+ "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ "CREATE DATABASE WITH TEMPLATE");
+
+# Testcase 2: ALTER DATABASE RENAME
+launch_bgworker($node, 'testdb', 2, "true");
+run_db_command(
+ $node,
+ "ALTER DATABASE testdb RENAME TO renameddb",
+ "ALTER DATABASE RENAME");
+
+# Preparation for the next test; create another tablespace
+my $tablespace = PostgreSQL::Test::Utils::tempdir;
+$node->safe_psql('postgres',
+ "CREATE TABLESPACE test_tablespace LOCATION '$tablespace'");
+
+# Testcase 3: ALTER DATABASE SET TABLESPACE
+launch_bgworker($node, 'renameddb', 3, "true");
+run_db_command(
+ $node,
+ "ALTER DATABASE renameddb SET TABLESPACE test_tablespace",
+ "ALTER DATABASE SET TABLESPACE");
+
+# Testcase 4: DROP DATABASE
+launch_bgworker($node, 'renameddb', 4, "true");
+run_db_command($node, "DROP DATABASE renameddb", "DROP DATABASE");
+
+done_testing();
diff --git a/src/test/modules/worker_spi/worker_spi--1.0.sql b/src/test/modules/worker_spi/worker_spi--1.0.sql
index 84deb6199f6..3d12de37bea 100644
--- a/src/test/modules/worker_spi/worker_spi--1.0.sql
+++ b/src/test/modules/worker_spi/worker_spi--1.0.sql
@@ -7,7 +7,8 @@
CREATE FUNCTION worker_spi_launch(index int4,
dboid oid DEFAULT 0,
roleoid oid DEFAULT 0,
- flags text[] DEFAULT '{}')
+ flags text[] DEFAULT '{}',
+ request_termination boolean DEFAULT false)
RETURNS pg_catalog.int4 STRICT
AS 'MODULE_PATHNAME'
LANGUAGE C;
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 1a21b8c8876..50d16aaf9a8 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -404,10 +404,15 @@ worker_spi_launch(PG_FUNCTION_ARGS)
Size ndim;
int nelems;
Datum *datum_flags;
+ bool request_termination = PG_GETARG_BOOL(4);
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
BGWORKER_BACKEND_DATABASE_CONNECTION;
+
+ if (request_termination)
+ worker.bgw_flags |= BGWORKER_INTERRUPTABLE;
+
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART;
sprintf(worker.bgw_library_name, "worker_spi");
--
2.39.3
[application/octet-stream] v0011-0002-Renamed-the-flag-to-BGWORKER_PROTECTED.patch (9.5K, 3-v0011-0002-Renamed-the-flag-to-BGWORKER_PROTECTED.patch)
download | inline diff:
From bc41ea1629f9e16f1c92c3475befecb0f6ef9a8c Mon Sep 17 00:00:00 2001
From: "iwata.aya" <[email protected]>
Date: Thu, 25 Dec 2025 17:16:55 +0900
Subject: [PATCH v0011 2/2] Renamed the flag to BGWORKER_PROTECTED and set
default behavior to Terminate.
---
doc/src/sgml/bgworker.sgml | 6 +-
src/backend/postmaster/bgworker.c | 2 +-
src/backend/storage/ipc/procarray.c | 2 +-
src/include/postmaster/bgworker.h | 2 +-
.../worker_spi/t/002_worker_terminate.pl | 117 ++++++++++++------
src/test/modules/worker_spi/worker_spi.c | 6 +-
6 files changed, 90 insertions(+), 45 deletions(-)
diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml
index a7c238750fc..f56a1850ffb 100644
--- a/doc/src/sgml/bgworker.sgml
+++ b/doc/src/sgml/bgworker.sgml
@@ -109,11 +109,11 @@ typedef struct BackgroundWorker
</varlistentry>
<varlistentry>
- <term><literal>BGWORKER_INTERRUPTABLE</literal></term>
+ <term><literal>BGWORKER_PROTECTED</literal></term>
<listitem>
<para>
- <indexterm><primary>BGWORKER_INTERRUPTABLE</primary></indexterm>
- Requests termination of the background worker when its connected database is
+ <indexterm><primary>BGWORKER_PROTECTED</primary></indexterm>
+ Prevents termination of the background worker when its connected database is
dropped, renamed, moved to a different tablespace, or used as a template for
<command>CREATE DATABASE</command>. Specifically, the postmaster sends a
termination signal when any of these commands affect the worker's database:
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 8334b75548c..4e0aa195140 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -1421,7 +1421,7 @@ TerminateInterruptableBgWorkersByDbOid(Oid databaseId)
BackgroundWorkerSlot *slot = &BackgroundWorkerData->slot[slotno];
if (slot->in_use &&
- (slot->worker.bgw_flags & BGWORKER_INTERRUPTABLE))
+ !(slot->worker.bgw_flags & BGWORKER_PROTECTED))
{
PGPROC *proc = BackendPidGetProc(slot->pid);
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index de80422caea..68b6aff151f 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3780,7 +3780,7 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
/*
* Terminate all background workers for this database, if they had
- * requested it (BGWORKER_INTERRUPTABLE)
+ * requested it (BGWORKER_PROTECTED)
*/
TerminateInterruptableBgWorkersByDbOid(databaseId);
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
index e43881448d5..022b8130a64 100644
--- a/src/include/postmaster/bgworker.h
+++ b/src/include/postmaster/bgworker.h
@@ -64,7 +64,7 @@
* database command.
* Requires BGWORKER_SHMEM_ACCESS and BGWORKER_BACKEND_DATABASE_CONNECTION.
*/
-#define BGWORKER_INTERRUPTABLE 0x0004
+#define BGWORKER_PROTECTED 0x0004
/*
* This class is used internally for parallel queries, to keep track of the
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
index 5467e423fd9..ca59f3a836b 100644
--- a/src/test/modules/worker_spi/t/002_worker_terminate.pl
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -50,6 +50,21 @@ sub run_db_command
note("background worker can be terminated at $testname");
}
+# Confirm a background worker is still running
+sub confirm_bgworker_running
+{
+ my ($node, $dbname, $testinfo) = @_;
+
+ my $result = $node->safe_psql(
+ "$dbname", qq(
+ SELECT count(1) FROM pg_stat_activity
+ WHERE backend_type = 'worker_spi dynamic';));
+
+ is($result, '1',
+ "background worker is not stopped after $testinfo");
+
+}
+
my $node = PostgreSQL::Test::Cluster->new('mynode');
$node->init;
$node->start;
@@ -64,10 +79,15 @@ if (!$node->check_extension('injection_points'))
$node->safe_psql('postgres', 'CREATE EXTENSION worker_spi;');
-# Launch a background worker without BGWORKER_INTERRUPTABLE
+# Launch a background worker without BGWORKER_PROTECTED
launch_bgworker($node, 'postgres', 0, "false");
-# Ensure CREATE DATABASE WITH TEMPLATE fails because background worker retains
+# Ensure CREATE DATABASE WITH TEMPLATE sucseeds because background worker retains
+
+run_db_command(
+ $node,
+ "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ "CREATE DATABASE WITH TEMPLATE");
# The injection point 'reduce-ncounts' reduces the number of backend
# retries, allowing for shorter test runs. See CountOtherDBBackends().
@@ -77,22 +97,20 @@ $node->safe_psql('postgres',
my $stderr;
+# Ensure BGWORKER_PROTECTED allows background workers not to be
+# terminated at some database manipulations.
+#
+# Testcase 1: CREATE DATABASE WITH TEMPLATE
+launch_bgworker($node, 'postgres', 1, "true");
$node->psql(
'postgres',
- "CREATE DATABASE testdb WITH TEMPLATE postgres",
+ "CREATE DATABASE testdb2 WITH TEMPLATE postgres",
stderr => \$stderr);
ok( $stderr =~
- "source database \"postgres\" is being accessed by other users",
+ "source database \"postgres\" is being accessed by other users",
"background worker blocked the database creation");
-# Confirm a background worker is still running
-my $result = $node->safe_psql(
- "postgres", qq(
- SELECT count(1) FROM pg_stat_activity
- WHERE backend_type = 'worker_spi dynamic';));
-
-is($result, '1',
- "background worker is still running after CREATE DATABASE WITH TEMPLATE");
+confirm_bgworker_running($node, "postgres", "CREATE DATABASE WITH TEMPLATE");
# Terminate the worker for upcoming tests
$node->safe_psql(
@@ -100,26 +118,23 @@ $node->safe_psql(
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity WHERE backend_type = 'worker_spi dynamic';));
-# The injection point won't be used anymore, release it.
-$node->safe_psql('postgres',
- "SELECT injection_points_detach('reduce-ncounts');");
-
-# Ensure BGWORKER_INTERRUPTABLE allows background workers to be
-# terminated at some database manipulations.
-#
-# Testcase 1: CREATE DATABASE WITH TEMPLATE
-launch_bgworker($node, 'postgres', 1, "true");
-run_db_command(
- $node,
- "CREATE DATABASE testdb WITH TEMPLATE postgres",
- "CREATE DATABASE WITH TEMPLATE");
-
# Testcase 2: ALTER DATABASE RENAME
launch_bgworker($node, 'testdb', 2, "true");
-run_db_command(
- $node,
+$node->psql(
+ 'testdb',
"ALTER DATABASE testdb RENAME TO renameddb",
- "ALTER DATABASE RENAME");
+ stderr => \$stderr);
+ok( $stderr =~
+ "current database cannot be renamed",
+ "background worker blocked the alter database rename to");
+
+confirm_bgworker_running($node, "testdb", "ALTER DATABASE RENAME TO");
+
+# Terminate the worker for upcoming tests
+$node->safe_psql(
+ "testdb", qq(
+ SELECT pg_terminate_backend(pid)
+ FROM pg_stat_activity WHERE backend_type = 'worker_spi dynamic';));
# Preparation for the next test; create another tablespace
my $tablespace = PostgreSQL::Test::Utils::tempdir;
@@ -127,14 +142,44 @@ $node->safe_psql('postgres',
"CREATE TABLESPACE test_tablespace LOCATION '$tablespace'");
# Testcase 3: ALTER DATABASE SET TABLESPACE
-launch_bgworker($node, 'renameddb', 3, "true");
-run_db_command(
- $node,
- "ALTER DATABASE renameddb SET TABLESPACE test_tablespace",
- "ALTER DATABASE SET TABLESPACE");
+launch_bgworker($node, 'testdb', 3, "true");
+$node->psql(
+ 'testdb',
+ "ALTER DATABASE testdb SET TABLESPACE test_tablespace",
+ stderr => \$stderr);
+ok( $stderr =~
+ "cannot change the tablespace of the currently open database",
+ "background worker blocked the alter database set tablespace");
+
+confirm_bgworker_running($node, "testdb", "ALTER DATABASE SET TABLESPACE");
+
+# Terminate the worker for upcoming tests
+$node->safe_psql(
+ "testdb", qq(
+ SELECT pg_terminate_backend(pid)
+ FROM pg_stat_activity WHERE backend_type = 'worker_spi dynamic';));
# Testcase 4: DROP DATABASE
-launch_bgworker($node, 'renameddb', 4, "true");
-run_db_command($node, "DROP DATABASE renameddb", "DROP DATABASE");
+launch_bgworker($node, 'testdb', 4, "true");
+$node->psql(
+ 'testdb',
+ "DROP DATABASE testdb",
+ stderr => \$stderr);
+ok( $stderr =~
+ "cannot drop the currently open database",
+ "background worker blocked the database drop");
+
+confirm_bgworker_running($node, "testdb", "DROP DATABASE");
+
+# Terminate the worker
+$node->safe_psql(
+ "testdb", qq(
+ SELECT pg_terminate_backend(pid)
+ FROM pg_stat_activity WHERE backend_type = 'worker_spi dynamic';));
+
+
+# The injection point won't be used anymore, release it.
+$node->safe_psql('postgres',
+ "SELECT injection_points_detach('reduce-ncounts');");
done_testing();
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 50d16aaf9a8..04ef060ff42 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -404,14 +404,14 @@ worker_spi_launch(PG_FUNCTION_ARGS)
Size ndim;
int nelems;
Datum *datum_flags;
- bool request_termination = PG_GETARG_BOOL(4);
+ bool prevent_termination = PG_GETARG_BOOL(4);
memset(&worker, 0, sizeof(worker));
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
BGWORKER_BACKEND_DATABASE_CONNECTION;
- if (request_termination)
- worker.bgw_flags |= BGWORKER_INTERRUPTABLE;
+ if (prevent_termination)
+ worker.bgw_flags |= BGWORKER_PROTECTED;
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
worker.bgw_restart_time = BGW_NEVER_RESTART;
--
2.39.3
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-12-26 10:17 Ryo Matsumura (Fujitsu) <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
1 sibling, 2 replies; 67+ messages in thread
From: Ryo Matsumura (Fujitsu) @ 2025-12-26 10:17 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; 'Pavel Stehule' <[email protected]>; Michael Paquier <[email protected]>; +Cc: Peter Smith <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
Hi Pavel-san, Iwata-san,
+1 to Allow-background-workers-to-be-terminated
The result is same, so I think it's better to prioritize compatibility.
PGWORKER_PROTECTED would be used in scenarios like the following:
Existing features are probably not designed to be forcibly stopped.
Therefore, all existing features should have PROTECTED applied to them.
Most newly implemented features will also have PROTECTED applied because it requires less thought and is safer.
Only considerate developers of features that can easily guarantee safety would adopt the default.
In conclusion, this is no different from BGWORKER_INTERRUPTABLE.
Therefore, I think it's better to prioritize compatibility.
Best Regards
Ryo Matsumura
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-12-26 11:04 Pavel Stehule <[email protected]>
parent: Ryo Matsumura (Fujitsu) <[email protected]>
1 sibling, 1 reply; 67+ messages in thread
From: Pavel Stehule @ 2025-12-26 11:04 UTC (permalink / raw)
To: Ryo Matsumura (Fujitsu) <[email protected]>; +Cc: Aya Iwata (Fujitsu) <[email protected]>; Michael Paquier <[email protected]>; Peter Smith <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
Hi
pá 26. 12. 2025 v 11:17 odesílatel Ryo Matsumura (Fujitsu) <
[email protected]> napsal:
> Hi Pavel-san, Iwata-san,
>
> +1 to Allow-background-workers-to-be-terminated
>
> The result is same, so I think it's better to prioritize compatibility.
>
> PGWORKER_PROTECTED would be used in scenarios like the following:
> Existing features are probably not designed to be forcibly stopped.
> Therefore, all existing features should have PROTECTED applied to them.
> Most newly implemented features will also have PROTECTED applied because
> it requires less thought and is safer.
> Only considerate developers of features that can easily guarantee safety
> would adopt the default.
>
> In conclusion, this is no different from BGWORKER_INTERRUPTABLE.
> Therefore, I think it's better to prioritize compatibility.
>
I am not sure if I understand what you prefer.
I share your opinion that most developers use options that are more safe
and require less thinking (and if this option will not be default, most
developers maybe don't use it in the first cycle).
At the end most bgworkers will be not be marked as interruptible (isn't
important if we use flag INTERRUPTIBLE or PROTECTED). Then proposed flag
without FORCE clause will be mostly inefficient - and then there is strong
question if proposed feature has some real benefit. I don't think so this
feature should be implemented and committed once, but we should to find a
consensus on implemented behaviour in all possible cases, and all possible
question.
1. When bgworker is marked as INTERRUPTIBLE, then can be cancelled
immediately - there is full agreement - I don't see any problem - just
probably almost all bgworkers will be not be marked as INTERRUPTIBLE
2. When bgworker is not marked as INTERRUPTIBLE, then can it be cancelled?
How dangerous is this? If it cannot be cancelled, then ALTER should wait on
lock forever, or there should be some special timeout - like CREATE, DROP
DATABASE and waiting on template databases.
3. If the user needs to execute ALTER, then probably he manually terminates
the worker any time. Are currently used workers safe against terminating?
We know so mostly workers will be restarted after timeout - and then it is
"safe". It is really safe? Can we expect it? If it is safe, then we can
implement FORCE clause. Probably any worker can be restarted - and without
safety it is hard to imagine using in the production.
I think there is a fundamental question that should be solved before - what
is a risk of canceling bgworker? The possible reply is so there is not any
risk for correctly implemented bgworkers. And then there is a question if
we still need the flag INTERRUPTIBLE? My opinion for this case is probably
yes, because cancelling can introduce some bigger latency.
Regards
Pavel
>
> Best Regards
> Ryo Matsumura
>
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-12-29 00:52 Michael Paquier <[email protected]>
parent: Pavel Stehule <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Michael Paquier @ 2025-12-29 00:52 UTC (permalink / raw)
To: Pavel Stehule <[email protected]>; +Cc: Ryo Matsumura (Fujitsu) <[email protected]>; Aya Iwata (Fujitsu) <[email protected]>; Peter Smith <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
On Fri, Dec 26, 2025 at 12:04:47PM +0100, Pavel Stehule wrote:
> I am not sure if I understand what you prefer.
Seems to me that Iwata-san sides with compatibility. Spoiler alert: I
do side with compatibility. See below for more details.
> 1. When bgworker is marked as INTERRUPTIBLE, then can be cancelled
> immediately - there is full agreement - I don't see any problem - just
> probably almost all bgworkers will be not be marked as INTERRUPTIBLE
Yes, these can and should be stopped.
> 2. When bgworker is not marked as INTERRUPTIBLE, then can it be cancelled?
> How dangerous is this? If it cannot be cancelled, then ALTER should wait on
> lock forever, or there should be some special timeout - like CREATE, DROP
> DATABASE and waiting on template databases.
Cancellation is a different thing. bgworkers can be tweaked to answer
to specific signals with their own handlers. What we are discussing
here is if a signal should be sent to a bgworker or not when
performing specific operations.
> 3. If the user needs to execute ALTER, then probably he manually terminates
> the worker any time. Are currently used workers safe against terminating?
> We know so mostly workers will be restarted after timeout - and then it is
> "safe". It is really safe? Can we expect it? If it is safe, then we can
> implement FORCE clause. Probably any worker can be restarted - and without
> safety it is hard to imagine using in the production.
That would be up to an extension developer. The new behavior can be
useful in some cases. Forcing it by default could lead to unwanted
results.
> I think there is a fundamental question that should be solved before - what
> is a risk of canceling bgworker? The possible reply is so there is not any
> risk for correctly implemented bgworkers. And then there is a question if
> we still need the flag INTERRUPTIBLE? My opinion for this case is probably
> yes, because cancelling can introduce some bigger latency.
I can think about two reasons at the top of my mind, at least:
1) Making the background workers non-interruptible is the default
behavior that we have since 9.3. Changing the default by allowin
these to be interrupted could lead to a silent behavior change that
extension developers are not aware of because the bgworker lubrary
code would still be compiled as-is, and would still be able to start
correctly. This links to my second point.
2) Operator error, and these tend to happen a lot. Somebody with the
right to create a database could decide to use as template a database
that a bgworker is linked to, leading to failures with operations
background workers expect to be able to achieve while online if we
change the default, because it does things that are critical and
should not be interrupted (one could compare that to what an
autovacuum worker does with an antiwraparound work, perhaps, which
cannot be interrupted).
As a whole, I think that we have more advantages in keeping the
default, making the possibility to make a bgworker interruptible an
opt-in choice is extra cream that can be added on top of a cake, and
one is free to add the extra cream to their portion of the cake.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, 2-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-12-29 02:03 Michael Paquier <[email protected]>
parent: Ryo Matsumura (Fujitsu) <[email protected]>
1 sibling, 0 replies; 67+ messages in thread
From: Michael Paquier @ 2025-12-29 02:03 UTC (permalink / raw)
To: Ryo Matsumura (Fujitsu) <[email protected]>; +Cc: Aya Iwata (Fujitsu) <[email protected]>; 'Pavel Stehule' <[email protected]>; Peter Smith <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
On Fri, Dec 26, 2025 at 10:17:27AM +0000, Ryo Matsumura (Fujitsu) wrote:
> +1 to Allow-background-workers-to-be-terminated
>
> The result is same, so I think it's better to prioritize compatibility.
>
> PGWORKER_PROTECTED would be used in scenarios like the following:
> Existing features are probably not designed to be forcibly stopped.
> Therefore, all existing features should have PROTECTED applied to them.
> Most newly implemented features will also have PROTECTED applied because it requires less thought and is safer.
> Only considerate developers of features that can easily guarantee safety would adopt the default.
We could design things so as we have a second flag to force a bgworker
to be non-interuptible, then we could force that either the
interruptible flag or the non-interruptible flag should be set. What
is mentioned as a problem is that 0 implies that the non-interruptible
is enforced. I don't think that we would have much to gain by doing
that, as it would just lead to extension breakages that we can avoid.
> In conclusion, this is no different from BGWORKER_INTERRUPTABLE.
> Therefore, I think it's better to prioritize compatibility.
Looking finally at the patch, I like the simplicity of what you are
doing here.
+ Requires both <literal>BGWORKER_SHMEM_ACCESS</literal> and
+ <literal>BGWORKER_BACKEND_DATABASE_CONNECTION</literal>.
SanityCheckBackgroundWorker() does not enforce this check when
registering a bgworker. But shouldn't we do so when the interruptible
flag is set?
+void
+TerminateInterruptableBgWorkersByDbOid(Oid databaseId)
Fine by me to aim for simplicity with this interface, discarding my
previous fancy comment about the extensibility we could do here.
Matsumura-san and I have also discussed a bit that offline at the last
JPUG, where I said that I'm OK with simpler at the end.
One issue with the test as written, as of run_db_command(), is that we
make sure that a worker is stopped by scanning the output of the logs.
This approach may detect incorrect patterns, unfortunately. For
example, if the termination logic has a bug it may be possible that
the worker found as terminated is the first one created by the test,
which we expect to always run. While the log is mandatory to have, I
have a suggestion to make that even better: let's keep track in
run_db_command() of the PIDs of the worker processes we expect to
exist after running each database command, then make sure that the
list of PIDs match with what we expect. This is a bit simpler in the
case of this test as we only expect one matching PID.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, 2-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2025-12-29 05:31 Pavel Stehule <[email protected]>
parent: Michael Paquier <[email protected]>
0 siblings, 0 replies; 67+ messages in thread
From: Pavel Stehule @ 2025-12-29 05:31 UTC (permalink / raw)
To: Michael Paquier <[email protected]>; +Cc: Ryo Matsumura (Fujitsu) <[email protected]>; Aya Iwata (Fujitsu) <[email protected]>; Peter Smith <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
Hi
po 29. 12. 2025 v 1:52 odesílatel Michael Paquier <[email protected]>
napsal:
> On Fri, Dec 26, 2025 at 12:04:47PM +0100, Pavel Stehule wrote:
> > I am not sure if I understand what you prefer.
>
> Seems to me that Iwata-san sides with compatibility. Spoiler alert: I
> do side with compatibility. See below for more details.
>
> > 1. When bgworker is marked as INTERRUPTIBLE, then can be cancelled
> > immediately - there is full agreement - I don't see any problem - just
> > probably almost all bgworkers will be not be marked as INTERRUPTIBLE
>
> Yes, these can and should be stopped.
>
> > 2. When bgworker is not marked as INTERRUPTIBLE, then can it be
> cancelled?
> > How dangerous is this? If it cannot be cancelled, then ALTER should wait
> on
> > lock forever, or there should be some special timeout - like CREATE, DROP
> > DATABASE and waiting on template databases.
>
> Cancellation is a different thing. bgworkers can be tweaked to answer
> to specific signals with their own handlers. What we are discussing
> here is if a signal should be sent to a bgworker or not when
> performing specific operations.
>
> > 3. If the user needs to execute ALTER, then probably he manually
> terminates
> > the worker any time. Are currently used workers safe against terminating?
> > We know so mostly workers will be restarted after timeout - and then it
> is
> > "safe". It is really safe? Can we expect it? If it is safe, then we can
> > implement FORCE clause. Probably any worker can be restarted - and
> without
> > safety it is hard to imagine using in the production.
>
> That would be up to an extension developer. The new behavior can be
> useful in some cases. Forcing it by default could lead to unwanted
> results.
>
> > I think there is a fundamental question that should be solved before -
> what
> > is a risk of canceling bgworker? The possible reply is so there is not
> any
> > risk for correctly implemented bgworkers. And then there is a question if
> > we still need the flag INTERRUPTIBLE? My opinion for this case is
> probably
> > yes, because cancelling can introduce some bigger latency.
>
> I can think about two reasons at the top of my mind, at least:
> 1) Making the background workers non-interruptible is the default
> behavior that we have since 9.3. Changing the default by allowin
> these to be interrupted could lead to a silent behavior change that
> extension developers are not aware of because the bgworker lubrary
> code would still be compiled as-is, and would still be able to start
> correctly. This links to my second point.
> 2) Operator error, and these tend to happen a lot. Somebody with the
> right to create a database could decide to use as template a database
> that a bgworker is linked to, leading to failures with operations
> background workers expect to be able to achieve while online if we
> change the default, because it does things that are critical and
> should not be interrupted (one could compare that to what an
> autovacuum worker does with an antiwraparound work, perhaps, which
> cannot be interrupted).
>
> As a whole, I think that we have more advantages in keeping the
> default, making the possibility to make a bgworker interruptible an
> opt-in choice is extra cream that can be added on top of a cake, and
> one is free to add the extra cream to their portion of the cake.
>
ok
Regards
Pavel
> --
> Michael
>
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-03-13 11:38 Aya Iwata (Fujitsu) <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
1 sibling, 1 reply; 67+ messages in thread
From: Aya Iwata (Fujitsu) @ 2026-03-13 11:38 UTC (permalink / raw)
To: 'Michael Paquier' <[email protected]>; Peter Smith <[email protected]>; +Cc: Pavel Stehule <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
Hi
On March 10th, the bgworker test "t/002_worker_terminate.pl" failed on the Build Farm.
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=jay&dt=2026-03-10%2019%3A26%3A19
We are currently attempting to reproduce this issue and are considering a fix.
According to the log, the test failed because the bgworker cannot be terminated
within the time frame (5 seconds) when ALTER DATABASE RENAME is executed.
So I suspect that there is a lock conflict.
Log of Build Farm;
2026-03-10 21:08:33.103 CET [28076:4] 002_worker_terminate.pl LOG: statement: ALTER DATABASE testdb RENAME TO renameddb
2026-03-10 21:08:34.578 CET [28071:2] FATAL: terminating background worker "worker_spi dynamic" due to administrator command
2026-03-10 21:08:38.109 CET [28076:5] 002_worker_terminate.pl ERROR: database "testdb" is being accessed by other users
2026-03-10 21:08:38.109 CET [28076:6] 002_worker_terminate.pl DETAIL: There is 1 other session using the database.
2026-03-10 21:08:38.109 CET [28076:7] 002_worker_terminate.pl STATEMENT: ALTER DATABASE testdb RENAME TO renameddb
2026-03-10 21:08:38.229 CET [28076:8] 002_worker_terminate.pl LOG: disconnection: session time: 0:00:05.129 user=buildfarm database=postgres host=[local]
2026-03-10 21:08:38.231 CET [28025:6] LOG: background worker "worker_spi dynamic" (PID 28071) exited with exit code 1
2026-03-10 21:08:38.234 CET [28025:7] LOG: received immediate shutdown request
2026-03-10 21:08:38.241 CET [28025:8] LOG: database system is shut down
If there is a lock conflict, we will come up with a solution.
Regards,
Aya Iwata
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-03-16 07:23 Michael Paquier <[email protected]>
parent: Aya Iwata (Fujitsu) <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Michael Paquier @ 2026-03-16 07:23 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: Peter Smith <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
On Fri, Mar 13, 2026 at 11:38:51AM +0000, Aya Iwata (Fujitsu) wrote:
> On March 10th, the bgworker test "t/002_worker_terminate.pl" failed on the Build Farm.
> https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=jay&dt=2026-03-10%2019%3A26%3A19
Indeed.
> We are currently attempting to reproduce this issue and are considering a fix.
>
> According to the log, the test failed because the bgworker cannot be terminated
> within the time frame (5 seconds) when ALTER DATABASE RENAME is executed.
>
> So I suspect that there is a lock conflict.
Aye, we have a timing issue here, and at first it seemed to me that
this was a bug in the backend logic. But actually I suspect that it
is simpler than that: we don't disable autovacuum so couldn't an
autovacuum worker connect to the database "testdb" that we are trying
to rename in this query? I cannot be 100% sure because we have
reduced the log activity for the sake of the tests, but that feels
possible to me. We could try to disable autovacuum entirely, then see
if the situation gets better in the buildfarm.
The failure rate is so low that it is likely going to take a few weeks
to check the stability of the situation. We could also lot more
things, of course, as a temporary solution.
What do you think?
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, 2-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-03-16 23:37 Michael Paquier <[email protected]>
parent: Michael Paquier <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Michael Paquier @ 2026-03-16 23:37 UTC (permalink / raw)
To: Aya Iwata (Fujitsu) <[email protected]>; +Cc: Peter Smith <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; pgsql-hackers
On Mon, Mar 16, 2026 at 04:23:40PM +0900, Michael Paquier wrote:
> Aye, we have a timing issue here, and at first it seemed to me that
> this was a bug in the backend logic. But actually I suspect that it
> is simpler than that: we don't disable autovacuum so couldn't an
> autovacuum worker connect to the database "testdb" that we are trying
> to rename in this query? I cannot be 100% sure because we have
> reduced the log activity for the sake of the tests, but that feels
> possible to me. We could try to disable autovacuum entirely, then see
> if the situation gets better in the buildfarm.
>
> The failure rate is so low that it is likely going to take a few weeks
> to check the stability of the situation. We could also lot more
> things, of course, as a temporary solution.
After sleeping on it, I do not have a better idea than the attached,
so we could always get that into the tree and see if the situation
improves, even if it would take time. Iwata-san and others, what do
you think? Perhaps you could think of a different reason causing this
failure?
--
Michael
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
index 6d3794355981..21dfb5961289 100644
--- a/src/test/modules/worker_spi/t/002_worker_terminate.pl
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -59,6 +59,10 @@ sub run_bgworker_interruptible_test
my $node = PostgreSQL::Test::Cluster->new('mynode');
$node->init;
+$node->append_conf(
+ "postgresql.conf", qq(
+autovacuum = off
+));
$node->start;
# Check if the extension injection_points is available, as it may be
Attachments:
[text/plain] worker-spi-test.patch (564B, 2-worker-spi-test.patch)
download | inline diff:
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
index 6d3794355981..21dfb5961289 100644
--- a/src/test/modules/worker_spi/t/002_worker_terminate.pl
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -59,6 +59,10 @@ sub run_bgworker_interruptible_test
my $node = PostgreSQL::Test::Cluster->new('mynode');
$node->init;
+$node->append_conf(
+ "postgresql.conf", qq(
+autovacuum = off
+));
$node->start;
# Check if the extension injection_points is available, as it may be
[application/pgp-signature] signature.asc (833B, 3-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-03-17 01:55 =?iso-2022-jp?B?S3Vyb2RhLCBIYXlhdG8vGyRCOXVFRBsoQiAbJEJIOz9NGyhC?= <[email protected]>
parent: Michael Paquier <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: =?iso-2022-jp?B?S3Vyb2RhLCBIYXlhdG8vGyRCOXVFRBsoQiAbJEJIOz9NGyhC?= @ 2026-03-17 01:55 UTC (permalink / raw)
To: Michael Paquier <[email protected]>; =?iso-2022-jp?B?SXdhdGEsIEF5YS8bJEI0ZEVEGyhCIBskQjpMGyhC?= <[email protected]>; +Cc: Peter Smith <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; pgsql-hackers
Dear Michael,
> After sleeping on it, I do not have a better idea than the attached,
> so we could always get that into the tree and see if the situation
> improves, even if it would take time. Iwata-san and others, what do
> you think? Perhaps you could think of a different reason causing this
> failure?
I also had a discussion with her off-list, but I could not find neither.
Additional idea is to increase the log level to make analysis easier, but not
sure what value is appropriate. Is it enough to raise to DEBUG1?
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-03-17 03:57 Michael Paquier <[email protected]>
parent: =?iso-2022-jp?B?S3Vyb2RhLCBIYXlhdG8vGyRCOXVFRBsoQiAbJEJIOz9NGyhC?= <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Michael Paquier @ 2026-03-17 03:57 UTC (permalink / raw)
To: Kuroda, Hayato/黒田 隼人 <[email protected]>; +Cc: Iwata, Aya/岩田 彩 <[email protected]>; Peter Smith <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; pgsql-hackers
On Tue, Mar 17, 2026 at 01:55:05AM +0000, Kuroda, Hayato/黒田 隼人 wrote:
> I also had a discussion with her off-list, but I could not find neither.
> Additional idea is to increase the log level to make analysis easier, but not
> sure what value is appropriate. Is it enough to raise to DEBUG1?
That would be an option. Let's do that if we see another failure
after disabling autovacuum. Another thing I have noticed is
debug_parallel_query=regress for this animal, which may encourage
parallel workers, so I have also disabled it. Let's see first if we
get more failures after af8837a10bc7.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, 2-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-03-17 06:00 Alexander Lakhin <[email protected]>
parent: Michael Paquier <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Alexander Lakhin @ 2026-03-17 06:00 UTC (permalink / raw)
To: Iwata, Aya/岩田 彩 <[email protected]>; Michael Paquier <[email protected]>; +Cc: Peter Smith <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; pgsql-hackers
Hello Michael and Iwata-san,
17.03.2026 05:57, Michael Paquier wrote:
> On Tue, Mar 17, 2026 at 01:55:05AM +0000, Kuroda, Hayato/黒田 隼人 wrote:
>> I also had a discussion with her off-list, but I could not find neither.
>> Additional idea is to increase the log level to make analysis easier, but not
>> sure what value is appropriate. Is it enough to raise to DEBUG1?
> That would be an option. Let's do that if we see another failure
> after disabling autovacuum. Another thing I have noticed is
> debug_parallel_query=regress for this animal, which may encourage
> parallel workers, so I have also disabled it. Let's see first if we
> get more failures after af8837a10bc7.
> --
I've noticed another failure of the test from widowbird: [1]. But probably
it's not related to f1e251be8 directly, as widowbird failed with a similar
error before: [2]. I couldn't reproduce such failures yet, but keep
trying in the background...
[1] https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=widowbird&dt=2026-03-16%2009%3A35%3A03
[2] https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=widowbird&dt=2025-10-25%2010%3A35%3A03
Best regards,
Alexander
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-03-17 08:33 Michael Paquier <[email protected]>
parent: Alexander Lakhin <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Michael Paquier @ 2026-03-17 08:33 UTC (permalink / raw)
To: Alexander Lakhin <[email protected]>; +Cc: Iwata, Aya/岩田 彩 <[email protected]>; Peter Smith <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; pgsql-hackers
On Tue, Mar 17, 2026 at 08:00:00AM +0200, Alexander Lakhin wrote:
> I've noticed another failure of the test from widowbird: [1]. But probably
> it's not related to f1e251be8 directly, as widowbird failed with a similar
> error before: [2]. I couldn't reproduce such failures yet, but keep
> trying in the background...
>
> [1] https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=widowbird&dt=2026-03-16%2009%3A35%3A03
> [2] https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=widowbird&dt=2025-10-25%2010%3A35%3A03
Thanks. widowbird also uses "debug_parallel_query = regress", so it
really looks like to me that this is related to a parallel worker
spawning and messing with the database being renamed.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, 2-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-03-19 00:54 Michael Paquier <[email protected]>
parent: Michael Paquier <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Michael Paquier @ 2026-03-19 00:54 UTC (permalink / raw)
To: Tom Lane <[email protected]>; +Cc: Alexander Lakhin <[email protected]>; Iwata, Aya/岩田 彩 <[email protected]>; Peter Smith <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; pgsql-hackers
On Wed, Mar 18, 2026 at 03:52:02PM -0400, Tom Lane wrote:
> which makes me wonder whether the problematic session is the second or
> third bgworker. I am not seeing entries indicating that those
> stopped, as there is for the first bgworker.
Looking at the logs produced at [1], the worker launched as number 1
would not be able to interact, it connects to the database postgres,
under PID 1616001, and is reported as exited by the postmaster.
The only interacting sessions would be:
1) The bgworker launched as number 2, connected to database testdb.
2) The session checking for pg_stat_activity, launched by
launch_bgworker(). The test was connected with the database we want
to rename, and this could interact as an extra session. This query
could be run while connected to the database postgres to reduce the
friction and discarding this one.
The timestamps of the logs tell that it takes 5 seconds for this host
to get out of the ALTER DATABASE .. RENAME TO, which implies that we
are looping inside CountOtherDBBackends() for 5 seconds. So it really
looks like the second bgworker is the one we are waiting for here.
Now, we are sure of the following things when we try to launch the
RENAME TO:
- The worker is seen in pg_stat_activity.
- The worker is already in worker_spi_main(), per its "LOG initialized
with" entry.
- The worker is connected to the database.
- The worker can receive signals.
How would it be possible for this worker to not receive the requests?
The only thing I could think of is that the postmaster does not have
the time to process the PMSIGNAL_BACKGROUND_WORKER_CHANGE requests?
The next thing would be to gather more data, I guess. The attached
would help in providing more information. If it happens that we are
able to send the requests and that the postmaster does not have the
time to process them, I don't really see what we can do except:
- Drop the portion of the tests for DROP DATABASE, SET TABLESPACE and
RENAME DB, because all these scenarios involve commands that work on
the same database as the worker connected, and if the postmaster does
not have the time to process the termination requests, I don't really
see what we could do. This could also point to a timing issue with
the feature in itself, of course.
- Revert the feature, stop playing with the buildfarm due to the end
of the release cycle, and rework it for v20.
For now I am planning for the attached to get more information from
widowbird, which should take a few days at worst. That would make
clear if we have a timing issue with the requests sent to the
postmaster. Launching the queries for worker_spi_launch() and
pg_stat_activity on the database postgres may also improve things, but
I don't really buy it, even if I may be wrong.
[1]: https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=widowbird&dt=2026-03-17%2015%3A35%3A03
--
Michael
From adb474866f12a66ac48c702d9ab25ce82277abca Mon Sep 17 00:00:00 2001
From: Michael Paquier <[email protected]>
Date: Thu, 19 Mar 2026 09:51:16 +0900
Subject: [PATCH] Add more debugging information for termination tests of
worker_spi
---
src/backend/postmaster/bgworker.c | 6 ++++++
src/test/modules/worker_spi/t/002_worker_terminate.pl | 10 ++++++++--
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 0104a86b9ecd..fd678ef2596d 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -1413,6 +1413,9 @@ TerminateBackgroundWorkersForDatabase(Oid databaseId)
{
bool signal_postmaster = false;
+ elog(DEBUG1, "attempting worker termination for database %u",
+ databaseId);
+
LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
/*
@@ -1432,6 +1435,9 @@ TerminateBackgroundWorkersForDatabase(Oid databaseId)
{
slot->terminate = true;
signal_postmaster = true;
+
+ elog(DEBUG1, "termination requested for worker (PID %d) on database %u",
+ slot->pid, databaseId);
}
}
}
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
index 6db80ffec88c..b0e6a5376d4c 100644
--- a/src/test/modules/worker_spi/t/002_worker_terminate.pl
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -24,7 +24,7 @@ sub launch_bgworker
# Launch a background worker on the given database.
my $pid = $node->safe_psql(
- $database, qq(
+ 'postgres', qq(
SELECT worker_spi_launch($testcase, '$database'::regdatabase, 0, '{}', $interruptible);
));
@@ -32,7 +32,7 @@ sub launch_bgworker
$node->wait_for_log(
qr/LOG: .*worker_spi dynamic worker $testcase initialized with .*\..*/,
$offset);
- my $result = $node->safe_psql($database,
+ my $result = $node->safe_psql('postgres',
"SELECT count(*) > 0 FROM pg_stat_activity WHERE pid = $pid;");
is($result, 't', "dynamic bgworker $testcase launched");
@@ -52,6 +52,11 @@ sub run_bgworker_interruptible_test
qr/terminating background worker \"worker_spi dynamic\" due to administrator command/,
$offset);
+ # Postmaster entry reporting the worker as exiting.
+ $node->wait_for_log(
+ qr/LOG: .*background worker \"worker_spi dynamic\" \(PID $pid\) exited with exit code/,
+ $offset);
+
my $result = $node->safe_psql('postgres',
"SELECT count(*) = 0 FROM pg_stat_activity WHERE pid = $pid;");
is($result, 't', "dynamic bgworker stopped for $testname");
@@ -63,6 +68,7 @@ $node->append_conf(
"postgresql.conf", qq(
autovacuum = off
debug_parallel_query = off
+log_min_messages = debug1
));
$node->start;
--
2.53.0
Attachments:
[text/plain] 0001-Add-more-debugging-information-for-termination-tests.patch (2.7K, 2-0001-Add-more-debugging-information-for-termination-tests.patch)
download | inline diff:
From adb474866f12a66ac48c702d9ab25ce82277abca Mon Sep 17 00:00:00 2001
From: Michael Paquier <[email protected]>
Date: Thu, 19 Mar 2026 09:51:16 +0900
Subject: [PATCH] Add more debugging information for termination tests of
worker_spi
---
src/backend/postmaster/bgworker.c | 6 ++++++
src/test/modules/worker_spi/t/002_worker_terminate.pl | 10 ++++++++--
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 0104a86b9ecd..fd678ef2596d 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -1413,6 +1413,9 @@ TerminateBackgroundWorkersForDatabase(Oid databaseId)
{
bool signal_postmaster = false;
+ elog(DEBUG1, "attempting worker termination for database %u",
+ databaseId);
+
LWLockAcquire(BackgroundWorkerLock, LW_EXCLUSIVE);
/*
@@ -1432,6 +1435,9 @@ TerminateBackgroundWorkersForDatabase(Oid databaseId)
{
slot->terminate = true;
signal_postmaster = true;
+
+ elog(DEBUG1, "termination requested for worker (PID %d) on database %u",
+ slot->pid, databaseId);
}
}
}
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
index 6db80ffec88c..b0e6a5376d4c 100644
--- a/src/test/modules/worker_spi/t/002_worker_terminate.pl
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -24,7 +24,7 @@ sub launch_bgworker
# Launch a background worker on the given database.
my $pid = $node->safe_psql(
- $database, qq(
+ 'postgres', qq(
SELECT worker_spi_launch($testcase, '$database'::regdatabase, 0, '{}', $interruptible);
));
@@ -32,7 +32,7 @@ sub launch_bgworker
$node->wait_for_log(
qr/LOG: .*worker_spi dynamic worker $testcase initialized with .*\..*/,
$offset);
- my $result = $node->safe_psql($database,
+ my $result = $node->safe_psql('postgres',
"SELECT count(*) > 0 FROM pg_stat_activity WHERE pid = $pid;");
is($result, 't', "dynamic bgworker $testcase launched");
@@ -52,6 +52,11 @@ sub run_bgworker_interruptible_test
qr/terminating background worker \"worker_spi dynamic\" due to administrator command/,
$offset);
+ # Postmaster entry reporting the worker as exiting.
+ $node->wait_for_log(
+ qr/LOG: .*background worker \"worker_spi dynamic\" \(PID $pid\) exited with exit code/,
+ $offset);
+
my $result = $node->safe_psql('postgres',
"SELECT count(*) = 0 FROM pg_stat_activity WHERE pid = $pid;");
is($result, 't', "dynamic bgworker stopped for $testname");
@@ -63,6 +68,7 @@ $node->append_conf(
"postgresql.conf", qq(
autovacuum = off
debug_parallel_query = off
+log_min_messages = debug1
));
$node->start;
--
2.53.0
[application/pgp-signature] signature.asc (833B, 3-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-03-21 02:46 Michael Paquier <[email protected]>
parent: Michael Paquier <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Michael Paquier @ 2026-03-21 02:46 UTC (permalink / raw)
To: Tom Lane <[email protected]>; +Cc: Alexander Lakhin <[email protected]>; Iwata, Aya/岩田 彩 <[email protected]>; Peter Smith <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; pgsql-hackers
On Thu, Mar 19, 2026 at 09:54:04AM +0900, Michael Paquier wrote:
> For now I am planning for the attached to get more information from
> widowbird, which should take a few days at worst. That would make
> clear if we have a timing issue with the requests sent to the
> postmaster. Launching the queries for worker_spi_launch() and
> pg_stat_activity on the database postgres may also improve things, but
> I don't really buy it, even if I may be wrong.
A couple of days later after 79a5911fe65b, widowbird seems to have
cooled down a bit:
https://buildfarm.postgresql.org/cgi-bin/show_history.pl?nm=widowbird&br=master
The extra debugging information is proving to be useful. For example,
looking at this one for the RENAME TO case:
https://buildfarm.postgresql.org/cgi-bin/show_stage_log.pl?nm=widowbird&dt=2026-03-19%2003%3A35%...
And some problematic pattern logs:
2026-03-19 04:49:07.848 UTC [271557:4] 002_worker_terminate.pl LOG:
statement: ALTER DATABASE testdb RENAME TO renameddb
2026-03-19 04:49:07.849 UTC [271557:5] 002_worker_terminate.pl DEBUG:
attempting worker termination for database 16413
2026-03-19 04:49:07.849 UTC [271557:6] 002_worker_terminate.pl DEBUG:
termination requested for worker (PID 271553) on database 16413
[...]
2026-03-19 04:49:08.732 UTC [271557:19] 002_worker_terminate.pl DEBUG:
attempting worker termination for database 16413
2026-03-19 04:49:08.732 UTC [271557:20] 002_worker_terminate.pl DEBUG:
termination requested for worker (PID 271553) on database 16413
So we are able to send the requests to the workers, and these can take
a long time before being processed by the postmaster. Querying
directly "postgres" for the worker_spi_launch() and pg_stat_activity
queries seems to have reduced the friction, with less requests to
send. However, I don't think that this is the end of the story, even
after 79a5911fe65b I have spotted one case of RENAME TO where the
requests were sent for a bit more than 4s, before the postmaster had
the idea to catch up. RENAME TO is the only one that can get slow
(really no idea why), so I guess that we could always tweak things a
bit more:
1) Extra injection point to increase the timeout (30s or 60s?) and
give the postmaster more room to proceed the requests.
2) Remove this portion of the test, but it would be sad.
I'll keep an eye for more failures, even if the situation is looking
slightly better.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, 2-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-03-31 07:00 Alexander Lakhin <[email protected]>
parent: Michael Paquier <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Alexander Lakhin @ 2026-03-31 07:00 UTC (permalink / raw)
To: Michael Paquier <[email protected]>; Tom Lane <[email protected]>; +Cc: Iwata, Aya/岩田 彩 <[email protected]>; Peter Smith <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; pgsql-hackers
Hello Michael,
21.03.2026 04:46, Michael Paquier wrote:
> So we are able to send the requests to the workers, and these can take
> a long time before being processed by the postmaster. Querying
> directly "postgres" for the worker_spi_launch() and pg_stat_activity
> queries seems to have reduced the friction, with less requests to
> send. However, I don't think that this is the end of the story, even
> after 79a5911fe65b I have spotted one case of RENAME TO where the
> requests were sent for a bit more than 4s, before the postmaster had
> the idea to catch up. RENAME TO is the only one that can get slow
> (really no idea why), so I guess that we could always tweak things a
> bit more:
> 1) Extra injection point to increase the timeout (30s or 60s?) and
> give the postmaster more room to proceed the requests.
> 2) Remove this portion of the test, but it would be sad.
>
> I'll keep an eye for more failures, even if the situation is looking
> slightly better.
Having reproduced this locally (running 3 tests in parallel with
ALTER DATABASE RENAME repeated 200 times, on a slow riscv64 machine), I
discovered that in the bad case the worker doesn't reach the main loop in
time (and CHECK_FOR_INTERRUPTS() inside it), because it doesn't get out of
initialize_worker_spi() -> CommitTransactionCommand().
With this modification:
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3752,3 +3752,3 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
*/
- int ntries = 50;
+ int ntries = 500;
@@ -3798,3 +3798,6 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
if (!found)
+{
+elog(LOG, "!!!CountOtherDBBackends| found no backends, try %d", tries);
return false; /* no conflicting backends, so done */
+}
I can see the following:
... !!!CountOtherDBBackends| found no backends, try 1
# most of the calls (200 of 201) succeeded with try 1, but there are also:
... !!!CountOtherDBBackends| found no backends, try 7
... !!!CountOtherDBBackends| found no backends, try 51
... !!!CountOtherDBBackends| found no backends, try 74
... !!!CountOtherDBBackends| found no backends, try 84
So the backend is not completely stuck, but CommitTransactionCommand()
may take more than 5 seconds under some circumstances (maybe it's worth
investigating which exactly).
Best regards,
Alexander
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-03-31 10:54 Michael Paquier <[email protected]>
parent: Alexander Lakhin <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Michael Paquier @ 2026-03-31 10:54 UTC (permalink / raw)
To: Alexander Lakhin <[email protected]>; +Cc: Tom Lane <[email protected]>; Iwata, Aya/岩田 彩 <[email protected]>; Peter Smith <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; pgsql-hackers
On Tue, Mar 31, 2026 at 10:00:00AM +0300, Alexander Lakhin wrote:
> So the backend is not completely stuck, but CommitTransactionCommand()
> may take more than 5 seconds under some circumstances (maybe it's worth
> investigating which exactly).
One could blame slow hardware, difficult to say, and I'm puzzled by
these periodic bumps that don't seem to happen elsewhere. There is at
least one alternative that I can think here to make the test more
stable and make sure that worker_spi reaches its main loop before
attempting the database commands. We could add a wait_for_event() at
the end of launch_bgworker() based on WorkerSpiMain, and enlarge
worker_spi_naptime to an insanely larger value, to make sure that we
remain on the WaitLatch() until the worker is interrupted and that we
don't attempt a new transaction.
--
Michael
Attachments:
[application/pgp-signature] signature.asc (833B, 2-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-03-31 17:00 Alexander Lakhin <[email protected]>
parent: Michael Paquier <[email protected]>
0 siblings, 1 reply; 67+ messages in thread
From: Alexander Lakhin @ 2026-03-31 17:00 UTC (permalink / raw)
To: Michael Paquier <[email protected]>; +Cc: Tom Lane <[email protected]>; Iwata, Aya/岩田 彩 <[email protected]>; Peter Smith <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; pgsql-hackers
31.03.2026 13:54, Michael Paquier wrote:
> On Tue, Mar 31, 2026 at 10:00:00AM +0300, Alexander Lakhin wrote:
>> So the backend is not completely stuck, but CommitTransactionCommand()
>> may take more than 5 seconds under some circumstances (maybe it's worth
>> investigating which exactly).
> One could blame slow hardware, difficult to say, and I'm puzzled by
> these periodic bumps that don't seem to happen elsewhere.
I managed to get the backtrace of such a sluggish backend:
Using host libthread_db library "/lib/riscv64-linux-gnu/libthread_db.so.1".
0x0000003fb1f4cc26 in posix_fadvise64 () from /lib/riscv64-linux-gnu/libc.so.6
Id Target Id Frame
* 1 Thread 0x3fb2a4c620 (LWP 564194) "postgres" 0x0000003fb1f4cc26 in posix_fadvise64 () from
/lib/riscv64-linux-gnu/libc.so.6
#0 0x0000003fb1f4cc26 in posix_fadvise64 () from /lib/riscv64-linux-gnu/libc.so.6
#1 0x0000002abef79444 in XLogFileClose () at xlog.c:3672
#2 0x0000002abef7cc66 in XLogWrite (WriteRqst=..., tli=tli@entry=1, flexible=flexible@entry=false) at xlog.c:2356
#3 0x0000002abef7dbfc in XLogFlush (record=33561688) at xlog.c:2892
#4 0x0000002abef77976 in RecordTransactionCommit () at xact.c:1516
#5 CommitTransaction () at xact.c:2379
#6 0x0000002abef78938 in CommitTransactionCommandInternal () at xact.c:3224
#7 0x0000002abef78acc in CommitTransactionCommand () at xact.c:3185
#8 0x0000003fb2a3ed88 in initialize_worker_spi (table=0x2abf8bf358) at worker_spi.c:132
#9 worker_spi_main (main_arg=<optimized out>) at worker_spi.c:181
....
(Three test runs produced the same stack trace.)
I think this can explain slow CommitTransactionCommand() and why it
happens not every time. Regarding other animals, I guess they can
experience the same bumps but not exceeding 5 seconds (50 tries). Thus,
from my understanding, for the failure to happen, we need to have slow
storage and initialize_worker_spi() -> CommitTransactionCommand() reaching
XLogFileClose().
Best regards,
Alexander
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-04-01 20:20 Tom Lane <[email protected]>
parent: Alexander Lakhin <[email protected]>
0 siblings, 2 replies; 67+ messages in thread
From: Tom Lane @ 2026-04-01 20:20 UTC (permalink / raw)
To: Alexander Lakhin <[email protected]>; +Cc: Michael Paquier <[email protected]>; Iwata, Aya/岩田 彩 <[email protected]>; Peter Smith <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; pgsql-hackers
Alexander Lakhin <[email protected]> writes:
> I think this can explain slow CommitTransactionCommand() and why it
> happens not every time. Regarding other animals, I guess they can
> experience the same bumps but not exceeding 5 seconds (50 tries). Thus,
> from my understanding, for the failure to happen, we need to have slow
> storage and initialize_worker_spi() -> CommitTransactionCommand() reaching
> XLogFileClose().
So, it remains not very clear why only widowbird is showing this
failure, but I think we can safely take away the bottom-line
conclusion that hard-wiring a maximum wait of 5s in
CountOtherDBBackends() was not a great idea.
I don't think I want to propose a GUC for this, but could it make
sense to check for an environment variable, similarly to PGCTLTIMEOUT
and PG_TEST_TIMEOUT_DEFAULT? Whichever way we do it, it could replace
the existing crude hack to change the max wait in injection-point
mode.
regards, tom lane
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-04-02 03:20 Hayato Kuroda (Fujitsu) <[email protected]>
parent: Tom Lane <[email protected]>
1 sibling, 2 replies; 67+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2026-04-02 03:20 UTC (permalink / raw)
To: 'Tom Lane' <[email protected]>; Alexander Lakhin <[email protected]>; Michael Paquier <[email protected]>; +Cc: Aya Iwata (Fujitsu) <[email protected]>; Peter Smith <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; pgsql-hackers
Dear hackers,
Thanks for paying attention the thread.
I've been considering why the XLogFileClose() is slow only on widowbird but I
have no idea. But on this thread, I think we can just put a workaround for
stabilization.
> I don't think I want to propose a GUC for this, but could it make
> sense to check for an environment variable, similarly to PGCTLTIMEOUT
> and PG_TEST_TIMEOUT_DEFAULT? Whichever way we do it, it could replace
> the existing crude hack to change the max wait in injection-point
> mode.
I feel it might be a better approach, attached one implemented the idea.
I did not use the term "_TEST_" because such env variables are only referred
in test modules or binaries. But I did not update the documentation.
Best regards,
Hayato Kuroda
FUJITSU LIMITED
Attachments:
[application/octet-stream] 0001-Introduce-PG_BACKEND_WAIT_MAX_RETRIES.patch (5.0K, 2-0001-Introduce-PG_BACKEND_WAIT_MAX_RETRIES.patch)
download | inline diff:
From 1703fa5247615d5d8e6d223f94606e951548b72d Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <[email protected]>
Date: Thu, 2 Apr 2026 12:10:01 +0900
Subject: [PATCH] Introduce PG_BACKEND_WAIT_MAX_RETRIES
---
src/backend/storage/ipc/procarray.c | 27 +++++++++------
.../worker_spi/t/002_worker_terminate.pl | 34 +++++--------------
2 files changed, 25 insertions(+), 36 deletions(-)
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 033ae68b57e..3e6df3780fd 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -3777,11 +3777,12 @@ CountUserBackends(Oid roleid)
/*
* CountOtherDBBackends -- check for other backends running in the given DB
*
- * If there are other backends in the DB, we will wait a maximum of 5 seconds
- * for them to exit (or 0.3s for testing purposes). Autovacuum backends are
- * encouraged to exit early by sending them SIGTERM, but normal user backends
- * are just waited for. If background workers connected to this database are
- * marked as interruptible, they are terminated.
+ * If there are other backends in the DB, we wait up to 50 times with 100ms
+ * between attempts (5s total by default). For test purposes, the retry
+ * count can be overridden with PG_BACKEND_WAIT_MAX_RETRIES. Autovacuum
+ * backends are encouraged to exit early by sending them SIGTERM, but normal
+ * user backends are just waited for. If background workers connected to this
+ * database are marked as interruptible, they are terminated.
*
* The current backend is always ignored; it is caller's responsibility to
* check whether the current backend uses the given DB, if it's important.
@@ -3808,15 +3809,19 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
int autovac_pids[MAXAUTOVACPIDS];
/*
- * Retry up to 50 times with 100ms between attempts (max 5s total). Can be
- * reduced to 3 attempts (max 0.3s total) to speed up tests.
+ * Retry up to 50 times with 100ms between attempts (max 5s total) by
+ * default. Use PG_BACKEND_WAIT_MAX_RETRIES if it's set.
*/
int ntries = 50;
+ char *env_wait;
-#ifdef USE_INJECTION_POINTS
- if (IS_INJECTION_POINT_ATTACHED("procarray-reduce-count"))
- ntries = 3;
-#endif
+ env_wait = getenv("PG_BACKEND_WAIT_MAX_RETRIES");
+ if (env_wait != NULL)
+ {
+ int val = atoi(env_wait);
+ if (val > 0)
+ ntries = val;
+ }
for (int tries = 0; tries < ntries; tries++)
{
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
index b0e6a5376d4..bf9d663ca1f 100644
--- a/src/test/modules/worker_spi/t/002_worker_terminate.pl
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -8,13 +8,6 @@ use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
-# This test depends on injection points to detect whether background workers
-# remain.
-if ($ENV{enable_injection_points} ne 'yes')
-{
- plan skip_all => 'Injection points not supported by this build';
-}
-
# Ensure the worker_spi dynamic worker is launched on the specified database.
# Returns the PID of the worker launched.
sub launch_bgworker
@@ -70,15 +63,12 @@ autovacuum = off
debug_parallel_query = off
log_min_messages = debug1
));
-$node->start;
-# Check if the extension injection_points is available, as it may be
-# possible that this script is run with installcheck, where the module
-# would not be installed by default.
-if (!$node->check_extension('injection_points'))
-{
- plan skip_all => 'Extension injection_points not installed';
-}
+# Reduce the number of backend retries, allowing for shorter test runs. See
+# CountOtherDBBackends().
+$ENV{PG_BACKEND_WAIT_MAX_RETRIES} = 3;
+
+$node->start;
$node->safe_psql('postgres', 'CREATE EXTENSION worker_spi;');
@@ -88,12 +78,6 @@ my $pid = launch_bgworker($node, 'postgres', 0, 'false');
# Ensure CREATE DATABASE WITH TEMPLATE fails because a non-interruptible
# bgworker exists.
-# The injection point 'procarray-reduce-count' reduces the number of backend
-# retries, allowing for shorter test runs. See CountOtherDBBackends().
-$node->safe_psql('postgres', "CREATE EXTENSION injection_points;");
-$node->safe_psql('postgres',
- "SELECT injection_points_attach('procarray-reduce-count', 'error');");
-
my $stderr;
$node->psql(
@@ -119,13 +103,13 @@ $node->safe_psql(
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity WHERE backend_type = 'worker_spi dynamic';));
-# The injection point is not used anymore, release it.
-$node->safe_psql('postgres',
- "SELECT injection_points_detach('procarray-reduce-count');");
-
# Check that BGWORKER_INTERRUPTIBLE allows background workers to be
# terminated with database-related commands.
+# Make the wait time longer than default to stabilize some builfarm clients
+$ENV{PG_BACKEND_WAIT_MAX_RETRIES} = 100;
+$node->restart;
+
# Test case 1: CREATE DATABASE WITH TEMPLATE
$pid = launch_bgworker($node, 'postgres', 1, 'true');
run_bgworker_interruptible_test(
--
2.47.3
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-04-02 04:00 Alexander Lakhin <[email protected]>
parent: Tom Lane <[email protected]>
1 sibling, 1 reply; 67+ messages in thread
From: Alexander Lakhin @ 2026-04-02 04:00 UTC (permalink / raw)
To: Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; +Cc: Michael Paquier <[email protected]>; Iwata, Aya/岩田 彩 <[email protected]>; Peter Smith <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; pgsql-hackers
Hello Tom and Tomas,
01.04.2026 23:20, Tom Lane wrote:
> Alexander Lakhin<[email protected]> writes:
>> I think this can explain slow CommitTransactionCommand() and why it
>> happens not every time. Regarding other animals, I guess they can
>> experience the same bumps but not exceeding 5 seconds (50 tries). Thus,
>> from my understanding, for the failure to happen, we need to have slow
>> storage and initialize_worker_spi() -> CommitTransactionCommand() reaching
>> XLogFileClose().
> So, it remains not very clear why only widowbird is showing this
> failure, but I think we can safely take away the bottom-line
> conclusion that hard-wiring a maximum wait of 5s in
> CountOtherDBBackends() was not a great idea.
There also were two failures from jay: [1], [2], but yes, widowbird is
getting more and more consistent in that aspect: [3], probably because
of the storage (SD card?) degradation.
Tomas, maybe you could check if the write speed is more or less acceptable
there?
Regarding the test-specific fix, let me remind of the other failure from
widowbird: [4].
[1] https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=jay&dt=2026-03-10%2019%3A26%3A19
[2] https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=jay&dt=2026-03-24%2020%3A43%3A24
[3] https://buildfarm.postgresql.org/cgi-bin/show_history.pl?nm=widowbird&br=master
[4] https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=widowbird&dt=2025-10-25%2010%3A35%3A03
Best regards,
Alexander
^ permalink raw reply [nested|flat] 67+ messages in thread
* RE: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-04-02 06:57 Hayato Kuroda (Fujitsu) <[email protected]>
parent: Hayato Kuroda (Fujitsu) <[email protected]>
1 sibling, 0 replies; 67+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2026-04-02 06:57 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; 'Tom Lane' <[email protected]>; Alexander Lakhin <[email protected]>; Michael Paquier <[email protected]>; +Cc: Aya Iwata (Fujitsu) <[email protected]>; Peter Smith <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; pgsql-hackers
> I feel it might be a better approach, attached one implemented the idea.
Note - the patch just set the env variable to the larger value at the later part,
but of course we can restore the value set on the user.
There we must do is to shorten the test time at the beginning then reset it.
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-04-02 11:26 Tomas Vondra <[email protected]>
parent: Alexander Lakhin <[email protected]>
0 siblings, 0 replies; 67+ messages in thread
From: Tomas Vondra @ 2026-04-02 11:26 UTC (permalink / raw)
To: Alexander Lakhin <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; +Cc: Michael Paquier <[email protected]>; Iwata, Aya/岩田 彩 <[email protected]>; Peter Smith <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; pgsql-hackers
On 4/2/26 06:00, Alexander Lakhin wrote:
> Hello Tom and Tomas,
>
> 01.04.2026 23:20, Tom Lane wrote:
>> Alexander Lakhin <[email protected]> writes:
>>> I think this can explain slow CommitTransactionCommand() and why it
>>> happens not every time. Regarding other animals, I guess they can
>>> experience the same bumps but not exceeding 5 seconds (50 tries). Thus,
>>> from my understanding, for the failure to happen, we need to have slow
>>> storage and initialize_worker_spi() -> CommitTransactionCommand() reaching
>>> XLogFileClose().
>> So, it remains not very clear why only widowbird is showing this
>> failure, but I think we can safely take away the bottom-line
>> conclusion that hard-wiring a maximum wait of 5s in
>> CountOtherDBBackends() was not a great idea.
>
> There also were two failures from jay: [1], [2], but yes, widowbird is
> getting more and more consistent in that aspect: [3], probably because
> of the storage (SD card?) degradation.
>
Jay is a regular machine, 2-core VM hosted at a university, so not very
powerful but was running for years just fine and seems to be healthy.
> Tomas, maybe you could check if the write speed is more or less acceptable
> there?
>
Will do. I'll wait for the tests to complete on widowbird, and will do
some testing on the storage (it's running from a flash drive, not SD
card, and I saw stuff in dmesg when it was dying in the past - but now
it's clean).
regards
--
Tomas Vondra
^ permalink raw reply [nested|flat] 67+ messages in thread
* Re: [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE
@ 2026-04-05 11:47 Michael Paquier <[email protected]>
parent: Hayato Kuroda (Fujitsu) <[email protected]>
1 sibling, 0 replies; 67+ messages in thread
From: Michael Paquier @ 2026-04-05 11:47 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: 'Tom Lane' <[email protected]>; Alexander Lakhin <[email protected]>; Aya Iwata (Fujitsu) <[email protected]>; Peter Smith <[email protected]>; Pavel Stehule <[email protected]>; Chao Li <[email protected]>; pgsql-hackers
On Thu, Apr 02, 2026 at 03:20:59AM +0000, Hayato Kuroda (Fujitsu) wrote:
> Thanks for paying attention the thread.
> I've been considering why the XLogFileClose() is slow only on widowbird but I
> have no idea. But on this thread, I think we can just put a workaround for
> stabilization.
I am not convinced that we have a need for an environment variable for
this case, TBH. It would be enough to make sure that the spawned
worker is paused on its WaitLatch(), with a naptime large enough to
allow the initial transaction to commit, as in the attached. When the
interruption is run, the worker would stop at CHECK_FOR_INTERRUPTS(),
before beginning a new transaction.
--
Michael
From fe0a2284d87930cd18d13ff4c473f96de5a965e8 Mon Sep 17 00:00:00 2001
From: Michael Paquier <[email protected]>
Date: Sun, 5 Apr 2026 20:45:16 +0900
Subject: [PATCH] Improve stability of worker_spi termination test
This switches the test to wait for the spawned bgworkers to reach their
main loops, napping. worker_spi.naptime is enlarged to 10 minutes to
give enough room for slow machines to reach this state.
---
.../worker_spi/t/002_worker_terminate.pl | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
index b0e6a5376d4c..a7d507cc0596 100644
--- a/src/test/modules/worker_spi/t/002_worker_terminate.pl
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -20,7 +20,6 @@ if ($ENV{enable_injection_points} ne 'yes')
sub launch_bgworker
{
my ($node, $database, $testcase, $interruptible) = @_;
- my $offset = -s $node->logfile;
# Launch a background worker on the given database.
my $pid = $node->safe_psql(
@@ -28,13 +27,11 @@ sub launch_bgworker
SELECT worker_spi_launch($testcase, '$database'::regdatabase, 0, '{}', $interruptible);
));
- # Check that the bgworker is initialized.
- $node->wait_for_log(
- qr/LOG: .*worker_spi dynamic worker $testcase initialized with .*\..*/,
- $offset);
- my $result = $node->safe_psql('postgres',
- "SELECT count(*) > 0 FROM pg_stat_activity WHERE pid = $pid;");
- is($result, 't', "dynamic bgworker $testcase launched");
+ # Check that the bgworker is initialized and napping.
+ my $result = $node->poll_query_until('postgres',
+ qq[SELECT wait_event FROM pg_stat_activity WHERE pid = $pid;],
+ qq[WorkerSpiMain]);
+ is($result, 1, "dynamic bgworker $testcase launched");
return $pid;
}
@@ -64,11 +61,15 @@ sub run_bgworker_interruptible_test
my $node = PostgreSQL::Test::Cluster->new('mynode');
$node->init;
+# The naptime is large enough to give room on slow machines to reach
+# the main loop of the spawned workers, after these initialize their
+# schemas.
$node->append_conf(
"postgresql.conf", qq(
autovacuum = off
debug_parallel_query = off
log_min_messages = debug1
+worker_spi.naptime = 600
));
$node->start;
--
2.53.0
Attachments:
[text/plain] 0001-Improve-stability-of-worker_spi-termination-test.patch (2.2K, 2-0001-Improve-stability-of-worker_spi-termination-test.patch)
download | inline diff:
From fe0a2284d87930cd18d13ff4c473f96de5a965e8 Mon Sep 17 00:00:00 2001
From: Michael Paquier <[email protected]>
Date: Sun, 5 Apr 2026 20:45:16 +0900
Subject: [PATCH] Improve stability of worker_spi termination test
This switches the test to wait for the spawned bgworkers to reach their
main loops, napping. worker_spi.naptime is enlarged to 10 minutes to
give enough room for slow machines to reach this state.
---
.../worker_spi/t/002_worker_terminate.pl | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/src/test/modules/worker_spi/t/002_worker_terminate.pl b/src/test/modules/worker_spi/t/002_worker_terminate.pl
index b0e6a5376d4c..a7d507cc0596 100644
--- a/src/test/modules/worker_spi/t/002_worker_terminate.pl
+++ b/src/test/modules/worker_spi/t/002_worker_terminate.pl
@@ -20,7 +20,6 @@ if ($ENV{enable_injection_points} ne 'yes')
sub launch_bgworker
{
my ($node, $database, $testcase, $interruptible) = @_;
- my $offset = -s $node->logfile;
# Launch a background worker on the given database.
my $pid = $node->safe_psql(
@@ -28,13 +27,11 @@ sub launch_bgworker
SELECT worker_spi_launch($testcase, '$database'::regdatabase, 0, '{}', $interruptible);
));
- # Check that the bgworker is initialized.
- $node->wait_for_log(
- qr/LOG: .*worker_spi dynamic worker $testcase initialized with .*\..*/,
- $offset);
- my $result = $node->safe_psql('postgres',
- "SELECT count(*) > 0 FROM pg_stat_activity WHERE pid = $pid;");
- is($result, 't', "dynamic bgworker $testcase launched");
+ # Check that the bgworker is initialized and napping.
+ my $result = $node->poll_query_until('postgres',
+ qq[SELECT wait_event FROM pg_stat_activity WHERE pid = $pid;],
+ qq[WorkerSpiMain]);
+ is($result, 1, "dynamic bgworker $testcase launched");
return $pid;
}
@@ -64,11 +61,15 @@ sub run_bgworker_interruptible_test
my $node = PostgreSQL::Test::Cluster->new('mynode');
$node->init;
+# The naptime is large enough to give room on slow machines to reach
+# the main loop of the spawned workers, after these initialize their
+# schemas.
$node->append_conf(
"postgresql.conf", qq(
autovacuum = off
debug_parallel_query = off
log_min_messages = debug1
+worker_spi.naptime = 600
));
$node->start;
--
2.53.0
[application/pgp-signature] signature.asc (833B, 3-signature.asc)
download
^ permalink raw reply [nested|flat] 67+ messages in thread
end of thread, other threads:[~2026-04-05 11:47 UTC | newest]
Thread overview: 67+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2025-10-03 09:27 [PROPOSAL] Termination of Background Workers for ALTER/DROP DATABASE Aya Iwata (Fujitsu) <[email protected]>
2025-10-06 01:59 ` Hayato Kuroda (Fujitsu) <[email protected]>
2025-10-06 03:17 ` Michael Paquier <[email protected]>
2025-10-06 09:15 ` Hayato Kuroda (Fujitsu) <[email protected]>
2025-10-06 11:21 ` Aya Iwata (Fujitsu) <[email protected]>
2025-10-07 02:00 ` Hayato Kuroda (Fujitsu) <[email protected]>
2025-10-07 03:12 ` Peter Smith <[email protected]>
2025-10-07 10:33 ` Aya Iwata (Fujitsu) <[email protected]>
2025-10-07 11:49 ` Hayato Kuroda (Fujitsu) <[email protected]>
2025-10-07 21:39 ` Peter Smith <[email protected]>
2025-10-08 12:26 ` Aya Iwata (Fujitsu) <[email protected]>
2025-10-08 22:18 ` Peter Smith <[email protected]>
2025-10-09 02:05 ` Hayato Kuroda (Fujitsu) <[email protected]>
2025-10-09 02:28 ` Hayato Kuroda (Fujitsu) <[email protected]>
2025-10-09 03:33 ` Michael Paquier <[email protected]>
2025-10-09 04:00 ` Hayato Kuroda (Fujitsu) <[email protected]>
2025-10-09 04:37 ` Michael Paquier <[email protected]>
2025-10-09 05:47 ` Hayato Kuroda (Fujitsu) <[email protected]>
2025-10-09 13:09 ` Aya Iwata (Fujitsu) <[email protected]>
2025-10-09 22:56 ` Peter Smith <[email protected]>
2025-10-10 02:03 ` Chao Li <[email protected]>
2025-10-10 09:04 ` Aya Iwata (Fujitsu) <[email protected]>
2025-10-10 11:05 ` Andrei Lepikhov <[email protected]>
2025-10-14 04:36 ` Hayato Kuroda (Fujitsu) <[email protected]>
2025-10-16 03:37 ` Michael Paquier <[email protected]>
2025-10-12 22:28 ` Peter Smith <[email protected]>
2025-10-13 01:20 ` Peter Smith <[email protected]>
2025-10-15 02:48 ` Aya Iwata (Fujitsu) <[email protected]>
2025-10-16 03:54 ` Michael Paquier <[email protected]>
2025-10-16 15:04 ` Aya Iwata (Fujitsu) <[email protected]>
2025-10-20 02:01 ` Peter Smith <[email protected]>
2025-10-20 04:32 ` Michael Paquier <[email protected]>
2025-12-24 08:42 ` Aya Iwata (Fujitsu) <[email protected]>
2025-10-21 14:14 ` Aya Iwata (Fujitsu) <[email protected]>
2025-11-20 06:57 ` Aya Iwata (Fujitsu) <[email protected]>
2025-12-14 07:40 ` Pavel Stehule <[email protected]>
2025-12-15 12:56 ` Aya Iwata (Fujitsu) <[email protected]>
2025-12-15 13:18 ` Pavel Stehule <[email protected]>
2025-12-17 13:31 ` Aya Iwata (Fujitsu) <[email protected]>
2025-12-17 15:33 ` Pavel Stehule <[email protected]>
2025-12-18 11:47 ` Aya Iwata (Fujitsu) <[email protected]>
2025-12-18 16:04 ` Pavel Stehule <[email protected]>
2025-12-25 13:24 ` Aya Iwata (Fujitsu) <[email protected]>
2025-12-26 10:17 ` Ryo Matsumura (Fujitsu) <[email protected]>
2025-12-26 11:04 ` Pavel Stehule <[email protected]>
2025-12-29 00:52 ` Michael Paquier <[email protected]>
2025-12-29 05:31 ` Pavel Stehule <[email protected]>
2025-12-29 02:03 ` Michael Paquier <[email protected]>
2026-03-13 11:38 ` Aya Iwata (Fujitsu) <[email protected]>
2026-03-16 07:23 ` Michael Paquier <[email protected]>
2026-03-16 23:37 ` Michael Paquier <[email protected]>
2026-03-17 01:55 ` =?iso-2022-jp?B?S3Vyb2RhLCBIYXlhdG8vGyRCOXVFRBsoQiAbJEJIOz9NGyhC?= <[email protected]>
2026-03-17 03:57 ` Michael Paquier <[email protected]>
2026-03-17 06:00 ` Alexander Lakhin <[email protected]>
2026-03-17 08:33 ` Michael Paquier <[email protected]>
2026-03-19 00:54 ` Michael Paquier <[email protected]>
2026-03-21 02:46 ` Michael Paquier <[email protected]>
2026-03-31 07:00 ` Alexander Lakhin <[email protected]>
2026-03-31 10:54 ` Michael Paquier <[email protected]>
2026-03-31 17:00 ` Alexander Lakhin <[email protected]>
2026-04-01 20:20 ` Tom Lane <[email protected]>
2026-04-02 03:20 ` Hayato Kuroda (Fujitsu) <[email protected]>
2026-04-02 06:57 ` Hayato Kuroda (Fujitsu) <[email protected]>
2026-04-05 11:47 ` Michael Paquier <[email protected]>
2026-04-02 04:00 ` Alexander Lakhin <[email protected]>
2026-04-02 11:26 ` Tomas Vondra <[email protected]>
2025-10-15 03:37 ` Chao Li <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox