bgworker-4.patch

application/octet-stream

Filename: bgworker-4.patch
Type: application/octet-stream
Part: 0
Message: Re: [v9.3] Extra Daemons (Re: elegant and effective way for running jobs inside a database)

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
contrib/auth_counter/auth_counter.c 203 0
contrib/auth_counter/Makefile 14 0
contrib/worker_spi/Makefile 14 0
contrib/worker_spi/worker_spi.c 177 0
src/backend/postmaster/postmaster.c 766 39
src/backend/utils/init/globals.c 1 0
src/backend/utils/init/miscinit.c 3 3
src/backend/utils/init/postinit.c 13 0
src/backend/utils/misc/guc.c 11 5
src/include/miscadmin.h 1 0
src/include/postmaster/bgworker.h 93 0
src/include/postmaster/postmaster.h 2 0
diff --git a/contrib/auth_counter/Makefile b/contrib/auth_counter/Makefile
new file mode 100644
index 0000000..74ac012
--- /dev/null
+++ b/contrib/auth_counter/Makefile
@@ -0,0 +1,14 @@
+# contrib/auth_counter/Makefile
+
+MODULES = auth_counter
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/auth_counter
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/auth_counter/auth_counter.c b/contrib/auth_counter/auth_counter.c
new file mode 100644
index 0000000..20f946a
--- /dev/null
+++ b/contrib/auth_counter/auth_counter.c
@@ -0,0 +1,203 @@
+/* -------------------------------------------------------------------------
+ *
+ * auth_counter.c
+ *
+ * Copyright (C) 2012, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *		contrib/auth_delay/auth_counter.c*
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "libpq/auth.h"
+#include "libpq/pqsignal.h"
+#include "miscadmin.h"
+#include "postmaster/bgworker.h"
+#include "storage/ipc.h"
+#include "storage/lwlock.h"
+#include "storage/shmem.h"
+#include "storage/smgr.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+#include "utils/timestamp.h"
+#include <unistd.h>
+
+PG_MODULE_MAGIC;
+
+void	_PG_init(void);
+
+/* GUC variable */
+static int	auth_counter_interval;
+
+/* Original hooks */
+static ClientAuthentication_hook_type	original_client_auth_hook;
+static shmem_startup_hook_type			shmem_startup_hook_next;
+
+/* Counter value and flag of loop */
+static LWLockId		auth_counter_lock;
+static long		   *auth_counter_success;
+static long		   *auth_counter_failed;
+static bool			auth_counter_loop;
+
+/*
+ * auth_counter_sigterm
+ *
+ * It reset auth_counter_loop to terminate current loop.
+ */
+static void
+auth_counter_sigterm(SIGNAL_ARGS)
+{
+	auth_counter_loop = false;
+}
+
+/*
+ * auth_counter_main
+ *
+ * The main routine of this extra daemon; that logs number of successful
+ * and failed authentication for each intervals unless receiving a signal.
+ */
+static int
+auth_counter_main(const char *daemon_name)
+{
+	sigjmp_buf		local_sigjmp_buf;
+	MemoryContext	auth_counter_context;
+
+	/* Loop condition should be set */
+	auth_counter_loop = true;
+
+	/* Create a resource owner to keep track of our resources */
+	CurrentResourceOwner = ResourceOwnerCreate(NULL, "Auth-Counter");
+
+	/*
+	 * Create a memory context that we will do all our work in.  We do this so
+	 * that we can reset the context during error recovery and thereby avoid
+	 * possible memory leaks.  Formerly this code just ran in
+	 * TopMemoryContext, but resetting that would be a really bad idea.
+	 */
+	auth_counter_context = AllocSetContextCreate(TopMemoryContext,
+												 "Auth Counter",
+												 ALLOCSET_DEFAULT_MINSIZE,
+												 ALLOCSET_DEFAULT_INITSIZE,
+												 ALLOCSET_DEFAULT_MAXSIZE);
+	MemoryContextSwitchTo(auth_counter_context);
+
+	/* Unblock signals (they were blocked when the postmaster forked us) */
+    PG_SETMASK(&UnBlockSig);
+
+	/*
+	 * Init counter variables
+	 */
+	LWLockAcquire(auth_counter_lock, LW_EXCLUSIVE);
+	*auth_counter_success = 0;
+	*auth_counter_failed  = 0;
+	LWLockRelease(auth_counter_lock);
+
+	while (auth_counter_loop)
+	{
+		Datum	tstamp;
+		long	n_success;
+		long	n_failed;
+
+		pg_usleep((long)auth_counter_interval * 1000000L);
+
+		LWLockAcquire(auth_counter_lock, LW_EXCLUSIVE);
+		n_success = *auth_counter_success;
+		n_failed  = *auth_counter_failed;
+
+		*auth_counter_success = 0;
+		*auth_counter_failed  = 0;
+		LWLockRelease(auth_counter_lock);
+
+		tstamp = DirectFunctionCall1(timestamptz_out,
+							TimestampTzGetDatum(GetCurrentTimestamp()));
+
+		elog(LOG, "%s(%d) %lu of login successful, %lu of failed - %s",
+			 daemon_name, MyProcPid, n_success, n_failed,
+			 DatumGetCString(tstamp));
+
+		/* clear temporary memory objects */
+		MemoryContextReset(auth_counter_context);
+	}
+	return 0;
+}
+
+
+/*
+ * auth_counter_check
+ *
+ * It increments the counter variable for each client authentication
+ */
+static void
+auth_counter_check(Port *port, int status)
+{
+	if (original_client_auth_hook)
+		original_client_auth_hook(port, status);
+
+	LWLockAcquire(auth_counter_lock, LW_EXCLUSIVE);
+	if (status == STATUS_OK)
+		(*auth_counter_success)++;
+	else
+		(*auth_counter_failed)++;
+	LWLockRelease(auth_counter_lock);
+}
+
+/*
+ * Callback just after shared memory allocation
+ */
+static void
+auth_counter_shmem_startup(void)
+{
+	if (shmem_startup_hook_next)
+		shmem_startup_hook_next();
+
+	/* allocate lwlock to protect counters */
+	auth_counter_lock = LWLockAssign();
+
+	/* allocate counter variable on shmem segment */
+	auth_counter_success = ShmemAlloc(sizeof(long));
+	auth_counter_failed  = ShmemAlloc(sizeof(long));
+}
+
+/*
+ * Entrypoint of this module
+ */
+void
+_PG_init(void)
+{
+	BackgroundWorker		worker;
+
+	DefineCustomIntVariable("auth_counter.interval",
+							"Interval to display number of logins",
+							NULL,
+							&auth_counter_interval,
+							10,				/* 1 minute (default) */
+							5,				/* 5 seconds */
+							24 * 60 * 60,	/* 1 day*/
+							PGC_SIGHUP,
+							GUC_UNIT_S,
+							NULL, NULL, NULL);
+
+	/* request for shared memory fraction */
+	RequestAddinShmemSpace(2 * sizeof(long));
+
+	shmem_startup_hook_next = shmem_startup_hook;
+	shmem_startup_hook = auth_counter_shmem_startup;
+
+	/* install a hook */
+	original_client_auth_hook = ClientAuthentication_hook;
+	ClientAuthentication_hook = auth_counter_check;
+
+	/* register the worker process */
+	worker.bgw_name = "auth counter";
+	worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
+	worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	worker.bgw_main = auth_counter_main;
+	worker.bgw_main_arg = NULL;
+	worker.bgw_sighup = auth_counter_sigterm;
+	worker.bgw_sigterm = auth_counter_sigterm;
+
+	RegisterBackgroundWorker(&worker);
+}
diff --git a/contrib/worker_spi/Makefile b/contrib/worker_spi/Makefile
new file mode 100644
index 0000000..edf4105
--- /dev/null
+++ b/contrib/worker_spi/Makefile
@@ -0,0 +1,14 @@
+# contrib/worker_spi/Makefile
+
+MODULES = worker_spi
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/worker_spi
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/worker_spi/worker_spi.c b/contrib/worker_spi/worker_spi.c
new file mode 100644
index 0000000..93f7c7f
--- /dev/null
+++ b/contrib/worker_spi/worker_spi.c
@@ -0,0 +1,177 @@
+/* -------------------------------------------------------------------------
+ *
+ * worker_spi.c
+ *
+ * Copyright (C) 2012, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *		contrib/worker_spi/worker_spi.c
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <unistd.h>
+
+#include "access/xact.h"
+#include "executor/spi.h"
+#include "fmgr.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqsignal.h"
+#include "postmaster/bgworker.h"
+#include "storage/ipc.h"
+#include "utils/snapmgr.h"
+
+PG_MODULE_MAGIC;
+
+void	_PG_init(void);
+
+static bool	got_sigterm = false;
+
+static void
+worker_spi_sigterm(SIGNAL_ARGS)
+{
+	got_sigterm = true;
+}
+
+static void
+worker_spi_sighup(SIGNAL_ARGS)
+{
+	elog(LOG, "got sighup!");
+}
+
+static void
+initialize_worker_spi(char *tabname)
+{
+	int		ret;
+	int		ntup;
+	bool	isnull;
+	StringInfoData	buf;
+
+	StartTransactionCommand();
+	SPI_connect();
+	PushActiveSnapshot(GetTransactionSnapshot());
+
+	initStringInfo(&buf);
+	appendStringInfo(&buf, "select count(*) from pg_namespace where nspname = '%s'", tabname);
+
+	ret = SPI_execute(buf.data, true, 0);
+	if (ret != SPI_OK_SELECT)
+		elog(FATAL, "SPI_execute failed: error code %d", ret);
+
+	if (SPI_processed != 1)
+		elog(FATAL, "not a singleton result");
+
+	ntup = DatumGetInt32(SPI_getbinval(SPI_tuptable->vals[0],
+									   SPI_tuptable->tupdesc,
+									   1, &isnull));
+	if (isnull)
+		elog(FATAL, "null result");
+
+	elog(LOG, "pg_namespace has %d tuples for nspname = '%s'", ntup, tabname);
+
+	if (ntup == 0)
+	{
+		resetStringInfo(&buf);
+		appendStringInfo(&buf,
+						 "create schema \"%s\" "
+						 "create table \"%s\" (type	text, "
+						 "value	int)", tabname, tabname);
+
+		ret = SPI_execute(buf.data, false, 0);
+
+		if (ret != SPI_OK_UTILITY)
+			elog(FATAL, "failed to create my schema");
+	}
+
+	SPI_finish();
+	PopActiveSnapshot();
+	CommitTransactionCommand();
+}
+
+static void
+worker_spi_main(void *main_arg)
+{
+	char		   *tabname;
+	StringInfoData		buf;
+
+	tabname = (char *) main_arg;
+
+	/* Unblock signals (they were blocked when the postmaster forked us) */
+    PG_SETMASK(&UnBlockSig);
+
+	BackgroundWorkerInitializeConnection("alvherre", NULL);
+
+	initialize_worker_spi(tabname);
+
+	initStringInfo(&buf);
+	appendStringInfo(&buf,
+					 "WITH deleted AS (DELETE "
+		"FROM %s.%s "
+		"WHERE type = 'delta' RETURNING value), "
+					 "total AS (SELECT coalesce(sum(value), 0) as sum "
+							   "FROM deleted) "
+					 "UPDATE %s.%s "
+					 "SET value = %s.value + total.sum "
+					 "FROM total WHERE type = 'total' "
+					 "RETURNING %s.value", tabname, tabname, tabname, tabname, tabname, tabname);
+
+	while (!got_sigterm)
+	{
+		int		ret;
+
+		pg_usleep(1000 * 1000 * 10);	/* 10s */
+
+		StartTransactionCommand();
+		SPI_connect();
+		PushActiveSnapshot(GetTransactionSnapshot());
+
+		ret = SPI_execute(buf.data, false, 0);
+
+		if (ret != SPI_OK_UPDATE_RETURNING)
+			elog(FATAL, "cannot select from table %s: error code %d", tabname, ret);
+
+		if (SPI_processed > 0)
+		{
+			bool	isnull;
+			int32	val;
+
+			val = DatumGetInt32(SPI_getbinval(SPI_tuptable->vals[0],
+											   SPI_tuptable->tupdesc,
+											   1, &isnull));
+			if (!isnull)
+				elog(LOG, "count is now %d", val);
+		}
+
+		SPI_finish();
+		PopActiveSnapshot();
+		CommitTransactionCommand();
+	}
+
+	proc_exit(0);
+}
+
+/*
+ * Entrypoint of this module
+ */
+void
+_PG_init(void)
+{
+	BackgroundWorker		worker;
+
+	/* register the worker process */
+	worker.bgw_name = "SPI worker";
+	worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
+	worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
+	worker.bgw_main = worker_spi_main;
+	worker.bgw_sighup = worker_spi_sighup;
+	worker.bgw_sigterm = worker_spi_sigterm;
+
+	worker.bgw_name = "SPI worker 1";
+	worker.bgw_main_arg = "table1";
+	RegisterBackgroundWorker(&worker);
+
+	worker.bgw_name = "SPI worker 2";
+	worker.bgw_main_arg = "table2";
+	RegisterBackgroundWorker(&worker);
+}
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index dff4c71..f386f78 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -103,6 +103,7 @@
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "postmaster/autovacuum.h"
+#include "postmaster/bgworker.h"
 #include "postmaster/fork_process.h"
 #include "postmaster/pgarch.h"
 #include "postmaster/postmaster.h"
@@ -132,7 +133,8 @@
  * children we have and send them appropriate signals when necessary.
  *
  * "Special" children such as the startup, bgwriter and autovacuum launcher
- * tasks are not in this list.	Autovacuum worker and walsender processes are
+ * tasks are not in this list.	Autovacuum worker, walsender and general
+ * background worker processes are
  * in it. Also, "dead_end" children are in it: these are children launched just
  * for the purpose of sending a friendly rejection message to a would-be
  * client.	We must track them because they are attached to shared memory,
@@ -144,7 +146,12 @@ typedef struct bkend
 	pid_t		pid;			/* process id of backend */
 	long		cancel_key;		/* cancel key for cancels for this backend */
 	int			child_slot;		/* PMChildSlot for this backend, if any */
-	bool		is_autovacuum;	/* is it an autovacuum process? */
+	int			bkend_type;		/* flavor of backend or auxiliary process
+								   Note that BACKEND_TYPE_WALSND backends
+								   initially announce themselves as
+								   BACKEND_TYPE_NORMAL, so if bkend_type is
+								   normal then you should check for a recent
+								   transition. */
 	bool		dead_end;		/* is it going to send an error and quit? */
 	Dlelem		elem;			/* list link in BackendList */
 } Backend;
@@ -155,6 +162,23 @@ static Dllist *BackendList;
 static Backend *ShmemBackendArray;
 #endif
 
+
+/*
+ * List of background workers.
+ */
+typedef struct RegisteredBgWorker
+{
+	BackgroundWorker *worker;	/* its registry entry */
+	Backend		   *backend;	/* its BackendList entry, or NULL */
+	pid_t			pid;		/* 0 if not running */
+	int				child_slot;
+	TimestampTz		crashed_at;	/* if not 0, time it last crashed */
+} RegisteredBgWorker;
+
+static List       *BackgroundWorkerList = NIL;
+
+
+
 /* The socket number we are listening for connections on */
 int			PostPortNumber;
 /* The directory names for Unix socket(s) */
@@ -306,6 +330,10 @@ static volatile sig_atomic_t start_autovac_launcher = false;
 /* the launcher needs to be signalled to communicate some condition */
 static volatile bool avlauncher_needs_signal = false;
 
+/* set when there's a worker that needs to be started up */
+static volatile bool StartWorkerNeeded = true;
+static volatile bool HaveCrashedWorker = false;
+
 /*
  * State for assigning random salts and cancel keys.
  * Also, the global MyCancelKey passes the cancel key assigned to a given
@@ -343,6 +371,8 @@ static void startup_die(SIGNAL_ARGS);
 static void dummy_handler(SIGNAL_ARGS);
 static void StartupPacketTimeoutHandler(void);
 static void CleanupBackend(int pid, int exitstatus);
+static bool CleanupBackgroundWorker(int pid, int exitstatus);
+static void do_start_worker(BackgroundWorker *worker);
 static void HandleChildCrash(int pid, int exitstatus, const char *procname);
 static void LogChildExit(int lev, const char *procname,
 			 int pid, int exitstatus);
@@ -361,6 +391,7 @@ static long PostmasterRandom(void);
 static void RandomSalt(char *md5Salt);
 static void signal_child(pid_t pid, int signal);
 static bool SignalSomeChildren(int signal, int targets);
+static bool SignalUnconnectedWorkers(int signal);
 
 #define SignalChildren(sig)			   SignalSomeChildren(sig, BACKEND_TYPE_ALL)
 
@@ -371,9 +402,14 @@ static bool SignalSomeChildren(int signal, int targets);
 #define BACKEND_TYPE_NORMAL		0x0001	/* normal backend */
 #define BACKEND_TYPE_AUTOVAC	0x0002	/* autovacuum worker process */
 #define BACKEND_TYPE_WALSND		0x0004	/* walsender process */
-#define BACKEND_TYPE_ALL		0x0007	/* OR of all the above */
+#define BACKEND_TYPE_BGWORKER	0x0008	/* bgworker process */
+#define BACKEND_TYPE_ALL		0x000F	/* OR of all the above */
+
+#define BACKEND_TYPE_WORKER		(BACKEND_TYPE_AUTOVAC | BACKEND_TYPE_BGWORKER)
 
 static int	CountChildren(int target);
+static int CountUnconnectedWorkers(void);
+static void StartOneBackgroundWorker(void);
 static bool CreateOptsFile(int argc, char *argv[], char *fullprogname);
 static pid_t StartChildProcess(AuxProcType type);
 static void StartAutovacuumWorker(void);
@@ -1092,7 +1128,8 @@ PostmasterMain(int argc, char *argv[])
 	 * handling setup of child processes.  See tcop/postgres.c,
 	 * bootstrap/bootstrap.c, postmaster/bgwriter.c, postmaster/walwriter.c,
 	 * postmaster/autovacuum.c, postmaster/pgarch.c, postmaster/pgstat.c,
-	 * postmaster/syslogger.c and postmaster/checkpointer.c.
+	 * postmaster/syslogger.c, postmaster/bgworker.c and
+	 * postmaster/checkpointer.c.
 	 */
 	pqinitmask();
 	PG_SETMASK(&BlockSig);
@@ -1182,6 +1219,9 @@ PostmasterMain(int argc, char *argv[])
 	Assert(StartupPID != 0);
 	pmState = PM_STARTUP;
 
+	/* Some workers may be scheduled to start now */
+	StartOneBackgroundWorker();
+
 	status = ServerLoop();
 
 	/*
@@ -1369,9 +1409,6 @@ ServerLoop(void)
 		/*
 		 * Wait for a connection request to arrive.
 		 *
-		 * We wait at most one minute, to ensure that the other background
-		 * tasks handled below get done even when no requests are arriving.
-		 *
 		 * If we are in PM_WAIT_DEAD_END state, then we don't want to accept
 		 * any new connections, so we don't call select() at all; just sleep
 		 * for a little bit with signals unblocked.
@@ -1390,8 +1427,23 @@ ServerLoop(void)
 			/* must set timeout each time; some OSes change it! */
 			struct timeval timeout;
 
-			timeout.tv_sec = 60;
-			timeout.tv_usec = 0;
+			/*
+			 * In normal conditions we wait at most one minute, to ensure that
+			 * the other background tasks handled below get done even when no
+			 * requests are arriving.  However, if there are background workers
+			 * waiting to be started, we don't actually sleep so that they are
+			 * quickly serviced.
+			 */
+			if (StartWorkerNeeded)
+			{
+				timeout.tv_sec = 0;
+				timeout.tv_usec = 0;
+			}
+			else
+			{
+				timeout.tv_sec = 60;
+				timeout.tv_usec = 0;
+			}
 
 			selres = select(nSockets, &rmask, NULL, NULL, &timeout);
 		}
@@ -1503,6 +1555,10 @@ ServerLoop(void)
 				kill(AutoVacPID, SIGUSR2);
 		}
 
+		/* Get other worker processes running, if needed */
+		if (StartWorkerNeeded || HaveCrashedWorker)
+			StartOneBackgroundWorker();
+
 		/*
 		 * Touch Unix socket and lock files every 58 minutes, to ensure that
 		 * they are not removed by overzealous /tmp-cleaning tasks.  We assume
@@ -2210,8 +2266,11 @@ pmdie(SIGNAL_ARGS)
 			if (pmState == PM_RUN || pmState == PM_RECOVERY ||
 				pmState == PM_HOT_STANDBY || pmState == PM_STARTUP)
 			{
-				/* autovacuum workers are told to shut down immediately */
-				SignalSomeChildren(SIGTERM, BACKEND_TYPE_AUTOVAC);
+				/* autovac workers are told to shut down immediately */
+				/* and bgworkers too; does this need tweaking? */
+				SignalSomeChildren(SIGTERM,
+								   BACKEND_TYPE_AUTOVAC | BACKEND_TYPE_BGWORKER);
+				SignalUnconnectedWorkers(SIGTERM);
 				/* and the autovac launcher too */
 				if (AutoVacPID != 0)
 					signal_child(AutoVacPID, SIGTERM);
@@ -2263,12 +2322,14 @@ pmdie(SIGNAL_ARGS)
 				signal_child(BgWriterPID, SIGTERM);
 			if (WalReceiverPID != 0)
 				signal_child(WalReceiverPID, SIGTERM);
+			SignalUnconnectedWorkers(SIGTERM);
 			if (pmState == PM_RECOVERY)
 			{
 				/*
-				 * Only startup, bgwriter, and checkpointer should be active
-				 * in this state; we just signaled the first two, and we don't
-				 * want to kill checkpointer yet.
+				 * Only startup, bgwriter, walreceiver, unconnected bgworkers,
+				 * and checkpointer should be active in this state; we just
+				 * signaled the first four, and we don't want to kill
+				 * checkpointer yet.
 				 */
 				pmState = PM_WAIT_BACKENDS;
 			}
@@ -2280,9 +2341,10 @@ pmdie(SIGNAL_ARGS)
 			{
 				ereport(LOG,
 						(errmsg("aborting any active transactions")));
-				/* shut down all backends and autovac workers */
+				/* shut down all backends and workers */
 				SignalSomeChildren(SIGTERM,
-								 BACKEND_TYPE_NORMAL | BACKEND_TYPE_AUTOVAC);
+								   BACKEND_TYPE_NORMAL | BACKEND_TYPE_AUTOVAC |
+								   BACKEND_TYPE_BGWORKER);
 				/* and the autovac launcher too */
 				if (AutoVacPID != 0)
 					signal_child(AutoVacPID, SIGTERM);
@@ -2326,6 +2388,7 @@ pmdie(SIGNAL_ARGS)
 				signal_child(PgArchPID, SIGQUIT);
 			if (PgStatPID != 0)
 				signal_child(PgStatPID, SIGQUIT);
+			SignalUnconnectedWorkers(SIGQUIT);
 			ExitPostmaster(0);
 			break;
 	}
@@ -2454,6 +2517,9 @@ reaper(SIGNAL_ARGS)
 			if (PgStatPID == 0)
 				PgStatPID = pgstat_start();
 
+			/* some workers may be scheduled to start now */
+			StartOneBackgroundWorker();
+
 			/* at this point we are really open for business */
 			ereport(LOG,
 				 (errmsg("database system is ready to accept connections")));
@@ -2620,6 +2686,14 @@ reaper(SIGNAL_ARGS)
 			continue;
 		}
 
+		/* Was it one of our background workers? */
+		if (CleanupBackgroundWorker(pid, exitstatus))
+		{
+			/* have it be restarted */
+			StartWorkerNeeded = true;
+			continue;
+		}
+
 		/*
 		 * Else do standard backend child cleanup.
 		 */
@@ -2638,11 +2712,93 @@ reaper(SIGNAL_ARGS)
 	errno = save_errno;
 }
 
+/*
+ * Scan the bgworkers list and see if the given PID (which has just stopped
+ * or crashed) is in it.  Handle its shutdown if so, and return true.  If not a
+ * bgworker, return false.
+ *
+ * This is heavily based on CleanupBackend, q.v.
+ */
+static bool
+CleanupBackgroundWorker(int pid,
+						int exitstatus)	/* child's exit status */
+{
+	ListCell   *cell;
+	char		namebuf[MAXPGPATH];
+
+	LogChildExit(DEBUG2, _("background worker process"), pid, exitstatus);
+
+#ifdef WIN32
+	if (exitstatus == ERROR_WAIT_NO_CHILDREN)
+	{
+		LogChildExit(LOG, _("background server process"), pid, exitstatus);
+		exitstatus = 0;
+	}
+#endif
+
+	foreach(cell, BackgroundWorkerList)
+	{
+		RegisteredBgWorker   *rw = lfirst(cell);
+
+		if (rw->pid != pid)
+			continue;
+
+		snprintf(namebuf, MAXPGPATH, "%s: %s", _("worker process"),
+				 rw->worker->bgw_name);
+
+		if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus))
+		{
+			if (rw->worker->bgw_flags & BGWORKER_SHMEM_ACCESS)
+			{
+				/* XXX do we need more cleanup here? */
+				rw->crashed_at = GetCurrentTimestamp();
+				HandleChildCrash(pid, exitstatus, namebuf);
+				return true;
+			}
+		}
+
+		if (!ReleasePostmasterChildSlot(rw->child_slot))
+		{
+			/*
+			 * Uh-oh, the child failed to clean itself up.	Treat as a
+			 * crash after all.
+			 */
+			rw->crashed_at = GetCurrentTimestamp();
+			HandleChildCrash(pid, exitstatus, namebuf);
+			return true;
+		}
+
+		/* Get it out of the BackendList and clear out remaining data */
+		if (rw->backend)
+		{
+			DLRemove(&rw->backend->elem);
+#ifdef EXEC_BACKEND
+			ShmemBackendArrayRemove(rw->backend);
+#endif
+			free(rw->backend);
+			rw->backend = NULL;
+		}
+		rw->pid = 0;
+		rw->child_slot = 0;
+		if (EXIT_STATUS_1(exitstatus))
+			rw->crashed_at = GetCurrentTimestamp();
+		else
+			rw->crashed_at = 0;
+
+		LogChildExit(LOG, namebuf, pid, exitstatus);
+
+		return true;
+	}
+
+	return false;
+}
 
 /*
  * CleanupBackend -- cleanup after terminated backend.
  *
  * Remove all local state associated with backend.
+ *
+ * If you change this, see also CleanupBackgroundWorker.
  */
 static void
 CleanupBackend(int pid,
@@ -2686,6 +2842,8 @@ CleanupBackend(int pid,
 
 		if (bp->pid == pid)
 		{
+			/* XXX do we need to check bkend_type here? */
+
 			if (!bp->dead_end)
 			{
 				if (!ReleasePostmasterChildSlot(bp->child_slot))
@@ -2710,7 +2868,7 @@ CleanupBackend(int pid,
 
 /*
  * HandleChildCrash -- cleanup after failed backend, bgwriter, checkpointer,
- * walwriter or autovacuum.
+ * walwriter, autovacuum, or background worker.
  *
  * The objectives here are to clean up our local state about the child
  * process, and to signal all other remaining children to quickdie.
@@ -2721,6 +2879,7 @@ HandleChildCrash(int pid, int exitstatus, const char *procname)
 	Dlelem	   *curr,
 			   *next;
 	Backend    *bp;
+	ListCell   *cell;
 
 	/*
 	 * Make log entry unless there was a previous crash (if so, nonzero exit
@@ -2733,6 +2892,53 @@ HandleChildCrash(int pid, int exitstatus, const char *procname)
 				(errmsg("terminating any other active server processes")));
 	}
 
+	/* Process background workers */
+	foreach(cell, BackgroundWorkerList)
+	{
+		RegisteredBgWorker *rw = lfirst(cell);
+
+		if (rw->pid == pid)
+		{
+			/*
+			 * Found entry for freshly-dead worker, so remove it.
+			 */
+			(void) ReleasePostmasterChildSlot(rw->child_slot);
+			if (rw->backend)
+			{
+				DLRemove(&rw->backend->elem);
+#ifdef EXEC_BACKEND
+				ShmemBackendArrayRemove(rw->backend);
+#endif
+				free(rw->backend);
+				rw->backend = NULL;
+			}
+			rw->pid = 0;
+			rw->child_slot = 0;
+			/* don't reset crashed_at */
+			/* Keep looping so we can signal remaining workers */
+		}
+		else
+		{
+			/*
+			 * This worker is still alive.  Unless we did so already, tell it
+			 * to commit hara-kiri.
+			 *
+			 * SIGQUIT is the special signal that says exit without proc_exit
+			 * and let the user know what's going on. But if SendStop is set
+			 * (-s on command line), then we send SIGSTOP instead, so that we
+			 * can get core dumps from all backends by hand.
+			 */
+			if (!FatalError)
+			{
+				ereport(DEBUG2,
+						(errmsg_internal("sending %s to process %d",
+										 (SendStop ? "SIGSTOP" : "SIGQUIT"),
+										 (int) rw->pid)));
+				signal_child(rw->pid, (SendStop ? SIGSTOP : SIGQUIT));
+			}
+		}
+	}
+
 	/* Process regular backends */
 	for (curr = DLGetHead(BackendList); curr; curr = next)
 	{
@@ -2882,6 +3088,24 @@ HandleChildCrash(int pid, int exitstatus, const char *procname)
 		allow_immediate_pgstat_restart();
 	}
 
+	/*
+	 * Power-cycle all shmem-connected bgworkers too
+	 */
+	foreach(cell, BackgroundWorkerList)
+	{
+		RegisteredBgWorker *rw = lfirst(cell);
+
+		if (rw->pid != 0 &&
+			(rw->worker->bgw_flags & BGWORKER_SHMEM_ACCESS))
+		{
+			ereport(DEBUG2,
+					(errmsg_internal("sending %s to process %d",
+									 "SIGQUIT",
+									 (int) rw->pid)));
+			signal_child(rw->pid, SIGQUIT);
+		}
+	}
+
 	/* We do NOT restart the syslogger */
 
 	FatalError = true;
@@ -3011,7 +3235,8 @@ PostmasterStateMachine(void)
 	{
 		/*
 		 * PM_WAIT_BACKENDS state ends when we have no regular backends
-		 * (including autovac workers) and no walwriter, autovac launcher or
+		 * (including autovac workers), no bgworkers (including unconnected
+		 * ones), and no walwriter, autovac launcher or
 		 * bgwriter.  If we are doing crash recovery then we expect the
 		 * checkpointer to exit as well, otherwise not. The archiver, stats,
 		 * and syslogger processes are disregarded since they are not
@@ -3020,7 +3245,8 @@ PostmasterStateMachine(void)
 		 * later after writing the checkpoint record, like the archiver
 		 * process.
 		 */
-		if (CountChildren(BACKEND_TYPE_NORMAL | BACKEND_TYPE_AUTOVAC) == 0 &&
+		if (CountChildren(BACKEND_TYPE_NORMAL | BACKEND_TYPE_WORKER) == 0 &&
+			CountUnconnectedWorkers() == 0 &&
 			StartupPID == 0 &&
 			WalReceiverPID == 0 &&
 			BgWriterPID == 0 &&
@@ -3233,6 +3459,35 @@ signal_child(pid_t pid, int signal)
 }
 
 /*
+ * Send a signal to bgworkers that did not request backend connections
+ */
+static bool
+SignalUnconnectedWorkers(int signal)
+{
+	ListCell   *cell;
+	bool		signaled = false;
+
+	foreach(cell, BackgroundWorkerList)
+	{
+		RegisteredBgWorker   *rw = lfirst(cell);
+
+		if (rw->pid == 0)
+			continue;
+		/* ignore connected workers */
+		if (rw->backend != NULL)
+			continue;
+
+		ereport(DEBUG4,
+				(errmsg_internal("sending signal %d to process %d",
+								 signal, (int) rw->pid)));
+		signal_child(rw->pid, signal);
+		signaled = true;
+	}
+
+	return signaled;
+}
+
+/*
  * Send a signal to the targeted children (but NOT special children;
  * dead_end children are never signaled, either).
  */
@@ -3255,15 +3510,15 @@ SignalSomeChildren(int signal, int target)
 		 */
 		if (target != BACKEND_TYPE_ALL)
 		{
-			int			child;
+			/*
+			 * Assign bkend_type for any recently announced
+			 * WAL Sender processes.
+			 */
+			if (bp->bkend_type == BACKEND_TYPE_NORMAL &&
+				IsPostmasterChildWalSender(bp->child_slot))
+				bp->bkend_type = BACKEND_TYPE_WALSND;
 
-			if (bp->is_autovacuum)
-				child = BACKEND_TYPE_AUTOVAC;
-			else if (IsPostmasterChildWalSender(bp->child_slot))
-				child = BACKEND_TYPE_WALSND;
-			else
-				child = BACKEND_TYPE_NORMAL;
-			if (!(target & child))
+			if (!(target & bp->bkend_type))
 				continue;
 		}
 
@@ -3381,7 +3636,7 @@ BackendStartup(Port *port)
 	 * of backends.
 	 */
 	bn->pid = pid;
-	bn->is_autovacuum = false;
+	bn->bkend_type = BACKEND_TYPE_NORMAL; /* Can change later to WALSND */
 	DLInitElem(&bn->elem, bn);
 	DLAddHead(BackendList, &bn->elem);
 #ifdef EXEC_BACKEND
@@ -3757,7 +4012,10 @@ internal_forkexec(int argc, char *argv[], Port *port)
 	fp = AllocateFile(tmpfilename, PG_BINARY_W);
 	if (!fp)
 	{
-		/* As in OpenTemporaryFile, try to make the temp-file directory */
+		/*
+		 * As in OpenTemporaryFileInTablespace, try to make the temp-file
+		 * directory
+		 */
 		mkdir(PG_TEMP_FILES_DIR, S_IRWXU);
 
 		fp = AllocateFile(tmpfilename, PG_BINARY_W);
@@ -4325,6 +4583,9 @@ sigusr1_handler(SIGNAL_ARGS)
 		(errmsg("database system is ready to accept read only connections")));
 
 		pmState = PM_HOT_STANDBY;
+
+		/* Some workers may be scheduled to start now */
+		StartOneBackgroundWorker();
 	}
 
 	if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER) &&
@@ -4492,6 +4753,30 @@ PostmasterRandom(void)
 }
 
 /*
+ * Count up number of worker processes that did not request backend connections
+ */
+static int
+CountUnconnectedWorkers(void)
+{
+	ListCell   *cell;
+	int			cnt = 0;
+
+	foreach(cell, BackgroundWorkerList)
+	{
+		RegisteredBgWorker   *rw = lfirst(cell);
+
+		if (rw->pid == 0)
+			continue;
+		/* ignore connected workers */
+		if (rw->backend != NULL)
+			continue;
+
+		cnt++;
+	}
+	return cnt;
+}
+
+/*
  * Count up number of child processes of specified types (dead_end chidren
  * are always excluded).
  */
@@ -4514,15 +4799,15 @@ CountChildren(int target)
 		 */
 		if (target != BACKEND_TYPE_ALL)
 		{
-			int			child;
+			/*
+			 * Assign bkend_type for any recently announced
+			 * WAL Sender processes.
+			 */
+			if (bp->bkend_type == BACKEND_TYPE_NORMAL &&
+				IsPostmasterChildWalSender(bp->child_slot))
+				bp->bkend_type = BACKEND_TYPE_WALSND;
 
-			if (bp->is_autovacuum)
-				child = BACKEND_TYPE_AUTOVAC;
-			else if (IsPostmasterChildWalSender(bp->child_slot))
-				child = BACKEND_TYPE_WALSND;
-			else
-				child = BACKEND_TYPE_NORMAL;
-			if (!(target & child))
+			if (!(target & bp->bkend_type))
 				continue;
 		}
 
@@ -4681,7 +4966,7 @@ StartAutovacuumWorker(void)
 			bn->pid = StartAutoVacWorker();
 			if (bn->pid > 0)
 			{
-				bn->is_autovacuum = true;
+				bn->bkend_type = BACKEND_TYPE_AUTOVAC;
 				DLInitElem(&bn->elem, bn);
 				DLAddHead(BackendList, &bn->elem);
 #ifdef EXEC_BACKEND
@@ -4757,7 +5042,8 @@ CreateOptsFile(int argc, char *argv[], char *fullprogname)
  *
  * This reports the number of entries needed in per-child-process arrays
  * (the PMChildFlags array, and if EXEC_BACKEND the ShmemBackendArray).
- * These arrays include regular backends, autovac workers and walsenders,
+ * These arrays include regular backends, autovac workers, walsenders
+ * and background workers,
  * but not special children nor dead_end children.	This allows the arrays
  * to have a fixed maximum size, to wit the same too-many-children limit
  * enforced by canAcceptConnections().	The exact value isn't too critical
@@ -4769,6 +5055,447 @@ MaxLivePostmasterChildren(void)
 	return 2 * MaxBackends;
 }
 
+/*
+ * Register a new background worker.
+ *
+ * This can only be called in the _PG_init function of a module library
+ * that's loaded by shared_preload_libraries; otherwise it has no effect.
+ */
+void
+RegisterBackgroundWorker(BackgroundWorker *worker)
+{
+	RegisteredBgWorker *rw;
+
+	if (!process_shared_preload_libraries_in_progress)
+	{
+		ereport(LOG,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("background worker must be registered in shared_preload_libraries")));
+		return;
+	}
+
+	/* sanity check for flags */
+	if (worker->bgw_flags & BGWORKER_BACKEND_DATABASE_CONNECTION)
+	{
+		if (!(worker->bgw_flags & BGWORKER_SHMEM_ACCESS))
+		{
+			ereport(LOG,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("background worker must attach to shared memory in order to request a database connection")));
+			return;
+		}
+
+		if (worker->bgw_start_time == BgWorkerStart_PostmasterStart)
+		{
+			ereport(LOG,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("background worker cannot request database access if starting at postmaster start")));
+			return;
+		}
+
+		/* XXX other checks? */
+	}
+
+	rw = malloc(MAXALIGN(sizeof(RegisteredBgWorker)) +
+				MAXALIGN(sizeof(BackgroundWorker)) +
+				strlen(worker->bgw_name) + 1);		/* FIXME -- check for NULL and missing \0 terminator? */
+	if (rw == NULL)
+	{
+		ereport(LOG,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory")));
+		return;
+	}
+
+	rw->worker = (BackgroundWorker *)
+		MAXALIGN((char *) rw + sizeof(RegisteredBgWorker));
+	memcpy(rw->worker, worker, sizeof(BackgroundWorker));
+	rw->worker->bgw_name = (char *)
+		MAXALIGN((char *) rw->worker + sizeof(BackgroundWorker));
+	strlcpy(rw->worker->bgw_name, worker->bgw_name, strlen(worker->bgw_name) + 1);	/* FIXME strlen again */
+
+	rw->backend = NULL;
+	rw->pid = 0;
+	rw->child_slot = 0;
+	rw->crashed_at = 0;
+
+	/* XXX should we worry about the memcxt this happens in? */
+	BackgroundWorkerList = lappend(BackgroundWorkerList, rw);
+}
+
+/*
+ * Connect worker to a database.
+ *
+ * XXX is this really necessary, or would it be better to have module call
+ * InitPostgres() directly?  If we were to have more stuff done here this would
+ * be more justified; as is, it doesn't seem to.
+ */
+void
+BackgroundWorkerInitializeConnection(char *dbname, char *username)
+{
+	/*
+	 * XXX Here we need to check that the BGWORKER_BACKEND_DATABASE_CONNECTION
+	 * flag was passed on registration, or raise an error otherwise.  Not sure
+	 * how ...  Maybe we need a "MyBgWorker" global pointer somehow (similar to
+	 * MyProc) that InitPostgres can check by itself.  That would require that
+	 * the BackgroundWorker structs live in shared memory ...
+	 */
+
+	InitPostgres(dbname, InvalidOid, username, NULL);
+
+	/* it had better not gotten out of "init" mode yet */
+	if (!IsInitProcessingMode())
+		ereport(ERROR,
+				(errmsg("invalid processing mode in bgworker")));
+	SetProcessingMode(NormalProcessing);
+}
+
+static void
+do_start_worker(BackgroundWorker *worker)
+{
+	sigjmp_buf	local_sigjmp_buf;
+	char		buf[MAXPGPATH];
+
+	/* we are a postmaster subprocess now */
+	IsUnderPostmaster = true;
+	IsBackgroundWorker = true;
+
+	/* reset MyProcPid */
+	MyProcPid = getpid();
+
+	/* record Start Time for logging */
+	MyStartTime = time(NULL);
+
+	/* Identify myself via ps */
+	snprintf(buf, MAXPGPATH, "bgworker: %s", worker->bgw_name);
+	init_ps_display(buf, "", "", "");
+
+	SetProcessingMode(InitProcessing);
+
+	/*
+	 * If possible, make this process a group leader, so that the postmaster
+	 * can signal any child processes too.
+	 */
+#ifdef HAVE_SETSID
+	if (setsid() < 0)
+		elog(FATAL, "setsid() failed: %m");
+#endif
+
+	/*
+	 * Set up signal handlers.
+	 */
+	if (worker->bgw_flags & BGWORKER_BACKEND_DATABASE_CONNECTION)
+	{
+		/*
+		 * SIGINT is used to signal canceling the current action
+		 */
+		pqsignal(SIGINT, StatementCancelHandler);
+		pqsignal(SIGUSR1, procsignal_sigusr1_handler);
+		pqsignal(SIGFPE, FloatExceptionHandler);
+
+		/* XXX Any other handlers needed here? */
+	}
+	else
+	{
+		pqsignal(SIGINT, SIG_IGN);
+		pqsignal(SIGUSR1, SIG_IGN);
+		pqsignal(SIGFPE, SIG_IGN);	/* XXX may need something else for FPE */
+	}
+
+	/* These are configurable */
+	pqsignal(SIGTERM, worker->bgw_sigterm);
+	pqsignal(SIGHUP, worker->bgw_sighup);
+
+	pqsignal(SIGQUIT, quickdie);
+	InitializeTimeouts();		/* establishes SIGALRM handler */
+
+	pqsignal(SIGPIPE, SIG_IGN);
+	pqsignal(SIGUSR2, SIG_IGN);
+	pqsignal(SIGCHLD, SIG_DFL);
+
+	/*
+	 * If an exception is encountered, processing resumes here.
+	 *
+	 * See notes in postgres.c about the design of this coding.
+	 */
+	if (sigsetjmp(local_sigjmp_buf, 1) != 0)
+	{
+		/* Since not using PG_TRY, must reset error stack by hand */
+		error_context_stack = NULL;
+
+		/* Prevent interrupts while cleaning up */
+		HOLD_INTERRUPTS();
+
+		/* Report the error to the server log */
+		EmitErrorReport();
+
+		/*
+		 * we might need more cleanup here ... LWLockReleaseAll, etc.
+		 * Note that in some cases we will call InitPocess which will register
+		 * ProcKill as exit callback.
+		 */
+
+		/* and go away */
+		proc_exit(1);
+	}
+
+	/* We can now handle ereport(ERROR) */
+	PG_exception_stack = &local_sigjmp_buf;
+
+	/* Early initialization */
+	BaseInit();
+
+	/*
+	 * If necessary, create a per-backend PGPROC struct in shared memory,
+	 * except in the EXEC_BACKEND case where this was done in
+	 * SubPostmasterMain. We must do this before we can use LWLocks (and in the
+	 * EXEC_BACKEND case we already had to do some stuff with LWLocks).
+	 */
+#ifndef EXEC_BACKEND
+	if (worker->bgw_flags & BGWORKER_SHMEM_ACCESS)
+		InitProcess();
+#endif
+
+	/*
+	 * Note that in normal processes, we would call InitPostgres here.  For
+	 * a worker, however, we don't know what database to connect to, yet; so we
+	 * need to wait until the user code does it via
+	 * BackgroundWorkerInitializeConnection().
+	 */
+
+	/*
+	 * Now invoke the user-defined worker code
+	 */
+	worker->bgw_main(worker->bgw_main_arg);
+
+	/* ... and if it returns, we're done */
+	proc_exit(0);
+}
+
+int
+GetNumRegisteredBackgroundWorkers(void)
+{
+	return list_length(BackgroundWorkerList);
+}
+
+/*
+ * Start a new bgworker.
+ * Starting time conditions must have been checked already.
+ *
+ * This code is heavily based on autovacuum.c, q.v.
+ */
+static void
+start_bgworker(RegisteredBgWorker *rw)
+{
+	pid_t		worker_pid;
+
+	ereport(LOG,
+			(errmsg("starting background worker process \"%s\"", rw->worker->bgw_name)));
+
+#ifdef EXEC_BACKEND
+	switch ((worker_pid = bgworker_forkexec()))
+#else
+	switch ((worker_pid = fork_process()))
+#endif
+	{
+		case -1:
+			ereport(LOG,
+					(errmsg("could not fork worker process: %m")));
+			return;
+
+#ifndef EXEC_BACKEND
+		case 0:
+			/* in postmaster child ... */
+			/* Close the postmaster's sockets */
+			ClosePostmasterPorts(false);
+
+			/* Lose the postmaster's on-exit routines */
+			on_exit_reset();
+
+			/* Do NOT release postmaster's working memory context */
+
+			do_start_worker(rw->worker);
+			break;
+#endif
+		default:
+			elog(LOG, "setting worker pid to %d", worker_pid);
+			rw->pid = worker_pid;
+			if (rw->backend)
+				rw->backend->pid = rw->pid;
+	}
+}
+
+/*
+ * Does the current postmaster state require starting a worker with the
+ * specified start_time?
+ */
+static bool
+bgworker_should_start_now(BgWorkerStartTime start_time)
+{
+	/* XXX maybe this'd be better as a table */
+	switch (pmState)
+	{
+		case PM_NO_CHILDREN:
+		case PM_WAIT_DEAD_END:
+		case PM_SHUTDOWN_2:
+		case PM_SHUTDOWN:
+		case PM_WAIT_BACKENDS:
+		case PM_WAIT_READONLY:
+		case PM_WAIT_BACKUP:
+			break;
+
+		case PM_RUN:
+			if (start_time == BgWorkerStart_RecoveryFinished)
+				return true;
+			/* fall through */
+
+		case PM_HOT_STANDBY:
+			if (start_time == BgWorkerStart_ConsistentState)
+				return true;
+			/* fall through */
+
+		case PM_RECOVERY:
+		case PM_STARTUP:
+		case PM_INIT:
+			if (start_time == BgWorkerStart_PostmasterStart)
+				return true;
+			/* fall through */
+	}
+
+	return false;
+}
+
+/*
+ * Allocate the Backend struct for a connected background worker, and add it
+ * to the BackendList.
+ *
+ * Some info from the Backend is copied into the passed rw.
+ */
+static bool
+assign_backendlist_entry(RegisteredBgWorker *rw)
+{
+	Backend *bn = malloc(sizeof(Backend));
+
+	if (bn == NULL)
+	{
+		ereport(LOG,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory")));
+
+		/*
+		 * The worker didn't really crash, but setting this nonzero makes
+		 * postmaster wait a bit before attempting to start it again; if it
+		 * tried again right away, most likely it'd find itself under the same
+		 * memory pressure.
+		 */
+		rw->crashed_at = GetCurrentTimestamp();
+		return false;
+	}
+
+	/*
+	 * Compute the cancel key that will be assigned to this session.
+	 * We probably don't need cancel keys for background workers, but
+	 * we'd better have something random in the field to prevent
+	 * unfriendly people from sending cancels to them.
+	 */
+	MyCancelKey = PostmasterRandom();
+	bn->cancel_key = MyCancelKey;
+
+	bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
+	bn->bkend_type = BACKEND_TYPE_BGWORKER;
+	bn->dead_end = false;
+	DLInitElem(&bn->elem, bn);
+	DLAddHead(BackendList, &bn->elem);
+#ifdef EXEC_BACKEND
+	ShmemBackendArrayAdd(bn);
+#endif
+
+	rw->backend = bn;
+	rw->child_slot = bn->child_slot;
+
+	return true;
+}
+
+/*
+ * If the time is right, start one background worker.  If there are more
+ * workers that need to be started, StartWorkerNeeded is set to true;
+ * otherwise it is reset.
+ */
+static void
+StartOneBackgroundWorker(void)
+{
+	ListCell   *cell;
+	TimestampTz	now = 0;
+
+	HaveCrashedWorker = false;
+
+	foreach(cell, BackgroundWorkerList)
+	{
+		RegisteredBgWorker *rw = lfirst(cell);
+
+		/* already running? */
+		if (rw->pid != 0)
+			continue;
+
+		/*
+		 * If this worker has crashed previously, check how long ago did it
+		 * last happen.  If the last crash is too recent, don't start the
+		 * worker right away; but let it be restarted once enough time has
+		 * passed.
+		 *
+		 * (The other alternative would be to have the worker not be started
+		 * again at all until postmaster is restarted, but this doesn't seem
+		 * as useful.)
+		 */
+		if (rw->crashed_at != 0)
+		{
+			if (now == 0)
+				now = GetCurrentTimestamp();
+
+			if (!TimestampDifferenceExceeds(rw->crashed_at, now,
+											5 * 1000)) /* 60 seconds */
+			{
+				HaveCrashedWorker = true;
+				continue;
+			}
+		}
+
+		if (bgworker_should_start_now(rw->worker->bgw_start_time))
+		{
+			/* reset crash time before calling assign_backendlist_entry */
+			rw->crashed_at = 0;
+
+			/*
+			 * If necessary, allocate and assign the Backend element.  Note we
+			 * must do this before forking, so that we can handle out of memory
+			 * properly.
+			 *
+			 * If not connected, we don't need a Backend element, but we still
+			 * need a PMChildSlot.
+			 */
+			if (rw->worker->bgw_flags & BGWORKER_BACKEND_DATABASE_CONNECTION)
+			{
+				if (!assign_backendlist_entry(rw))
+					return;
+			}
+			else
+				rw->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
+
+			start_bgworker(rw);	/* sets rw->pid */
+
+			/*
+			 * Have ServerLoop call us again.  Note that there might not
+			 * actually *be* another runnable worker, but we don't care all
+			 * that much; we will find out the next time we run.
+			 */
+			StartWorkerNeeded = true;
+			return;
+		}
+	}
+
+	/* no runnable worker found */
+	StartWorkerNeeded = false;
+}
 
 #ifdef EXEC_BACKEND
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 4b66bd3..8dd2b4b 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -87,6 +87,7 @@ pid_t		PostmasterPid = 0;
 bool		IsPostmasterEnvironment = false;
 bool		IsUnderPostmaster = false;
 bool		IsBinaryUpgrade = false;
+bool		IsBackgroundWorker = false;
 
 bool		ExitOnAnyError = false;
 
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 5288aa7..c518c21 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -498,10 +498,10 @@ void
 InitializeSessionUserIdStandalone(void)
 {
 	/*
-	 * This function should only be called in single-user mode and in
-	 * autovacuum workers.
+	 * This function should only be called in single-user mode, in
+	 * autovacuum workers, and in background workers.
 	 */
-	AssertState(!IsUnderPostmaster || IsAutoVacuumWorkerProcess());
+	AssertState(!IsUnderPostmaster || IsAutoVacuumWorkerProcess() || IsBackgroundWorker);
 
 	/* call only once */
 	AssertState(!OidIsValid(AuthenticatedUserId));
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 2eb456d..b87ec6c 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -627,6 +627,19 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 					 errhint("You should immediately run CREATE USER \"%s\" SUPERUSER;.",
 							 username)));
 	}
+	else if (IsBackgroundWorker)
+	{
+		if (username == NULL)
+		{
+			InitializeSessionUserIdStandalone();
+			am_superuser = true;
+		}
+		else
+		{
+			InitializeSessionUserId(username);
+			am_superuser = superuser();
+		}
+	}
 	else
 	{
 		/* normal multiuser case */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 48652b2..dad259a 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -52,6 +52,7 @@
 #include "parser/scansup.h"
 #include "pgstat.h"
 #include "postmaster/autovacuum.h"
+#include "postmaster/bgworker.h"
 #include "postmaster/bgwriter.h"
 #include "postmaster/postmaster.h"
 #include "postmaster/syslogger.h"
@@ -108,7 +109,8 @@
  * removed, we still could not exceed INT_MAX/4 because some places compute
  * 4*MaxBackends without any overflow check.  This is rechecked in
  * check_maxconnections, since MaxBackends is computed as MaxConnections
- * plus autovacuum_max_workers plus one (for the autovacuum launcher).
+ * plus the number of bgworkers plus autovacuum_max_workers plus one (for the
+ * autovacuum launcher).
  */
 #define MAX_BACKENDS	0x7fffff
 
@@ -8611,7 +8613,8 @@ show_tcp_keepalives_count(void)
 static bool
 check_maxconnections(int *newval, void **extra, GucSource source)
 {
-	if (*newval + autovacuum_max_workers + 1 > MAX_BACKENDS)
+	if (*newval + GetNumRegisteredBackgroundWorkers() +
+		autovacuum_max_workers + 1 > MAX_BACKENDS)
 		return false;
 	return true;
 }
@@ -8619,13 +8622,15 @@ check_maxconnections(int *newval, void **extra, GucSource source)
 static void
 assign_maxconnections(int newval, void *extra)
 {
-	MaxBackends = newval + autovacuum_max_workers + 1;
+	MaxBackends = newval + autovacuum_max_workers + 1 +
+		GetNumRegisteredBackgroundWorkers();
 }
 
 static bool
 check_autovacuum_max_workers(int *newval, void **extra, GucSource source)
 {
-	if (MaxConnections + *newval + 1 > MAX_BACKENDS)
+	if (MaxConnections + *newval + 1 + GetNumRegisteredBackgroundWorkers() >
+		MAX_BACKENDS)
 		return false;
 	return true;
 }
@@ -8633,7 +8638,8 @@ check_autovacuum_max_workers(int *newval, void **extra, GucSource source)
 static void
 assign_autovacuum_max_workers(int newval, void *extra)
 {
-	MaxBackends = MaxConnections + newval + 1;
+	MaxBackends = MaxConnections + newval + 1 +
+		GetNumRegisteredBackgroundWorkers();
 }
 
 static bool
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 9f57989..c2bf2e4 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -131,6 +131,7 @@ do { \
 extern pid_t PostmasterPid;
 extern bool IsPostmasterEnvironment;
 extern PGDLLIMPORT bool IsUnderPostmaster;
+extern bool IsBackgroundWorker;
 extern bool IsBinaryUpgrade;
 
 extern bool ExitOnAnyError;
diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h
new file mode 100644
index 0000000..4a7e506
--- /dev/null
+++ b/src/include/postmaster/bgworker.h
@@ -0,0 +1,93 @@
+/*--------------------------------------------------------------------
+ * bgworker.h
+ * 		POSTGRES pluggable background workers interface
+ *
+ * A background worker is a process able to run arbitrary, user-supplied code,
+ * including normal transactions.
+ *
+ * Any external module loaded via shared_preload_libraries can register a
+ * worker.  Then, at the appropriate time, the worker process is forked from
+ * the postmaster and runs the user-supplied "main" function.  This code may
+ * connect to a database and run transactions.  Once started, it stays active
+ * until shutdown or crash.  The process should sleep during periods of
+ * inactivity.
+ *
+ * If the fork() call fails in the postmaster, it will try again later.  Note
+ * that the failure can only be transient (fork failure due to high load,
+ * memory pressure, too many processes, etc); more permanent problems, like
+ * failure to connect to a database, are detected later in the worker and dealt
+ * with just by having the worker exit normally.  Postmaster will launch a new
+ * worker again later.
+ *
+ * Note that there might be more than one worker in a database concurrently,
+ * and the same module may request more than one worker running the same (or
+ * different) code.
+ *
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * 		src/include/postmaster/bgworker.h
+ *--------------------------------------------------------------------
+ */
+#ifndef BGWORKER_H
+#define BGWORKER_H
+
+/*---------------------------------------------------------------------
+ * External module API.
+ *---------------------------------------------------------------------
+ */
+
+/*
+ * Pass this flag to have your worker be able to connect to shared memory.
+ */
+#define BGWORKER_SHMEM_ACCESS						0x0001
+
+/*
+ * This flag means the bgworker requires a database connection.  The connection
+ * is not established automatically; the worker must establish it later.
+ * It requires that BGWORKER_SHMEM_ACCESS was passed too.
+ */
+#define BGWORKER_BACKEND_DATABASE_CONNECTION		0x0002
+
+
+typedef void (*bgworker_main_type)(void *main_arg);
+typedef void (*bgworker_sighdlr_type)(SIGNAL_ARGS);
+
+/*
+ * Points in time at which a bgworker can request to be started
+ */
+typedef enum
+{
+	BgWorkerStart_PostmasterStart,
+	BgWorkerStart_ConsistentState,
+	BgWorkerStart_RecoveryFinished
+} BgWorkerStartTime;
+
+typedef struct BackgroundWorker
+{
+	char	   *bgw_name;
+	int         bgw_flags;
+	BgWorkerStartTime bgw_start_time;
+	bgworker_main_type	bgw_main;
+	void	   *bgw_main_arg;
+	bgworker_sighdlr_type bgw_sighup;
+	bgworker_sighdlr_type bgw_sigterm;
+} BackgroundWorker;
+
+/* Register a new bgworker */
+extern void RegisterBackgroundWorker(BackgroundWorker *worker);
+
+/*
+ * Connect to the specified database, as the specified user.  Only a worker
+ * that passed BGWORKER_BACKEND_DATABASE_CONNECTION during registration may
+ * call this.
+ *
+ * If username is NULL, bootstrapping superuser is used.
+ * If dbname is NULL, connection is made to no specific database;
+ * only shared catalogs can be accessed.
+ */
+extern void BackgroundWorkerInitializeConnection(char *dbname, char *username);
+
+#endif /* BGWORKER_H */
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 0fe7ec2..7ea4462 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -51,6 +51,8 @@ extern void ClosePostmasterPorts(bool am_syslogger);
 
 extern int	MaxLivePostmasterChildren(void);
 
+extern int GetNumRegisteredBackgroundWorkers(void);
+
 #ifdef EXEC_BACKEND
 extern pid_t postmaster_forkexec(int argc, char *argv[]);
 extern void	SubPostmasterMain(int argc, char *argv[]) __attribute__((noreturn));