Similarly to a normal PostgreSQL child process, a custom background worker should be running using a loop that can be interrupted with signals to update a given status or simply exit the process. Two types of signals are handled by custom background workers in the PostgreSQL architecture: SIGHUP and SIGTERM.

When such signals are received by a bgworker, you should be aware that processing similar to what is done for a normal backend process might be needed. For example, if your custom bgworker uses some GUC parameters, the process needs to take proper action to reload configuration parameters. Even if there are already good examples in the PostgreSQL source code for bgworkers, I noticed that there are no examples focusing only on certain fundamentals of bgworker. As someone who likes simple things, I think that this is essential to get the essence of what this feature can do piece by piece.

So here is in this post an example of bgworker that I wrote for beginners in order to understand only the fundamentals of signal handling. First you need to know that it is necessary to store a static variable to store the status of representing if signal has been activated or not in a way similar to that:
static bool got_sigterm = false;
static bool got_sighup = false;

You also need dedicated functions to set those flags to true if a signal is received by the worker.
static void
hello_sigterm(SIGNAL_ARGS)
{
 got_sigterm = true;
}
 
static void
hello_sighup(SIGNAL_ARGS)
{
 got_sighup = true;
}

A dedicated function that is used as a main loop for processing is essential as well.
static void
hello_main(void *main_arg)
{
 /* We're now ready to receive signals */
 BackgroundWorkerUnblockSignals();
 while (true)
 {
  /* Process signals */
  if (got_sighup)
  {
   got_sighup = false;
   ereport(LOG, (errmsg("hello signal: processed SIGHUP")));
  }
 
  if (got_sigterm)
  {
   /* Simply exit */
   ereport(LOG, (errmsg("hello signal: processed SIGTERM")));
   proc_exit(0);
  }
 }
 proc_exit(0)
}

Note the call to BackgroundWorkerUnblockSignals. This is extremely important in order to allow the reception of signals by the background worker.

Once you have this basic infrastructure in place, you need to register this worker process correctly with something like that:
void
_PG_init(void)
{
 BackgroundWorker worker;
 worker.bgw_flags = 0;
 worker.bgw_start_time = BgWorkerStart_PostmasterStart;
 worker.bgw_main = hello_main;
 worker.bgw_sigterm = hello_sigterm;
 worker.bgw_sighup = hello_sighup;
 worker.bgw_name = "hello_signal";
 /* Wait 10 seconds for restart before crash */
 worker.bgw_restart_time = 10;
 worker.bgw_main_arg = NULL;
 RegisterBackgroundWorker(&worker);
}

Also don’t forget that you need a header similar to that to have this code working properly.
/* Some general headers for custom bgworker facility */
#include "postgres.h"
#include "fmgr.h"
#include "postmaster/bgworker.h"
#include "storage/ipc.h"
 
/* Allow load of this module in shared libs */
PG_MODULE_MAGIC;
 
/* Entry point of library loading */
void _PG_init(void);

Also, be sure that when you create a custom background worker, signal handling is similar to what is done for normal backend process. For example, the configuration file reload should be processed if SIGHUP is received. You can do that properly by calling ProcessConfigFile in a manner similar to that in the main loop.
if (got_sighup)
{
 /* Process config file */
 ProcessConfigFile(PGC_SIGHUP);
 got_sighup = false;
 ereport(LOG, (errmsg("hello signal: processed SIGHUP")));
}

In order to bring more fluidity to you custom worker and not have it use all the CPU of your server by running continuously, don’t forget to define a latch to control some sleep period of your worker. It can be defined with that:
/* The latch used for this worker to manage sleep correctly */
static Latch signalLatch;

Then when entering in the main loop process, initialize the latch with that:
InitializeLatchSupport();
InitLatch(&signalLatch);

Finally you need to set up your main loop to use the latch
while (true)
{
 int rc;
 
 /* Wait 1s */
 rc = WaitLatch(&signalLatch,
   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
   1000L);
 ResetLatch(&signalLatch);
 
 /* Emergency bailout if postmaster has died */
 if (rc & WL_POSTMASTER_DEATH)
  proc_exit(1);
 
 [... code for signal handling ...]
}

You can also set up the latch such as the sleep will stop immediately if a signal is received. Simply add the following call in hello_sighup and hello_sigterm to do that.
SetLatch(&signalLatch);

This code can be found on Github as repository pg_workers. I created it to group all the bgworker examples I wrote using the facility of PostgreSQL 9.3 and above. You can find the example presented in this post in the folder hello_signal.

Based on my previous experience using custom background workers (new feature of PostgreSQL 9.3), here is more detailed example of bgworker doing a simple “Hello World” in the server logs.

Well, the example provided in this post does a little bit more than logging a simple “Hello World”, as logging is controlled by a loop running at a given interval of time. Also, the process can be immediately stopped even during its sleep with SIGTERM. So here is in more details how this is done.

Header files

/* Minimum set of headers */
#include "postgres.h"
#include "postmaster/bgworker.h"
#include "storage/ipc.h"
#include "storage/latch.h"
#include "storage/proc.h"
#include "fmgr.h"

This is the minimal set of header files that needs to be provided in order to have the bgworker working correctly. Note that in the case of this example, the sleep time is controlled by a Latch on the process of the background worker, explaining why latch.h and proc.h are included.

Declarations

/* Essential for shared libs! */
PG_MODULE_MAGIC;
 
/* Entry point of library loading */
void _PG_init(void);
 
/* Signal handling */
static bool got_sigterm = false;

PG_MODULE_MAGIC is absolutely necessary for libraries loaded via shared_preload_libraries or server will fail with a FATAL error. Then the only function needed in library is _PG_init, entry point to register the bgworker using the dedicated APIs. Finally a static boolean is used as a flag for SIGTERM activation.

Initialization

void
_PG_init(void)
{
 BackgroundWorker worker;
 
 /* Register the worker processes */
 worker.bgw_flags = BGWORKER_SHMEM_ACCESS;
 worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
 worker.bgw_main = hello_main;
 worker.bgw_sighup = NULL;
 worker.bgw_sigterm = hello_sigterm;
 worker.bgw_name = "hello world";
 worker.bgw_restart_time = BGW_NEVER_RESTART;
 worker.bgw_main_arg = NULL;
 RegisterBackgroundWorker(&worker);
}

This portion of code is used to register the new worker. In this example bgw_start_time is set to start only once the system has reached a stable read-write state. The process is also allowed access to shared memory with the flag BGWORKER_SHMEM_ACCESS (this is used for MyProc, as this is statically included in procarray.c). Finally there are definitiosn for the functions used first as a main loop for logging of “Hello World” and for the function to kick when there is a SIGTERM on background worker. The process is requested not to restart in case of a crash.

Main loop

static void
hello_main(void *main_arg)
{
 /* We're now ready to receive signals */
 BackgroundWorkerUnblockSignals();
 while (!got_sigterm)
 {
  int rc;
  /* Wait 10s */
  rc = WaitLatch(&MyProc->procLatch,
    WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
    10000L);
  ResetLatch(&MyProc->procLatch);
  elog(LOG, "Hello World!"); /* Say Hello to the world */
 }
 proc_exit(0);
}

This is the main loop used for the background process. As long as SIGTERM is not received, the process will continue to loop and log “Hello World” every 10s, time interval being symbolized by 10000L. Note that WaitLatch can be awaken with different events: timeout of the time interval provided, Latch being set or postmaster death.

SIGTERM handler

static void
hello_sigterm(SIGNAL_ARGS)
{
 int save_errno = errno;
 got_sigterm = true;
 if (MyProc)
  SetLatch(&MyProc->procLatch);
 errno = save_errno;
}

When SIGTERM is used on the process, the sleep time previously invoked with WaitLatch is stopped with SetLatch immediately. This is controlled by flag WL_LATCH_SET that awakes the Latch when set properly.

Makefile

MODULES = hello_world
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)

This is a simple Makefile. Be sure to name the file containing the code given in this post as hello_world.c. The library generated will be called hello_world.so.

Once this code is run, you will be able to see the process running with a simple ps command.
$ ps x | grep "hello"
13327 ?? Ss 0:00.00 postgres: bgworker: hello world

Be sure to set this variable in postgresql.conf to load the worker correctly.
shared_preload_libraries = 'hello_world'

Modifying the format name of dump file in a Linux system can be made with sysctl like this.
sysctl -w kernel.core_pattern=core.%e.%p
However, making this modification command-based will not make it effective at next reboot.

In order to make the modification permanent, you need to edit the file /etc/sysctl.conf. Here the core file has the executable name %e and the process ID %p.
kernel.core_pattern = core.%e.%p

Here is a list of the possible keywords usable:

  • %p, PID of dumped process
  • %u, (numeric) real UID of dumped process
  • %g, (numeric) real GID of dumped process
  • %s, number of signal causing dump
  • %t time of dump, expressed as seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC)
  • %h, hostname (same as nodename returned by uname(2))
  • %e, executable filename (without path prefix)
  • %c, core file size soft resource limit of crashing process (since Linux 2.6.24)
©2010-2013 Michael Paquier All content is ©Copyright of Otacoo.com 2010-2013. Privacy Policy - Terms of Use