*** 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;
  
