This patch turns the existing autovacuum launcher into an always running process, partly called the coordinator. If autovacuum is disabled, the coordinator process still gets started and keeps around, but it doesn't dispatch vacuum jobs. The coordinator process now uses imessages to communicate with background (autovacuum) workers and to trigger a vacuum job. So please apply the imessages patches [1] before any of the bg worker ones.

It also adds two new controlling GUCs: min_spare_background_workers and max_spare_background_workers. The autovacuum_max_workers still serves as a limit for the total amount of background/autovacuum workers. (It is going to be renamed in step 4).

Interaction with the postmaster has changed a bit. If autovacuum is disabled, the coordinator isn't started with PMSIGNAL_START_AUTOVAC_LAUNCHER anymore, instead there is an IMSGT_FORCE_VACUUM that any backend might want to send to the coordinator to prevent data loss due to XID wrap around (see changes in access/transam/varsup.c). The SIGUSR2 from postmaster to the coordinator doesn't need to be multiplexed anymore, but is only sent to inform about fork failures.

A note on the dependency on imessages: for just autovacuum, this message passing infrastructure isn't absolutely necessary and could be removed. However, for Postgres-R it turned out to be really helpful and I think chances are good another user of this background worker infrastructure would also want to transfer data of varying size to and from these workers.

Just as in the current version of Postgres, the background worker terminates immediately after having performed a vacuum job.


Open issue: if the postmaster fails to fork a new background worker, the coordinator still waits a whole second after receiving the SIGUSR2 notification signal from the postmaster. That might have been fine with just autovacuum, but for other jobs, namely changeset application in Postgres-R, that's not feasible.


[1] dynshmem and imessages patch
http://archives.postgresql.org/message-id/ab0cd52a64e788f4ecb4515d1e6e4...@localhost

*** src/backend/access/transam/varsup.c	58aea89cb33fe0ee2cb3e10c0a714cd0057a8bdb
--- src/backend/access/transam/varsup.c	058cb2f268e956e37724f1d05d98946d1cc56ab4
*************** GetNewTransactionId(bool isSubXact)
*** 46,51 ****
--- 46,52 ----
  GetNewTransactionId(bool isSubXact)
  {
  	TransactionId xid;
+ 	IMessage *msg;
  
  	/*
  	 * During bootstrap initialization, we return the special bootstrap
*************** GetNewTransactionId(bool isSubXact)
*** 94,105 ****
  		LWLockRelease(XidGenLock);
  
  		/*
! 		 * To avoid swamping the postmaster with signals, we issue the autovac
  		 * request only once per 64K transaction starts.  This still gives
  		 * plenty of chances before we get into real trouble.
  		 */
  		if (IsUnderPostmaster && (xid % 65536) == 0)
! 			SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
  
  		if (IsUnderPostmaster &&
  			TransactionIdFollowsOrEquals(xid, xidStopLimit))
--- 95,109 ----
  		LWLockRelease(XidGenLock);
  
  		/*
! 		 * To avoid swamping the coordinator with signals, we issue the autovac
  		 * request only once per 64K transaction starts.  This still gives
  		 * plenty of chances before we get into real trouble.
  		 */
  		if (IsUnderPostmaster && (xid % 65536) == 0)
! 		{
! 			msg = IMessageCreate(IMSGT_FORCE_VACUUM, 0);
! 			IMessageActivate(msg, GetAutovacuumLauncherId());
! 		}
  
  		if (IsUnderPostmaster &&
  			TransactionIdFollowsOrEquals(xid, xidStopLimit))
*************** SetTransactionIdLimit(TransactionId olde
*** 258,263 ****
--- 262,268 ----
  	TransactionId xidStopLimit;
  	TransactionId xidWrapLimit;
  	TransactionId curXid;
+ 	IMessage *msg;
  
  	Assert(TransactionIdIsNormal(oldest_datfrozenxid));
  
*************** SetTransactionIdLimit(TransactionId olde
*** 341,347 ****
  	 */
  	if (TransactionIdFollowsOrEquals(curXid, xidVacLimit) &&
  		IsUnderPostmaster && !InRecovery)
! 		SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
  
  	/* Give an immediate warning if past the wrap warn point */
  	if (TransactionIdFollowsOrEquals(curXid, xidWarnLimit) && !InRecovery)
--- 346,355 ----
  	 */
  	if (TransactionIdFollowsOrEquals(curXid, xidVacLimit) &&
  		IsUnderPostmaster && !InRecovery)
! 	{
! 		msg = IMessageCreate(IMSGT_FORCE_VACUUM, 0);
! 		IMessageActivate(msg, GetAutovacuumLauncherId());
! 	}
  
  	/* Give an immediate warning if past the wrap warn point */
  	if (TransactionIdFollowsOrEquals(curXid, xidWarnLimit) && !InRecovery)
============================================================
*** src/backend/postmaster/postmaster.c	13da26f5fb9a09c47a8dc3b30f0bbfdb1da0420c
--- src/backend/postmaster/postmaster.c	e6a455be46eee967cafa76cef125cedef4ba986f
*************** bool		redirection_done = false;	/* stder
*** 294,301 ****
  
  bool		redirection_done = false;	/* stderr redirected for syslogger? */
  
- /* received START_AUTOVAC_LAUNCHER signal */
- 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;
--- 294,299 ----
*************** ServerLoop(void)
*** 1463,1476 ****
  			WalWriterPID = StartWalWriter();
  
  		/* If we have lost the autovacuum launcher, try to start a new one */
! 		if (AutoVacPID == 0 &&
! 			(AutoVacuumingActive() || start_autovac_launcher) &&
! 			pmState == PM_RUN)
! 		{
  			AutoVacPID = StartAutoVacLauncher();
- 			if (AutoVacPID != 0)
- 				start_autovac_launcher = false; /* signal processed */
- 		}
  
  		/* If we have lost the archiver, try to start a new one */
  		if (XLogArchivingActive() && PgArchPID == 0 && pmState == PM_RUN)
--- 1461,1468 ----
  			WalWriterPID = StartWalWriter();
  
  		/* If we have lost the autovacuum launcher, try to start a new one */
! 		if (AutoVacPID == 0 && pmState == PM_RUN)
  			AutoVacPID = StartAutoVacLauncher();
  
  		/* If we have lost the archiver, try to start a new one */
  		if (XLogArchivingActive() && PgArchPID == 0 && pmState == PM_RUN)
*************** reaper(SIGNAL_ARGS)
*** 2390,2396 ****
  			 */
  			if (WalWriterPID == 0)
  				WalWriterPID = StartWalWriter();
! 			if (AutoVacuumingActive() && AutoVacPID == 0)
  				AutoVacPID = StartAutoVacLauncher();
  			if (XLogArchivingActive() && PgArchPID == 0)
  				PgArchPID = pgarch_start();
--- 2382,2388 ----
  			 */
  			if (WalWriterPID == 0)
  				WalWriterPID = StartWalWriter();
! 			if (AutoVacPID == 0)
  				AutoVacPID = StartAutoVacLauncher();
  			if (XLogArchivingActive() && PgArchPID == 0)
  				PgArchPID = pgarch_start();
*************** sigusr1_handler(SIGNAL_ARGS)
*** 4198,4217 ****
  		signal_child(SysLoggerPID, SIGUSR1);
  	}
  
- 	if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER))
- 	{
- 		/*
- 		 * Start one iteration of the autovacuum daemon, even if autovacuuming
- 		 * is nominally not enabled.  This is so we can have an active defense
- 		 * against transaction ID wraparound.  We set a flag for the main loop
- 		 * to do it rather than trying to do it here --- this is because the
- 		 * autovac process itself may send the signal, and we want to handle
- 		 * that by launching another iteration as soon as the current one
- 		 * completes.
- 		 */
- 		start_autovac_launcher = true;
- 	}
- 
  	if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER))
  	{
  		/* The autovacuum launcher wants us to start a worker process. */
--- 4190,4195 ----
*************** StartAutovacuumWorker(void)
*** 4518,4536 ****
  	}
  
  	/*
! 	 * Report the failure to the launcher, if it's running.  (If it's not, we
! 	 * might not even be connected to shared memory, so don't try to call
! 	 * AutoVacWorkerFailed.)  Note that we also need to signal it so that it
! 	 * responds to the condition, but we don't do that here, instead waiting
! 	 * for ServerLoop to do it.  This way we avoid a ping-pong signalling in
! 	 * quick succession between the autovac launcher and postmaster in case
! 	 * things get ugly.
  	 */
  	if (AutoVacPID != 0)
- 	{
- 		AutoVacWorkerFailed();
  		avlauncher_needs_signal = true;
- 	}
  }
  
  /*
--- 4496,4508 ----
  	}
  
  	/*
! 	 * Report the failure to the coordinator, if it's running. Note that we
! 	 * wait for the ServerLoop to actually trigger the signal. This way we
! 	 * avoid a ping-pong signalling in quick succession between coordinator
! 	 * and postmaster in case things get ugly.
  	 */
  	if (AutoVacPID != 0)
  		avlauncher_needs_signal = true;
  }
  
  /*
============================================================
*** src/backend/storage/lmgr/proc.c	410c53ed63ba8902d9747e9ad91a639e21195411
--- src/backend/storage/lmgr/proc.c	bbd84c0cdc85587bd4e04f1b69ead6ea11bb08dd
*************** InitProcess(void)
*** 263,276 ****
  
  	set_spins_per_delay(procglobal->spins_per_delay);
  
! 	if (IsAnyAutoVacuumProcess())
  		MyProc = procglobal->autovacFreeProcs;
  	else
  		MyProc = procglobal->freeProcs;
  
  	if (MyProc != NULL)
  	{
! 		if (IsAnyAutoVacuumProcess())
  			procglobal->autovacFreeProcs = (PGPROC *) MyProc->links.next;
  		else
  			procglobal->freeProcs = (PGPROC *) MyProc->links.next;
--- 263,276 ----
  
  	set_spins_per_delay(procglobal->spins_per_delay);
  
! 	if (IsAutoVacuumLauncherProcess() || IsAutoVacuumWorkerProcess())
  		MyProc = procglobal->autovacFreeProcs;
  	else
  		MyProc = procglobal->freeProcs;
  
  	if (MyProc != NULL)
  	{
! 		if (IsAutoVacuumLauncherProcess() || IsAutoVacuumWorkerProcess())
  			procglobal->autovacFreeProcs = (PGPROC *) MyProc->links.next;
  		else
  			procglobal->freeProcs = (PGPROC *) MyProc->links.next;
*************** ProcKill(int code, Datum arg)
*** 682,688 ****
  	SpinLockAcquire(ProcStructLock);
  
  	/* Return PGPROC structure (and semaphore) to appropriate freelist */
! 	if (IsAnyAutoVacuumProcess())
  	{
  		MyProc->links.next = (SHM_QUEUE *) procglobal->autovacFreeProcs;
  		procglobal->autovacFreeProcs = MyProc;
--- 682,688 ----
  	SpinLockAcquire(ProcStructLock);
  
  	/* Return PGPROC structure (and semaphore) to appropriate freelist */
! 	if (IsAutoVacuumLauncherProcess() || IsAutoVacuumWorkerProcess())
  	{
  		MyProc->links.next = (SHM_QUEUE *) procglobal->autovacFreeProcs;
  		procglobal->autovacFreeProcs = MyProc;
*************** ProcKill(int code, Datum arg)
*** 708,717 ****
  	 */
  	if (IsUnderPostmaster && !IsAutoVacuumLauncherProcess())
  		MarkPostmasterChildInactive();
- 
- 	/* wake autovac launcher if needed -- see comments in FreeWorkerInfo */
- 	if (AutovacuumLauncherPid != 0)
- 		kill(AutovacuumLauncherPid, SIGUSR2);
  }
  
  /*
--- 708,713 ----
============================================================
*** src/backend/utils/misc/guc.c	0d02c70862077335d5f009dc9f295b2dc7071c52
--- src/backend/utils/misc/guc.c	b1a0bdd9b727094182159680278763a09f3ded9c
*************** static struct config_bool ConfigureNames
*** 935,944 ****
  
  	{
  		{"autovacuum", PGC_SIGHUP, AUTOVACUUM,
! 			gettext_noop("Starts the autovacuum subprocess."),
  			NULL
  		},
! 		&autovacuum_start_daemon,
  		true, NULL, NULL
  	},
  
--- 935,944 ----
  
  	{
  		{"autovacuum", PGC_SIGHUP, AUTOVACUUM,
! 			gettext_noop("Enable automatic vacuuming."),
  			NULL
  		},
! 		&autovacuum_enabled,
  		true, NULL, NULL
  	},
  
*************** static struct config_int ConfigureNamesI
*** 1941,1946 ****
--- 1941,1963 ----
  	},
  
  	{
+ 		{"min_spare_background_workers", PGC_POSTMASTER, AUTOVACUUM,
+ 			gettext_noop("Sets the maximum number of simultaneously running autovacuum worker processes."),
+ 			NULL
+ 		},
+ 		&min_spare_background_workers,
+ 		0, 0, INT_MAX / 4, NULL, NULL
+ 	},
+ 	{
+ 		{"max_spare_background_workers", PGC_POSTMASTER, AUTOVACUUM,
+ 			gettext_noop("Sets the maximum number of simultaneously running autovacuum worker processes."),
+ 			NULL
+ 		},
+ 		&max_spare_background_workers,
+ 		0, 0, INT_MAX / 4, NULL, NULL
+ 	},
+ 
+ 	{
  		{"autovacuum_naptime", PGC_SIGHUP, AUTOVACUUM,
  			gettext_noop("Time to sleep between autovacuum runs."),
  			NULL,
============================================================
*** src/backend/utils/misc/postgresql.conf.sample	3b137fa18da4ec43ae76dc954fd19449556fbdce
--- src/backend/utils/misc/postgresql.conf.sample	14a99f6b70482f481ccd82dad86e31a9421c9336
***************
*** 404,419 ****
  
  
  #------------------------------------------------------------------------------
  # AUTOVACUUM PARAMETERS
  #------------------------------------------------------------------------------
  
! #autovacuum = on			# Enable autovacuum subprocess?  'on' 
! 					# requires track_counts to also be on.
  #log_autovacuum_min_duration = -1	# -1 disables, 0 logs all actions and
  					# their durations, > 0 logs only
  					# actions running at least this number
  					# of milliseconds.
- #autovacuum_max_workers = 3		# max number of autovacuum subprocesses
  #autovacuum_naptime = 1min		# time between autovacuum runs
  #autovacuum_vacuum_threshold = 50	# min number of row updates before
  					# vacuum
--- 404,431 ----
  
  
  #------------------------------------------------------------------------------
+ # BACKGROUND WORKER PARAMETERS (including autovacuum)
+ #------------------------------------------------------------------------------
+ 
+ #autovacuum_max_workers = 3		# max number of background helper
+ 			  		# subprocesses (for all databases)
+ 
+ #min_spare_background_helpers = 0	# min spare workers to prefork and
+ 			  		# keep waiting for jobs, per database
+ #max_spare_background_helpers = 0	# max spare helpers per database
+ 
+ 
+ 
+ #------------------------------------------------------------------------------
  # AUTOVACUUM PARAMETERS
  #------------------------------------------------------------------------------
  
! #autovacuum = on			# Enable autovacuum?  'on' requires
! 					# track_counts to also be on.
  #log_autovacuum_min_duration = -1	# -1 disables, 0 logs all actions and
  					# their durations, > 0 logs only
  					# actions running at least this number
  					# of milliseconds.
  #autovacuum_naptime = 1min		# time between autovacuum runs
  #autovacuum_vacuum_threshold = 50	# min number of row updates before
  					# vacuum
============================================================
*** src/include/storage/pmsignal.h	517450db2bab1f2a3dd7320a273b8e7320c29bac
--- src/include/storage/pmsignal.h	c8113d31e2e76fc64ad82209ff2fb8aea91f68b3
*************** typedef enum
*** 26,32 ****
  	PMSIGNAL_BEGIN_HOT_STANDBY, /* begin Hot Standby */
  	PMSIGNAL_WAKEN_ARCHIVER,	/* send a NOTIFY signal to xlog archiver */
  	PMSIGNAL_ROTATE_LOGFILE,	/* send SIGUSR1 to syslogger to rotate logfile */
- 	PMSIGNAL_START_AUTOVAC_LAUNCHER,	/* start an autovacuum launcher */
  	PMSIGNAL_START_AUTOVAC_WORKER,		/* start an autovacuum worker */
  	PMSIGNAL_START_WALRECEIVER, /* start a walreceiver */
  
--- 26,31 ----
============================================================
*** src/backend/postmaster/autovacuum.c	e0cb7d340f41ac1fd6f3d6709cb9ab42052a31ab
--- src/backend/postmaster/autovacuum.c	3b9cbe9ba0d576b32d490f21969b073a7ec55b98
***************
*** 101,108 ****
  /*
   * GUC parameters
   */
! bool		autovacuum_start_daemon = false;
  int			autovacuum_max_workers;
  int			autovacuum_naptime;
  int			autovacuum_vac_thresh;
  double		autovacuum_vac_scale;
--- 101,110 ----
  /*
   * GUC parameters
   */
! bool		autovacuum_enabled = false;
  int			autovacuum_max_workers;
+ int			min_spare_background_workers;
+ int			max_spare_background_workers;
  int			autovacuum_naptime;
  int			autovacuum_vac_thresh;
  double		autovacuum_vac_scale;
*************** typedef struct autovac_table
*** 188,199 ****
   * an array of these in shared memory, sized according to
   * autovacuum_max_workers.
   *
!  * wi_links		entry into free list or running list
!  * wi_dboid		OID of the database this worker is supposed to work on
!  * wi_tableoid	OID of the table currently being vacuumed
!  * wi_proc		pointer to PGPROC of the running worker, NULL if not started
!  * wi_launchtime Time at which this worker was launched
!  * wi_cost_*	Vacuum cost-based delay parameters current in this worker
   *
   * All fields are protected by AutovacuumLock, except for wi_tableoid which is
   * protected by AutovacuumScheduleLock (which is read-only for everyone except
--- 190,201 ----
   * an array of these in shared memory, sized according to
   * autovacuum_max_workers.
   *
!  * wi_links			entry into free list or running list
!  * wi_dboid			OID of the database this worker is supposed to work on
!  * wi_tableoid		OID of the table currently being vacuumed
!  * wi_backend_id	id of the running worker backend, NULL if not started
!  * wi_launchtime	Time at which this worker was launched
!  * wi_cost_*		Vacuum cost-based delay parameters current in this worker
   *
   * All fields are protected by AutovacuumLock, except for wi_tableoid which is
   * protected by AutovacuumScheduleLock (which is read-only for everyone except
*************** typedef struct WorkerInfoData
*** 205,211 ****
  	SHM_QUEUE	wi_links;
  	Oid			wi_dboid;
  	Oid			wi_tableoid;
! 	PGPROC	   *wi_proc;
  	TimestampTz wi_launchtime;
  	int			wi_cost_delay;
  	int			wi_cost_limit;
--- 207,213 ----
  	SHM_QUEUE	wi_links;
  	Oid			wi_dboid;
  	Oid			wi_tableoid;
! 	BackendId	wi_backend_id;
  	TimestampTz wi_launchtime;
  	int			wi_cost_delay;
  	int			wi_cost_limit;
*************** typedef struct WorkerInfoData *WorkerInf
*** 214,250 ****
  
  typedef struct WorkerInfoData *WorkerInfo;
  
- /*
-  * Possible signals received by the launcher from remote processes.  These are
-  * stored atomically in shared memory so that other processes can set them
-  * without locking.
-  */
- typedef enum
- {
- 	AutoVacForkFailed,			/* failed trying to start a worker */
- 	AutoVacRebalance,			/* rebalance the cost limits */
- 	AutoVacNumSignals			/* must be last */
- } AutoVacuumSignal;
- 
  /*-------------
   * The main autovacuum shmem struct.  On shared memory we store this main
   * struct and the array of WorkerInfo structs.	This struct keeps:
   *
!  * av_signal		set by other processes to indicate various conditions
!  * av_launcherpid	the PID of the autovacuum launcher
   * av_freeWorkers	the WorkerInfo freelist
   * av_runningWorkers the WorkerInfo non-free queue
   * av_startingWorker pointer to WorkerInfo currently being started (cleared by
   *					the worker itself as soon as it's up and running)
   *
!  * This struct is protected by AutovacuumLock, except for av_signal and parts
!  * of the worker list (see above).
   *-------------
   */
  typedef struct
  {
! 	sig_atomic_t av_signal[AutoVacNumSignals];
! 	pid_t		av_launcherpid;
  	WorkerInfo	av_freeWorkers;
  	SHM_QUEUE	av_runningWorkers;
  	WorkerInfo	av_startingWorker;
--- 216,238 ----
  
  typedef struct WorkerInfoData *WorkerInfo;
  
  /*-------------
   * The main autovacuum shmem struct.  On shared memory we store this main
   * struct and the array of WorkerInfo structs.	This struct keeps:
   *
!  * av_launcherid	the BackendId of the autovacuum launcher
   * av_freeWorkers	the WorkerInfo freelist
   * av_runningWorkers the WorkerInfo non-free queue
   * av_startingWorker pointer to WorkerInfo currently being started (cleared by
   *					the worker itself as soon as it's up and running)
   *
!  * This struct is protected by AutovacuumLock, except for parts of the worker
!  * list (see above).
   *-------------
   */
  typedef struct
  {
! 	BackendId	av_launcherid;
  	WorkerInfo	av_freeWorkers;
  	SHM_QUEUE	av_runningWorkers;
  	WorkerInfo	av_startingWorker;
*************** static WorkerInfo MyWorkerInfo = NULL;
*** 259,277 ****
  /* Pointer to my own WorkerInfo, valid on each worker */
  static WorkerInfo MyWorkerInfo = NULL;
  
- /* PID of launcher, valid only in worker while shutting down */
- int			AutovacuumLauncherPid = 0;
  
  #ifdef EXEC_BACKEND
  static pid_t avlauncher_forkexec(void);
  static pid_t avworker_forkexec(void);
  #endif
  NON_EXEC_STATIC void AutoVacWorkerMain(int argc, char *argv[]);
  NON_EXEC_STATIC void AutoVacLauncherMain(int argc, char *argv[]);
  
! static Oid	do_start_worker(void);
! static void launcher_determine_sleep(bool canlaunch, bool recursing,
! 						 struct timeval * nap);
  static void launch_worker(TimestampTz now);
  static List *get_database_list(void);
  static void rebuild_database_list(Oid newdb);
--- 247,267 ----
  /* Pointer to my own WorkerInfo, valid on each worker */
  static WorkerInfo MyWorkerInfo = NULL;
  
  
  #ifdef EXEC_BACKEND
  static pid_t avlauncher_forkexec(void);
  static pid_t avworker_forkexec(void);
  #endif
  NON_EXEC_STATIC void AutoVacWorkerMain(int argc, char *argv[]);
+ static void handle_imessage(IMessage *msg);
  NON_EXEC_STATIC void AutoVacLauncherMain(int argc, char *argv[]);
  
! static void CoordinatorMaybeTriggerAutoVacuum(void);
! 
! static void do_start_worker(avw_dbase *avdb);
! static Oid	do_start_autovacuum_worker(void);
! static void launcher_determine_sleep(bool can_launch, bool recursing,
! 									 struct timeval *nap);
  static void launch_worker(TimestampTz now);
  static List *get_database_list(void);
  static void rebuild_database_list(Oid newdb);
*************** AutoVacLauncherMain(int argc, char *argv
*** 386,391 ****
--- 376,382 ----
  AutoVacLauncherMain(int argc, char *argv[])
  {
  	sigjmp_buf	local_sigjmp_buf;
+ 	IMessage   *msg = NULL;
  
  	/* we are a postmaster subprocess now */
  	IsUnderPostmaster = true;
*************** AutoVacLauncherMain(int argc, char *argv
*** 526,540 ****
  	/* must unblock signals before calling rebuild_database_list */
  	PG_SETMASK(&UnBlockSig);
  
! 	/* in emergency mode, just start a worker and go away */
! 	if (!AutoVacuumingActive())
! 	{
! 		do_start_worker();
! 		proc_exit(0);			/* done */
! 	}
  
- 	AutoVacuumShmem->av_launcherpid = MyProcPid;
- 
  	/*
  	 * Create the initial database list.  The invariant we want this list to
  	 * keep is that it's ordered by decreasing next_time.  As soon as an entry
--- 517,524 ----
  	/* must unblock signals before calling rebuild_database_list */
  	PG_SETMASK(&UnBlockSig);
  
! 	AutoVacuumShmem->av_launcherid = MyBackendId;
  
  	/*
  	 * Create the initial database list.  The invariant we want this list to
  	 * keep is that it's ordered by decreasing next_time.  As soon as an entry
*************** AutoVacLauncherMain(int argc, char *argv
*** 547,555 ****
  	for (;;)
  	{
  		struct timeval nap;
- 		TimestampTz current_time = 0;
- 		bool		can_launch;
- 		Dlelem	   *elem;
  
  		/*
  		 * Emergency bailout if postmaster has died.  This is to avoid the
--- 531,536 ----
*************** AutoVacLauncherMain(int argc, char *argv
*** 595,601 ****
  			if (!PostmasterIsAlive(true))
  				proc_exit(1);
  
! 			if (got_SIGTERM || got_SIGHUP || got_SIGUSR2)
  				break;
  		}
  
--- 576,583 ----
  			if (!PostmasterIsAlive(true))
  				proc_exit(1);
  
! 			msg = IMessageCheck();
! 			if (got_SIGTERM || got_SIGHUP || got_SIGUSR2 || msg)
  				break;
  		}
  
*************** AutoVacLauncherMain(int argc, char *argv
*** 607,619 ****
  
  		if (got_SIGHUP)
  		{
  			got_SIGHUP = false;
  			ProcessConfigFile(PGC_SIGHUP);
  
- 			/* shutdown requested in config file? */
- 			if (!AutoVacuumingActive())
- 				break;
- 
  			/* rebalance in case the default cost parameters changed */
  			LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  			autovac_balance_cost();
--- 589,601 ----
  
  		if (got_SIGHUP)
  		{
+ #ifdef COORDINATOR_DEBUG
+ 			elog(DEBUG5, "Coordinator: got SIGHUP");
+ #endif
+ 
  			got_SIGHUP = false;
  			ProcessConfigFile(PGC_SIGHUP);
  
  			/* rebalance in case the default cost parameters changed */
  			LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  			autovac_balance_cost();
*************** AutoVacLauncherMain(int argc, char *argv
*** 624,647 ****
  		}
  
  		/*
! 		 * a worker finished, or postmaster signalled failure to start a
! 		 * worker
  		 */
  		if (got_SIGUSR2)
  		{
  			got_SIGUSR2 = false;
  
! 			/* rebalance cost limits, if needed */
! 			if (AutoVacuumShmem->av_signal[AutoVacRebalance])
  			{
- 				LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
- 				AutoVacuumShmem->av_signal[AutoVacRebalance] = false;
- 				autovac_balance_cost();
- 				LWLockRelease(AutovacuumLock);
- 			}
  
- 			if (AutoVacuumShmem->av_signal[AutoVacForkFailed])
- 			{
  				/*
  				 * If the postmaster failed to start a new worker, we sleep
  				 * for a little while and resend the signal.  The new worker's
--- 606,624 ----
  		}
  
  		/*
! 		 * postmaster signalled failure to start a worker
  		 */
  		if (got_SIGUSR2)
  		{
+ #ifdef COORDINATOR_DEBUG
+ 			elog(DEBUG5, "Coordinator: got SIGUSR2");
+ #endif
+ 
  			got_SIGUSR2 = false;
  
! 			/* FIXME: fix indentation after merging */
  			{
  
  				/*
  				 * If the postmaster failed to start a new worker, we sleep
  				 * for a little while and resend the signal.  The new worker's
*************** AutoVacLauncherMain(int argc, char *argv
*** 651,665 ****
  				 * XXX should we put a limit to the number of times we retry?
  				 * I don't think it makes much sense, because a future start
  				 * of a worker will continue to fail in the same way.
  				 */
- 				AutoVacuumShmem->av_signal[AutoVacForkFailed] = false;
  				pg_usleep(1000000L);	/* 1s */
  				SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER);
  				continue;
  			}
  		}
  
  		/*
  		 * There are some conditions that we need to check before trying to
  		 * start a launcher.  First, we need to make sure that there is a
  		 * launcher slot available.  Second, we need to make sure that no
--- 628,740 ----
  				 * XXX should we put a limit to the number of times we retry?
  				 * I don't think it makes much sense, because a future start
  				 * of a worker will continue to fail in the same way.
+ 				 *
+ 				 * FIXME: for the autovac launcher, it might have been okay
+ 				 *        to just sleep. But the coordinator needs to remain
+ 				 *        as responsive as possible, even if the postmaster
+ 				 *        is currently unable to fork new workers.
  				 */
  				pg_usleep(1000000L);	/* 1s */
  				SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER);
  				continue;
  			}
  		}
  
+ 		/* handle pending imessages */
+ 		while (msg != NULL)
+ 		{
+ 			handle_imessage(msg);
+ 			msg = IMessageCheck();
+ 		}
+ 
  		/*
+ 		 * Periodically check and trigger autovacuum workers, if autovacuum
+ 		 * is enabled.
+ 		 */
+ 		if (autovacuum_enabled)
+ 			CoordinatorMaybeTriggerAutoVacuum();
+ 	}
+ 
+ 	/* Normal exit from the autovac launcher is here */
+ 	ereport(LOG,
+ 			(errmsg("autovacuum launcher shutting down")));
+ 	AutoVacuumShmem->av_launcherid = InvalidBackendId;
+ 
+ 	proc_exit(0);				/* done */
+ }
+ 
+ void
+ handle_imessage(IMessage *msg)
+ {
+ 	IMessageType	msg_type;
+ 	BackendId		msg_sender;
+ 
+ 	msg_type = msg->type;
+ 	msg_sender = msg->sender;
+ 
+ #ifdef COORDINATOR_DEBUG
+ 	elog(DEBUG3, "Coordinator: received %s from pid %d",
+ 		 decode_imessage_type(msg->type), msg->sender);
+ #endif
+ 
+ 	switch (msg->type)
+ 	{
+ 		/*
+ 		 * Standard messages from background worker processes
+ 		 */
+ 		case IMSGT_REGISTER_WORKER:
+ 		case IMSGT_READY:
+ 			/* consume the message */
+ 			IMessageRemove(msg);
+ 
+ 			/*
+ 			 * default to immediately command the new background
+ 			 * worker to perform an autovacuum step.
+ 			 */
+ 			if (IMSGT_REGISTER_WORKER)
+ 			{
+ 				msg = IMessageCreate(IMSGT_PERFORM_VACUUM, 0);
+ 				IMessageActivate(msg, msg_sender);
+ 			}
+ 
+ 			/*
+ 			 * Rebalance cost limits, as the worker has already reported its
+ 			 * startup to the stats collector.  However, that needs to be
+ 			 * removed, so there's probably no point in rebalancing here.
+ 			 * So: FIXME.
+ 			 */
+ 			LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
+ 			autovac_balance_cost();
+ 			LWLockRelease(AutovacuumLock);
+ 
+ 			break;
+ 
+ 		case IMSGT_FORCE_VACUUM:
+ 			/* consume the message */
+ 			IMessageRemove(msg);
+ 
+ 			/* trigger an autovacuum worker */
+ 			do_start_autovacuum_worker();
+ 
+ 			break;
+ 
+ 		default:
+ 			elog(WARNING, "Unknown message type: %c, ignored!", msg->type);
+ 			IMessageRemove(msg);
+ 	}
+ }
+ 
+ static void
+ CoordinatorMaybeTriggerAutoVacuum()
+ {
+ 	TimestampTz current_time = 0;
+ 	bool		can_launch;
+ 	Dlelem	   *elem;
+ 
+ 	/* FIXME: indentation */
+ 	{
+ 
+ 		/*
  		 * There are some conditions that we need to check before trying to
  		 * start a launcher.  First, we need to make sure that there is a
  		 * launcher slot available.  Second, we need to make sure that no
*************** AutoVacLauncherMain(int argc, char *argv
*** 676,681 ****
--- 751,760 ----
  			int			waittime;
  			WorkerInfo	worker = AutoVacuumShmem->av_startingWorker;
  
+ #ifdef COORDINATOR_DEBUG
+ 			elog(DEBUG5, "Coordinator: another worker is starting...");
+ #endif
+ 
  			/*
  			 * We can't launch another worker when another one is still
  			 * starting up (or failed while doing so), so just sleep for a bit
*************** AutoVacLauncherMain(int argc, char *argv
*** 709,715 ****
  					worker = AutoVacuumShmem->av_startingWorker;
  					worker->wi_dboid = InvalidOid;
  					worker->wi_tableoid = InvalidOid;
! 					worker->wi_proc = NULL;
  					worker->wi_launchtime = 0;
  					worker->wi_links.next = (SHM_QUEUE *) AutoVacuumShmem->av_freeWorkers;
  					AutoVacuumShmem->av_freeWorkers = worker;
--- 788,794 ----
  					worker = AutoVacuumShmem->av_startingWorker;
  					worker->wi_dboid = InvalidOid;
  					worker->wi_tableoid = InvalidOid;
! 					worker->wi_backend_id = InvalidBackendId;
  					worker->wi_launchtime = 0;
  					worker->wi_links.next = (SHM_QUEUE *) AutoVacuumShmem->av_freeWorkers;
  					AutoVacuumShmem->av_freeWorkers = worker;
*************** AutoVacLauncherMain(int argc, char *argv
*** 722,730 ****
  		}
  		LWLockRelease(AutovacuumLock);	/* either shared or exclusive */
  
! 		/* if we can't do anything, just go back to sleep */
  		if (!can_launch)
! 			continue;
  
  		/* We're OK to start a new worker */
  
--- 801,809 ----
  		}
  		LWLockRelease(AutovacuumLock);	/* either shared or exclusive */
  
! 		/* if we can't do anything, just return. */
  		if (!can_launch)
! 			return;
  
  		/* We're OK to start a new worker */
  
*************** AutoVacLauncherMain(int argc, char *argv
*** 754,777 ****
  			launch_worker(current_time);
  		}
  	}
- 
- 	/* Normal exit from the autovac launcher is here */
- 	ereport(LOG,
- 			(errmsg("autovacuum launcher shutting down")));
- 	AutoVacuumShmem->av_launcherpid = 0;
- 
- 	proc_exit(0);				/* done */
  }
  
  /*
   * Determine the time to sleep, based on the database list.
   *
!  * The "canlaunch" parameter indicates whether we can start a worker right now,
   * for example due to the workers being all busy.  If this is false, we will
   * cause a long sleep, which will be interrupted when a worker exits.
   */
  static void
! launcher_determine_sleep(bool canlaunch, bool recursing, struct timeval * nap)
  {
  	Dlelem	   *elem;
  
--- 833,849 ----
  			launch_worker(current_time);
  		}
  	}
  }
  
  /*
   * Determine the time to sleep, based on the database list.
   *
!  * The "can_launch" parameter indicates whether we can start a worker right now,
   * for example due to the workers being all busy.  If this is false, we will
   * cause a long sleep, which will be interrupted when a worker exits.
   */
  static void
! launcher_determine_sleep(bool can_launch, bool recursing, struct timeval *nap)
  {
  	Dlelem	   *elem;
  
*************** launcher_determine_sleep(bool canlaunch,
*** 781,787 ****
  	 * in the past; if the first entry has too close a next_worker value, or a
  	 * time in the past, we will sleep a small nominal time.
  	 */
! 	if (!canlaunch)
  	{
  		nap->tv_sec = autovacuum_naptime;
  		nap->tv_usec = 0;
--- 853,859 ----
  	 * in the past; if the first entry has too close a next_worker value, or a
  	 * time in the past, we will sleep a small nominal time.
  	 */
! 	if (!can_launch)
  	{
  		nap->tv_sec = autovacuum_naptime;
  		nap->tv_usec = 0;
*************** launcher_determine_sleep(bool canlaunch,
*** 820,826 ****
  	if (nap->tv_sec == 0 && nap->tv_usec == 0 && !recursing)
  	{
  		rebuild_database_list(InvalidOid);
! 		launcher_determine_sleep(canlaunch, true, nap);
  		return;
  	}
  
--- 892,898 ----
  	if (nap->tv_sec == 0 && nap->tv_usec == 0 && !recursing)
  	{
  		rebuild_database_list(InvalidOid);
! 		launcher_determine_sleep(can_launch, true, nap);
  		return;
  	}
  
*************** db_comparator(const void *a, const void 
*** 1052,1058 ****
  /*
   * do_start_worker
   *
!  * Bare-bones procedure for starting an autovacuum worker from the launcher.
   * It determines what database to work on, sets up shared memory stuff and
   * signals postmaster to start the worker.	It fails gracefully if invoked when
   * autovacuum_workers are already active.
--- 1124,1170 ----
  /*
   * do_start_worker
   *
!  * Bare-bones procedure for starting an autovacuum worker from the
!  * coordinator. It sets up shared memory stuff and signals the postmaster to
!  * start a worker.
!  */
! void
! do_start_worker(avw_dbase *avdb)
! {
! 	WorkerInfo	worker;
! 
! #ifdef COORDINATOR_DEBUG
! 	elog(DEBUG5, "Coordinator: requesting worker for database %s",
! 		 avdb->adw_name);
! #endif
! 
! 	LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
! 
! 	/*
! 	 * Get a worker entry from the freelist.  We checked above, so there
! 	 * really should be a free slot -- complain very loudly if there
! 	 * isn't.
! 	 */
! 	worker = AutoVacuumShmem->av_freeWorkers;
! 	if (worker == NULL)
! 		elog(FATAL, "no free worker found");
! 
! 	AutoVacuumShmem->av_freeWorkers = (WorkerInfo) worker->wi_links.next;
! 
! 	worker->wi_dboid = avdb->adw_datid;
! 	worker->wi_backend_id = InvalidBackendId;
! 	worker->wi_launchtime = GetCurrentTimestamp();
! 
! 	AutoVacuumShmem->av_startingWorker = worker;
! 
! 	LWLockRelease(AutovacuumLock);
! 
! 	SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER);
! }
! 
! /*
!  * do_start_autovacuum_worker
!  *
   * It determines what database to work on, sets up shared memory stuff and
   * signals postmaster to start the worker.	It fails gracefully if invoked when
   * autovacuum_workers are already active.
*************** static Oid
*** 1061,1067 ****
   * or InvalidOid if no worker was actually started.
   */
  static Oid
! do_start_worker(void)
  {
  	List	   *dblist;
  	ListCell   *cell;
--- 1173,1179 ----
   * or InvalidOid if no worker was actually started.
   */
  static Oid
! do_start_autovacuum_worker(void)
  {
  	List	   *dblist;
  	ListCell   *cell;
*************** do_start_worker(void)
*** 1206,1236 ****
  	/* Found a database -- process it */
  	if (avdb != NULL)
  	{
! 		WorkerInfo	worker;
! 
! 		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
! 
! 		/*
! 		 * Get a worker entry from the freelist.  We checked above, so there
! 		 * really should be a free slot -- complain very loudly if there
! 		 * isn't.
! 		 */
! 		worker = AutoVacuumShmem->av_freeWorkers;
! 		if (worker == NULL)
! 			elog(FATAL, "no free worker found");
! 
! 		AutoVacuumShmem->av_freeWorkers = (WorkerInfo) worker->wi_links.next;
! 
! 		worker->wi_dboid = avdb->adw_datid;
! 		worker->wi_proc = NULL;
! 		worker->wi_launchtime = GetCurrentTimestamp();
! 
! 		AutoVacuumShmem->av_startingWorker = worker;
! 
! 		LWLockRelease(AutovacuumLock);
! 
! 		SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER);
! 
  		retval = avdb->adw_datid;
  	}
  	else if (skipit)
--- 1318,1324 ----
  	/* Found a database -- process it */
  	if (avdb != NULL)
  	{
! 		do_start_worker(avdb);
  		retval = avdb->adw_datid;
  	}
  	else if (skipit)
*************** do_start_worker(void)
*** 1254,1260 ****
   * Wrapper for starting a worker from the launcher.  Besides actually starting
   * it, update the database list to reflect the next time that another one will
   * need to be started on the selected database.  The actual database choice is
!  * left to do_start_worker.
   *
   * This routine is also expected to insert an entry into the database list if
   * the selected database was previously absent from the list.
--- 1342,1348 ----
   * Wrapper for starting a worker from the launcher.  Besides actually starting
   * it, update the database list to reflect the next time that another one will
   * need to be started on the selected database.  The actual database choice is
!  * left to do_start_autovacuum_worker.
   *
   * This routine is also expected to insert an entry into the database list if
   * the selected database was previously absent from the list.
*************** launch_worker(TimestampTz now)
*** 1265,1271 ****
  	Oid			dbid;
  	Dlelem	   *elem;
  
! 	dbid = do_start_worker();
  	if (OidIsValid(dbid))
  	{
  		/*
--- 1353,1359 ----
  	Oid			dbid;
  	Dlelem	   *elem;
  
! 	dbid = do_start_autovacuum_worker();
  	if (OidIsValid(dbid))
  	{
  		/*
*************** launch_worker(TimestampTz now)
*** 1304,1320 ****
  	}
  }
  
- /*
-  * Called from postmaster to signal a failure to fork a process to become
-  * worker.	The postmaster should kill(SIGUSR2) the launcher shortly
-  * after calling this function.
-  */
- void
- AutoVacWorkerFailed(void)
- {
- 	AutoVacuumShmem->av_signal[AutoVacForkFailed] = true;
- }
- 
  /* SIGHUP: set flag to re-read config file at next convenient time */
  static void
  avl_sighup_handler(SIGNAL_ARGS)
--- 1392,1397 ----
*************** avl_sighup_handler(SIGNAL_ARGS)
*** 1322,1328 ****
  	got_SIGHUP = true;
  }
  
! /* SIGUSR2: a worker is up and running, or just finished, or failed to fork */
  static void
  avl_sigusr2_handler(SIGNAL_ARGS)
  {
--- 1399,1405 ----
  	got_SIGHUP = true;
  }
  
! /* SIGUSR2: postmaster failed to fork a worker for us */
  static void
  avl_sigusr2_handler(SIGNAL_ARGS)
  {
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 1421,1427 ****
--- 1498,1508 ----
  AutoVacWorkerMain(int argc, char *argv[])
  {
  	sigjmp_buf	local_sigjmp_buf;
+ 	BackendId   coordinator_id;
+ 	IMessage   *msg;
  	Oid			dbid;
+ 	char		dbname[NAMEDATALEN];
+ 	bool		terminate_worker = false;
  
  	/* we are a postmaster subprocess now */
  	IsUnderPostmaster = true;
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 1537,1548 ****
  	 * launcher might have decided to remove it from the queue and start
  	 * again.
  	 */
! 	if (AutoVacuumShmem->av_startingWorker != NULL)
  	{
! 		MyWorkerInfo = AutoVacuumShmem->av_startingWorker;
! 		dbid = MyWorkerInfo->wi_dboid;
! 		MyWorkerInfo->wi_proc = MyProc;
  
  		/* insert into the running list */
  		SHMQueueInsertBefore(&AutoVacuumShmem->av_runningWorkers,
  							 &MyWorkerInfo->wi_links);
--- 1618,1637 ----
  	 * launcher might have decided to remove it from the queue and start
  	 * again.
  	 */
! 	if (AutoVacuumShmem->av_startingWorker == NULL)
  	{
! 		/* no worker entry for me, go away */
! 		elog(WARNING, "autovacuum worker started without a worker entry");
! 		LWLockRelease(AutovacuumLock);
! 		proc_exit(0);
! 	}
  
+ 	MyWorkerInfo = AutoVacuumShmem->av_startingWorker;
+ 	dbid = MyWorkerInfo->wi_dboid;
+ 
+ 	/* FIXME: indentation */
+ 	{
+ 
  		/* insert into the running list */
  		SHMQueueInsertBefore(&AutoVacuumShmem->av_runningWorkers,
  							 &MyWorkerInfo->wi_links);
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 1552,1576 ****
  		 * a new worker if required
  		 */
  		AutoVacuumShmem->av_startingWorker = NULL;
- 		LWLockRelease(AutovacuumLock);
  
! 		on_shmem_exit(FreeWorkerInfo, 0);
! 
! 		/* wake up the launcher */
! 		if (AutoVacuumShmem->av_launcherpid != 0)
! 			kill(AutoVacuumShmem->av_launcherpid, SIGUSR2);
! 	}
! 	else
! 	{
! 		/* no worker entry for me, go away */
! 		elog(WARNING, "autovacuum worker started without a worker entry");
! 		dbid = InvalidOid;
  		LWLockRelease(AutovacuumLock);
- 	}
  
! 	if (OidIsValid(dbid))
! 	{
! 		char		dbname[NAMEDATALEN];
  
  		/*
  		 * Report autovac startup to the stats collector.  We deliberately do
--- 1641,1651 ----
  		 * a new worker if required
  		 */
  		AutoVacuumShmem->av_startingWorker = NULL;
  
! 		coordinator_id = AutoVacuumShmem->av_launcherid;
  		LWLockRelease(AutovacuumLock);
  
! 		on_shmem_exit(FreeWorkerInfo, 0);
  
  		/*
  		 * Report autovac startup to the stats collector.  We deliberately do
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 1593,1613 ****
  		set_ps_display(dbname, false);
  		ereport(DEBUG1,
  				(errmsg("autovacuum: processing database \"%s\"", dbname)));
  
! 		if (PostAuthDelay)
! 			pg_usleep(PostAuthDelay * 1000000L);
  
! 		/* And do an appropriate amount of work */
! 		recentXid = ReadNewTransactionId();
! 		do_autovacuum();
  	}
  
! 	/*
! 	 * The launcher will be notified of my death in ProcKill, *if* we managed
! 	 * to get a worker slot at all
! 	 */
  
  	/* All done, go away */
  	proc_exit(0);
  }
  
--- 1668,1765 ----
  		set_ps_display(dbname, false);
  		ereport(DEBUG1,
  				(errmsg("autovacuum: processing database \"%s\"", dbname)));
+ 	}
  
! 	MyWorkerInfo->wi_backend_id = MyBackendId;
  
! 	/* register with the coordinator */
! 	if (coordinator_id != InvalidBackendId)
! 	{
! 		msg = IMessageCreate(IMSGT_REGISTER_WORKER, 0);
! 		IMessageActivate(msg, coordinator_id);
  	}
  
! 	if (PostAuthDelay)
! 		pg_usleep(PostAuthDelay * 1000000L);
  
+ 	while (!terminate_worker)
+ 	{
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		ImmediateInterruptOK = true;
+ 		pg_usleep(1000000L);
+ 		ImmediateInterruptOK = false;
+ 
+ 		while ((msg = IMessageCheck()) && !terminate_worker)
+ 		{
+ #ifdef COORDINATOR_DEBUG
+ 			ereport(DEBUG3,
+ 					(errmsg("bg worker [%d/%d]: received message %s of size %d "
+ 							"from backend id %d, db %d",
+ 							MyProcPid, MyBackendId,
+ 							decode_imessage_type(msg->type),
+ 							msg->size, msg->sender,
+ 							MyDatabaseId)));
+ #endif
+ 
+ 			/*
+ 			 * StartTransactionCommand and CommitTransactionCommand will
+ 			 * automatically switch to other contexts.  None the less we need
+ 			 * this one for other book-keeping of the various background
+ 			 * jobs across transactions, for example to keep the list of
+ 			 * relations to vacuum.
+ 			 */
+ 			AutovacMemCxt = AllocSetContextCreate(TopMemoryContext,
+ 												  "Background Worker",
+ 												  ALLOCSET_DEFAULT_MINSIZE,
+ 												  ALLOCSET_DEFAULT_INITSIZE,
+ 												  ALLOCSET_DEFAULT_MAXSIZE);
+ 			MemoryContextSwitchTo(AutovacMemCxt);
+ 
+ 			switch (msg->type)
+ 			{
+ 				case IMSGT_PERFORM_VACUUM:
+ 					/* immediately remove the message to free shared memory */
+ 					IMessageRemove(msg);
+ 
+ 					/* do an appropriate amount of work */
+ 					do_autovacuum();
+ 
+ 
+ 					/*
+ 					 * send an IMSGT_READY to inform the coordinator that we
+ 					 * finished vacuuming.
+ 					 */
+ 					msg = IMessageCreate(IMSGT_READY, 0);
+ 					IMessageActivate(msg, GetAutovacuumLauncherId());
+ 
+ 					/*
+ 					 * for now, we always terminate the worker after an 
+ 					 * autovacuum job.
+ 					 */
+ 					terminate_worker = true;
+ 					break;
+ 
+ 				default:
+ 					ereport(WARNING,
+ 							(errmsg("bg worker [%d]: invalid message type "
+ 									"'%c' ignored",
+ 									MyProcPid, msg->type)));
+ 
+ 					/* keep shared memory clean */
+ 					IMessageRemove(msg);
+ 			}
+ 
+ 			MemoryContextSwitchTo(TopMemoryContext);
+ 			MemoryContextDelete(AutovacMemCxt);
+ 
+ 			CHECK_FOR_INTERRUPTS();
+ 		}
+ 	}
+ 
  	/* All done, go away */
+ 	ereport(DEBUG1, (errmsg("bg worker [%d/%d]: terminating",
+ 							MyProcPid, MyBackendId)));
  	proc_exit(0);
  }
  
*************** FreeWorkerInfo(int code, Datum arg)
*** 1621,1645 ****
  	{
  		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  
- 		/*
- 		 * Wake the launcher up so that he can launch a new worker immediately
- 		 * if required.  We only save the launcher's PID in local memory here;
- 		 * the actual signal will be sent when the PGPROC is recycled.	Note
- 		 * that we always do this, so that the launcher can rebalance the cost
- 		 * limit setting of the remaining workers.
- 		 *
- 		 * We somewhat ignore the risk that the launcher changes its PID
- 		 * between we reading it and the actual kill; we expect ProcKill to be
- 		 * called shortly after us, and we assume that PIDs are not reused too
- 		 * quickly after a process exits.
- 		 */
- 		AutovacuumLauncherPid = AutoVacuumShmem->av_launcherpid;
- 
  		SHMQueueDelete(&MyWorkerInfo->wi_links);
  		MyWorkerInfo->wi_links.next = (SHM_QUEUE *) AutoVacuumShmem->av_freeWorkers;
  		MyWorkerInfo->wi_dboid = InvalidOid;
  		MyWorkerInfo->wi_tableoid = InvalidOid;
! 		MyWorkerInfo->wi_proc = NULL;
  		MyWorkerInfo->wi_launchtime = 0;
  		MyWorkerInfo->wi_cost_delay = 0;
  		MyWorkerInfo->wi_cost_limit = 0;
--- 1773,1783 ----
  	{
  		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  
  		SHMQueueDelete(&MyWorkerInfo->wi_links);
  		MyWorkerInfo->wi_links.next = (SHM_QUEUE *) AutoVacuumShmem->av_freeWorkers;
  		MyWorkerInfo->wi_dboid = InvalidOid;
  		MyWorkerInfo->wi_tableoid = InvalidOid;
! 		MyWorkerInfo->wi_backend_id = InvalidBackendId;
  		MyWorkerInfo->wi_launchtime = 0;
  		MyWorkerInfo->wi_cost_delay = 0;
  		MyWorkerInfo->wi_cost_limit = 0;
*************** FreeWorkerInfo(int code, Datum arg)
*** 1648,1658 ****
  		/* not mine anymore */
  		MyWorkerInfo = NULL;
  
- 		/*
- 		 * now that we're inactive, cause a rebalancing of the surviving
- 		 * workers
- 		 */
- 		AutoVacuumShmem->av_signal[AutoVacRebalance] = true;
  		LWLockRelease(AutovacuumLock);
  	}
  }
--- 1786,1791 ----
*************** autovac_balance_cost(void)
*** 1704,1710 ****
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
! 		if (worker->wi_proc != NULL &&
  			worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
  			cost_total +=
  				(double) worker->wi_cost_limit_base / worker->wi_cost_delay;
--- 1837,1843 ----
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
! 		if (worker->wi_backend_id != InvalidBackendId &&
  			worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
  			cost_total +=
  				(double) worker->wi_cost_limit_base / worker->wi_cost_delay;
*************** autovac_balance_cost(void)
*** 1727,1733 ****
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
! 		if (worker->wi_proc != NULL &&
  			worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
  		{
  			int			limit = (int)
--- 1860,1866 ----
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
! 		if (worker->wi_backend_id != InvalidBackendId &&
  			worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
  		{
  			int			limit = (int)
*************** autovac_balance_cost(void)
*** 1739,1746 ****
  			 */
  			worker->wi_cost_limit = Max(Min(limit, worker->wi_cost_limit_base), 1);
  
! 			elog(DEBUG2, "autovac_balance_cost(pid=%u db=%u, rel=%u, cost_limit=%d, cost_delay=%d)",
! 				 worker->wi_proc->pid, worker->wi_dboid,
  				 worker->wi_tableoid, worker->wi_cost_limit, worker->wi_cost_delay);
  		}
  
--- 1872,1879 ----
  			 */
  			worker->wi_cost_limit = Max(Min(limit, worker->wi_cost_limit_base), 1);
  
! 			elog(DEBUG2, "autovac_balance_cost(backend_id=%u db=%u, rel=%u, cost_limit=%d, cost_delay=%d)",
! 				 worker->wi_backend_id, worker->wi_dboid,
  				 worker->wi_tableoid, worker->wi_cost_limit, worker->wi_cost_delay);
  		}
  
*************** do_autovacuum(void)
*** 1830,1846 ****
  	ScanKeyData key;
  	TupleDesc	pg_class_desc;
  
! 	/*
! 	 * StartTransactionCommand and CommitTransactionCommand will automatically
! 	 * switch to other contexts.  We need this one to keep the list of
! 	 * relations to vacuum/analyze across transactions.
! 	 */
! 	AutovacMemCxt = AllocSetContextCreate(TopMemoryContext,
! 										  "AV worker",
! 										  ALLOCSET_DEFAULT_MINSIZE,
! 										  ALLOCSET_DEFAULT_INITSIZE,
! 										  ALLOCSET_DEFAULT_MAXSIZE);
! 	MemoryContextSwitchTo(AutovacMemCxt);
  
  	/*
  	 * may be NULL if we couldn't find an entry (only happens if we are
--- 1963,1969 ----
  	ScanKeyData key;
  	TupleDesc	pg_class_desc;
  
! 	recentXid = ReadNewTransactionId();
  
  	/*
  	 * may be NULL if we couldn't find an entry (only happens if we are
*************** autovac_report_activity(autovac_table *t
*** 2694,2712 ****
  }
  
  /*
-  * AutoVacuumingActive
-  *		Check GUC vars and report whether the autovacuum process should be
-  *		running.
-  */
- bool
- AutoVacuumingActive(void)
- {
- 	if (!autovacuum_start_daemon || !pgstat_track_counts)
- 		return false;
- 	return true;
- }
- 
- /*
   * autovac_init
   *		This is called at postmaster initialization.
   *
--- 2817,2822 ----
*************** autovac_init(void)
*** 2715,2723 ****
  void
  autovac_init(void)
  {
! 	if (autovacuum_start_daemon && !pgstat_track_counts)
  		ereport(WARNING,
! 				(errmsg("autovacuum not started because of misconfiguration"),
  				 errhint("Enable the \"track_counts\" option.")));
  }
  
--- 2825,2833 ----
  void
  autovac_init(void)
  {
! 	if (autovacuum_enabled && !pgstat_track_counts)
  		ereport(WARNING,
! 				(errmsg("autovacuum disabled because of misconfiguration"),
  				 errhint("Enable the \"track_counts\" option.")));
  }
  
*************** IsAutoVacuumWorkerProcess(void)
*** 2738,2744 ****
--- 2848,2869 ----
  	return am_autovacuum_worker;
  }
  
+ /*
+  * GetAutovacuumLauncherId
+  *     Returns the backendId of the currently active coordinator process.
+  */ 
+ BackendId
+ GetAutovacuumLauncherId(void)
+ {
+ 	BackendId AutovacuumLauncherId;
  
+ 	LWLockAcquire(AutovacuumLock, LW_SHARED);
+ 	AutovacuumLauncherId = AutoVacuumShmem->av_launcherid;
+ 	LWLockRelease(AutovacuumLock);
+    
+ 	return AutovacuumLauncherId;
+ }
+ 
  /*
   * AutoVacuumShmemSize
   *		Compute space needed for autovacuum-related shared memory
*************** AutoVacuumShmemInit(void)
*** 2779,2785 ****
  
  		Assert(!found);
  
! 		AutoVacuumShmem->av_launcherpid = 0;
  		AutoVacuumShmem->av_freeWorkers = NULL;
  		SHMQueueInit(&AutoVacuumShmem->av_runningWorkers);
  		AutoVacuumShmem->av_startingWorker = NULL;
--- 2904,2910 ----
  
  		Assert(!found);
  
! 		AutoVacuumShmem->av_launcherid = InvalidBackendId;
  		AutoVacuumShmem->av_freeWorkers = NULL;
  		SHMQueueInit(&AutoVacuumShmem->av_runningWorkers);
  		AutoVacuumShmem->av_startingWorker = NULL;
============================================================
*** src/include/postmaster/autovacuum.h	0bae2233087c757cad1e627118530e452ba166e7
--- src/include/postmaster/autovacuum.h	9d1ae53662236fcc871f84a9b1036619931bffe6
***************
*** 14,24 ****
  #ifndef AUTOVACUUM_H
  #define AUTOVACUUM_H
  
  #include "storage/lock.h"
  
  /* GUC variables */
! extern bool autovacuum_start_daemon;
  extern int	autovacuum_max_workers;
  extern int	autovacuum_naptime;
  extern int	autovacuum_vac_thresh;
  extern double autovacuum_vac_scale;
--- 14,27 ----
  #ifndef AUTOVACUUM_H
  #define AUTOVACUUM_H
  
+ #include "storage/imsg.h"
  #include "storage/lock.h"
  
  /* GUC variables */
! extern bool autovacuum_enabled;
  extern int	autovacuum_max_workers;
+ extern int min_spare_background_workers;
+ extern int max_spare_background_workers;
  extern int	autovacuum_naptime;
  extern int	autovacuum_vac_thresh;
  extern double autovacuum_vac_scale;
*************** extern int	autovacuum_vac_cost_limit;
*** 28,54 ****
  extern int	autovacuum_vac_cost_delay;
  extern int	autovacuum_vac_cost_limit;
  
- /* autovacuum launcher PID, only valid when worker is shutting down */
- extern int	AutovacuumLauncherPid;
- 
  extern int	Log_autovacuum_min_duration;
  
  /* Status inquiry functions */
- extern bool AutoVacuumingActive(void);
  extern bool IsAutoVacuumLauncherProcess(void);
  extern bool IsAutoVacuumWorkerProcess(void);
  
- #define IsAnyAutoVacuumProcess() \
- 	(IsAutoVacuumLauncherProcess() || IsAutoVacuumWorkerProcess())
- 
  /* Functions to start autovacuum process, called from postmaster */
  extern void autovac_init(void);
  extern int	StartAutoVacLauncher(void);
  extern int	StartAutoVacWorker(void);
  
- /* called from postmaster when a worker could not be forked */
- extern void AutoVacWorkerFailed(void);
- 
  /* autovacuum cost-delay balancer */
  extern void AutoVacuumUpdateDelay(void);
  
--- 31,48 ----
  extern int	autovacuum_vac_cost_delay;
  extern int	autovacuum_vac_cost_limit;
  
  extern int	Log_autovacuum_min_duration;
  
  /* Status inquiry functions */
  extern bool IsAutoVacuumLauncherProcess(void);
  extern bool IsAutoVacuumWorkerProcess(void);
+ extern BackendId GetAutovacuumLauncherId(void);
  
  /* Functions to start autovacuum process, called from postmaster */
  extern void autovac_init(void);
  extern int	StartAutoVacLauncher(void);
  extern int	StartAutoVacWorker(void);
  
  /* autovacuum cost-delay balancer */
  extern void AutoVacuumUpdateDelay(void);
  
============================================================
*** src/backend/storage/ipc/imsg.c	dc149eef487eafb43409a78b8a33c70e7d3c2bfa
--- src/backend/storage/ipc/imsg.c	81f58ac6067b9e4ebfaaf1e8ada1df68a20e8493
*************** decode_imessage_type(const IMessageType 
*** 79,84 ****
--- 79,95 ----
  	{
  		case IMSGT_TEST:
  			return "IMSGT_TEST";
+ 
+ 		case IMSGT_REGISTER_WORKER:
+ 			return "IMSGT_REGISTER_WORKER";
+ 		case IMSGT_READY:
+ 			return "IMSGT_READY";
+ 
+ 		case IMSGT_PERFORM_VACUUM:
+ 			return "IMSGT_PERFORM_VACUUM";
+ 		case IMSGT_FORCE_VACUUM:
+ 			return "IMSGT_FORCE_VACUUM";
+ 
  		default:
  			return "unknown message type";
  	}
============================================================
*** src/include/storage/imsg.h	d7d3960f42396bef49eb598411c215271570330e
--- src/include/storage/imsg.h	33f7d6772ba8d599a4eed0b882ccfa474a5cf258
*************** typedef enum
*** 28,33 ****
--- 28,47 ----
   */
  typedef enum
  {
+ 	/*
+ 	 * messages from worker to coordinator, for background worker
+ 	 * registration and job management.
+ 	 */
+ 	IMSGT_REGISTER_WORKER = 'W',
+ 	IMSGT_READY = 'r',
+ 
+ 	/* inform the coordinator about a database that needs vacuuming to
+ 	 * prevent transaction wrap around, from backends to coordinator */
+ 	IMSGT_FORCE_VACUUM = 'v',
+ 
+ 	/* messages from coordinator to worker */
+ 	IMSGT_PERFORM_VACUUM = 'V',
+ 
  	IMSGT_TEST = 'T'				/* test message type */
  } IMessageType;
  
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to