Skip to content

Commit 7f7485a

Browse files
committed
Allow background workers to be started dynamically.
There is a new API, RegisterDynamicBackgroundWorker, which allows an ordinary user backend to register a new background writer during normal running. This means that it's no longer necessary for all background workers to be registered during processing of shared_preload_libraries, although the option of registering workers at that time remains available. When a background worker exits and will not be restarted, the slot previously used by that background worker is automatically released and becomes available for reuse. Slots used by background workers that are configured for automatic restart can't (yet) be released without shutting down the system. This commit adds a new source file, bgworker.c, and moves some of the existing control logic for background workers there. Previously, there was little enough logic that it made sense to keep everything in postmaster.c, but not any more. This commit also makes the worker_spi contrib module into an extension and adds a new function, worker_spi_launch, which can be used to demonstrate the new facility.
1 parent 233bfe0 commit 7f7485a

File tree

13 files changed

+710
-209
lines changed

13 files changed

+710
-209
lines changed

contrib/worker_spi/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
MODULES = worker_spi
44

5+
EXTENSION = worker_spi
6+
DATA = worker_spi--1.0.sql
7+
58
ifdef USE_PGXS
69
PG_CONFIG = pg_config
710
PGXS := $(shell $(PG_CONFIG) --pgxs)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/* contrib/worker_spi/worker_spi--1.0.sql */
2+
3+
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
4+
\echo Use "CREATE EXTENSION worker_spi" to load this file. \quit
5+
6+
CREATE FUNCTION worker_spi_launch(pg_catalog.int4)
7+
RETURNS pg_catalog.bool STRICT
8+
AS 'MODULE_PATHNAME'
9+
LANGUAGE C;

contrib/worker_spi/worker_spi.c

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@
4242
#include "tcop/utility.h"
4343

4444
PG_MODULE_MAGIC;
45+
PG_FUNCTION_INFO_V1(worker_spi_launch);
4546

4647
void _PG_init(void);
48+
void worker_spi_main(Datum);
49+
Datum worker_spi_launch(PG_FUNCTION_ARGS);
4750

4851
/* flags set by signal handlers */
4952
static volatile sig_atomic_t got_sighup = false;
@@ -153,11 +156,22 @@ initialize_worker_spi(worktable *table)
153156
pgstat_report_activity(STATE_IDLE, NULL);
154157
}
155158

156-
static void
157-
worker_spi_main(void *main_arg)
159+
void
160+
worker_spi_main(Datum main_arg)
158161
{
159-
worktable *table = (worktable *) main_arg;
162+
int index = DatumGetInt32(main_arg);
163+
worktable *table;
160164
StringInfoData buf;
165+
char name[20];
166+
167+
table = palloc(sizeof(worktable));
168+
sprintf(name, "schema%d", index);
169+
table->schema = pstrdup(name);
170+
table->name = pstrdup("counted");
171+
172+
/* Establish signal handlers before unblocking signals. */
173+
pqsignal(SIGHUP, worker_spi_sighup);
174+
pqsignal(SIGTERM, worker_spi_sigterm);
161175

162176
/* We're now ready to receive signals */
163177
BackgroundWorkerUnblockSignals();
@@ -279,7 +293,7 @@ worker_spi_main(void *main_arg)
279293
pgstat_report_activity(STATE_IDLE, NULL);
280294
}
281295

282-
proc_exit(0);
296+
proc_exit(1);
283297
}
284298

285299
/*
@@ -292,9 +306,7 @@ void
292306
_PG_init(void)
293307
{
294308
BackgroundWorker worker;
295-
worktable *table;
296309
unsigned int i;
297-
char name[20];
298310

299311
/* get the configuration */
300312
DefineCustomIntVariable("worker_spi.naptime",
@@ -309,6 +321,10 @@ _PG_init(void)
309321
NULL,
310322
NULL,
311323
NULL);
324+
325+
if (!process_shared_preload_libraries_in_progress)
326+
return;
327+
312328
DefineCustomIntVariable("worker_spi.total_workers",
313329
"Number of workers.",
314330
NULL,
@@ -328,23 +344,41 @@ _PG_init(void)
328344
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
329345
worker.bgw_restart_time = BGW_NEVER_RESTART;
330346
worker.bgw_main = worker_spi_main;
331-
worker.bgw_sighup = worker_spi_sighup;
332-
worker.bgw_sigterm = worker_spi_sigterm;
347+
worker.bgw_sighup = NULL;
348+
worker.bgw_sigterm = NULL;
333349

334350
/*
335351
* Now fill in worker-specific data, and do the actual registrations.
336352
*/
337353
for (i = 1; i <= worker_spi_total_workers; i++)
338354
{
339-
sprintf(name, "worker %d", i);
340-
worker.bgw_name = pstrdup(name);
341-
342-
table = palloc(sizeof(worktable));
343-
sprintf(name, "schema%d", i);
344-
table->schema = pstrdup(name);
345-
table->name = pstrdup("counted");
346-
worker.bgw_main_arg = (void *) table;
355+
snprintf(worker.bgw_name, BGW_MAXLEN, "worker %d", i);
356+
worker.bgw_main_arg = Int32GetDatum(i);
347357

348358
RegisterBackgroundWorker(&worker);
349359
}
350360
}
361+
362+
/*
363+
* Dynamically launch an SPI worker.
364+
*/
365+
Datum
366+
worker_spi_launch(PG_FUNCTION_ARGS)
367+
{
368+
int32 i = PG_GETARG_INT32(0);
369+
BackgroundWorker worker;
370+
371+
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
372+
BGWORKER_BACKEND_DATABASE_CONNECTION;
373+
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
374+
worker.bgw_restart_time = BGW_NEVER_RESTART;
375+
worker.bgw_main = NULL; /* new worker might not have library loaded */
376+
sprintf(worker.bgw_library_name, "worker_spi");
377+
sprintf(worker.bgw_function_name, "worker_spi_main");
378+
worker.bgw_sighup = NULL; /* new worker might not have library loaded */
379+
worker.bgw_sigterm = NULL; /* new worker might not have library loaded */
380+
snprintf(worker.bgw_name, BGW_MAXLEN, "worker %d", i);
381+
worker.bgw_main_arg = Int32GetDatum(i);
382+
383+
PG_RETURN_BOOL(RegisterDynamicBackgroundWorker(&worker));
384+
}

contrib/worker_spi/worker_spi.control

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# worker_spi extension
2+
comment = 'Sample background worker'
3+
default_version = '1.0'
4+
module_pathname = '$libdir/worker_spi'
5+
relocatable = true

doc/src/sgml/bgworker.sgml

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,35 @@
3030
</warning>
3131

3232
<para>
33-
Only modules listed in <varname>shared_preload_libraries</> can run
34-
background workers. A module wishing to run a background worker needs
35-
to register it by calling
33+
Background workers can be initialized at the time that
34+
<productname>PostgreSQL</> is started including the module name in
35+
<varname>shared_preload_libraries</>. A module wishing to run a background
36+
worker can register it by calling
3637
<function>RegisterBackgroundWorker(<type>BackgroundWorker *worker</type>)</function>
37-
from its <function>_PG_init()</>.
38+
from its <function>_PG_init()</>. Background workers can also be started
39+
after the system is up and running by calling the function
40+
<function>RegisterDynamicBackgroundWorker</function>(<type>BackgroundWorker
41+
*worker</type>). Unlike <function>RegisterBackgroundWorker</>, which can
42+
only be called from within the postmaster,
43+
<function>RegisterDynamicBackgroundWorker</function> must be called from
44+
a regular backend.
45+
</para>
46+
47+
<para>
3848
The structure <structname>BackgroundWorker</structname> is defined thus:
3949
<programlisting>
4050
typedef void (*bgworker_main_type)(void *main_arg);
4151
typedef void (*bgworker_sighdlr_type)(SIGNAL_ARGS);
4252
typedef struct BackgroundWorker
4353
{
44-
char *bgw_name;
54+
char bgw_name[BGW_MAXLEN];
4555
int bgw_flags;
4656
BgWorkerStartTime bgw_start_time;
4757
int bgw_restart_time; /* in seconds, or BGW_NEVER_RESTART */
48-
bgworker_main_type bgw_main;
49-
void *bgw_main_arg;
58+
bgworker_main_type bgw_main;
59+
char bgw_library_name[BGW_MAXLEN]; /* only if bgw_main is NULL */
60+
char bgw_function_name[BGW_MAXLEN]; /* only if bgw_main is NULL */
61+
Datum bgw_main_arg;
5062
bgworker_sighdlr_type bgw_sighup;
5163
bgworker_sighdlr_type bgw_sigterm;
5264
} BackgroundWorker;
@@ -101,15 +113,40 @@ typedef struct BackgroundWorker
101113
<structfield>bgw_main_arg</structfield> will be passed to it as its only
102114
argument. Note that the global variable <literal>MyBgworkerEntry</literal>
103115
points to a copy of the <structname>BackgroundWorker</structname> structure
104-
passed at registration time.
116+
passed at registration time. <structfield>bgw_main</structfield> may be
117+
NULL; in that case, <structfield>bgw_library_name</structfield> and
118+
<structfield>bgw_function_name</structfield> will be used to determine
119+
the entrypoint. This is useful for background workers launched after
120+
postmaster startup, where the postmaster does not have the requisite
121+
library loaded.
122+
</para>
123+
124+
<para>
125+
<structfield>bgw_library_name</structfield> is the name of a library in
126+
which the initial entrypoint for the background worker should be sought.
127+
It is ignored unless <structfield>bgw_main</structfield> is NULL.
128+
But if <structfield>bgw_main</structfield> is NULL, then the named library
129+
will be dynamically loaded by the worker process and
130+
<structfield>bgw_function_name</structfield> will be used to identify
131+
the function to be called.
132+
</para>
133+
134+
<para>
135+
<structfield>bgw_function_name</structfield> is the name of a function in
136+
a dynamically loaded library which should be used as the initial entrypoint
137+
for a new background worker. It is ignored unless
138+
<structfield>bgw_main</structfield> is NULL.
105139
</para>
106140

107141
<para>
108142
<structfield>bgw_sighup</structfield> and <structfield>bgw_sigterm</> are
109143
pointers to functions that will be installed as signal handlers for the new
110144
process. If <structfield>bgw_sighup</> is NULL, then <literal>SIG_IGN</>
111145
is used; if <structfield>bgw_sigterm</> is NULL, a handler is installed that
112-
will terminate the process after logging a suitable message.
146+
will terminate the process after logging a suitable message. These
147+
fields should not be used if <structfield>bgw_main</> is NULL; instead,
148+
the worker process should set its own signal handlers before calling
149+
<function>BackgroundWorkerUnblockSignals()</function>.
113150
</para>
114151

115152
<para>Once running, the process can connect to a database by calling

src/backend/postmaster/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ subdir = src/backend/postmaster
1212
top_builddir = ../../..
1313
include $(top_builddir)/src/Makefile.global
1414

15-
OBJS = autovacuum.o bgwriter.o fork_process.o pgarch.o pgstat.o postmaster.o \
16-
startup.o syslogger.o walwriter.o checkpointer.o
15+
OBJS = autovacuum.o bgworker.o bgwriter.o checkpointer.o fork_process.o \
16+
pgarch.o pgstat.o postmaster.o startup.o syslogger.o walwriter.o
1717

1818
include $(top_srcdir)/src/backend/common.mk

0 commit comments

Comments
 (0)