diff -cpr HEAD/src/backend/commands/vacuum.c autovacuum/src/backend/commands/vacuum.c
*** HEAD/src/backend/commands/vacuum.c	Fri Mar 16 10:15:08 2007
--- autovacuum/src/backend/commands/vacuum.c	Thu Mar 29 13:39:22 2007
*************** vacuum(VacuumStmt *vacstmt, List *relids
*** 384,390 ****
  	{
  		ListCell   *cur;
  
! 		VacuumCostActive = (VacuumCostDelay > 0);
  		VacuumCostBalance = 0;
  
  		/*
--- 384,391 ----
  	{
  		ListCell   *cur;
  
! 		VacuumCostActive = (VacuumCostDelay > 0 ||
! 							IsAutoVacuumWorkerProcess());
  		VacuumCostBalance = 0;
  
  		/*
*************** vacuum_delay_point(void)
*** 3503,3508 ****
--- 3504,3510 ----
  		pg_usleep(msec * 1000L);
  
  		VacuumCostBalance = 0;
+ 		AutoVacUpdateDelay();
  
  		/* Might have gotten an interrupt while sleeping */
  		CHECK_FOR_INTERRUPTS();
diff -cpr HEAD/src/backend/postmaster/autovacuum.c autovacuum/src/backend/postmaster/autovacuum.c
*** HEAD/src/backend/postmaster/autovacuum.c	Mon Apr  9 19:34:55 2007
--- autovacuum/src/backend/postmaster/autovacuum.c	Mon Apr  9 09:36:25 2007
*************** typedef struct
*** 141,146 ****
--- 141,149 ----
  	Oid			wi_tableoid;
  	int			wi_workerpid;
  	bool		wi_finished;
+ 	int			wi_cost_delay;
+ 	int			wi_cost_limit;
+ 	int			wi_cost_limit_base;
  } WorkerInfo;
  
  typedef struct
*************** typedef struct
*** 151,160 ****
  } AutoVacuumShmemStruct;
  
  /* Macro to iterate over all workers.  Beware multiple evaluation of args! */
! #define foreach_worker(_i, _worker) \
! 	_worker = (WorkerInfo *) (AutoVacuumShmem + \
! 							  offsetof(AutoVacuumShmemStruct, av_workers)); \
! 	for (_i = 0; _i < autovacuum_max_workers; _i++, _worker += sizeof(WorkerInfo))
  
  static AutoVacuumShmemStruct *AutoVacuumShmem;
  
--- 154,163 ----
  } AutoVacuumShmemStruct;
  
  /* Macro to iterate over all workers.  Beware multiple evaluation of args! */
! #define foreach_worker(_worker) \
! 	for ((_worker) = AutoVacuumShmem->av_workers; \
! 		(_worker) < AutoVacuumShmem->av_workers + autovacuum_max_workers; \
! 		(_worker)++)
  
  static AutoVacuumShmemStruct *AutoVacuumShmem;
  
*************** static int free_workers;
*** 163,168 ****
--- 166,172 ----
  /* the database list in the launcher, and the context that contains it */
  static Dllist *DatabaseList = NULL;
  static MemoryContext DatabaseListCxt = NULL;
+ static WorkerInfo *MyAutoVacWorker;
  
  #ifdef EXEC_BACKEND
  static pid_t avlauncher_forkexec(void);
*************** static int db_comparator(const void *a, 
*** 180,185 ****
--- 184,190 ----
  
  static void do_autovacuum(WorkerInfo *worker);
  static HeapTuple get_pg_autovacuum_tuple_relid(Relation avRel, Oid relid);
+ static void autovac_balance_cost(void);
  
  static void relation_check_autovac(Oid relid, Form_pg_class classForm,
  					   Form_pg_autovacuum avForm, PgStat_StatTabEntry *tabentry,
*************** AutoVacLauncherMain(int argc, char *argv
*** 492,510 ****
  		{
  			got_SIGHUP = false;
  			ProcessConfigFile(PGC_SIGHUP);
  		}
  
  		/* a worker started up or finished */
  		if (got_SIGUSR1)
  		{
  			WorkerInfo *worker;
! 			int			i;
  
  			got_SIGUSR1 = false;
  
  			/* Walk the workers and clean up finished entries. */
! 			LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);	
! 			foreach_worker(i, worker)
  			{
  				if (worker->wi_finished)
  				{
--- 497,519 ----
  		{
  			got_SIGHUP = false;
  			ProcessConfigFile(PGC_SIGHUP);
+ 
+ 			LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
+ 			autovac_balance_cost();
+ 			LWLockRelease(AutovacuumLock);
  		}
  
  		/* a worker started up or finished */
  		if (got_SIGUSR1)
  		{
  			WorkerInfo *worker;
! 			bool		rebalance = false;
  
  			got_SIGUSR1 = false;
  
  			/* Walk the workers and clean up finished entries. */
! 			LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
! 			foreach_worker(worker)
  			{
  				if (worker->wi_finished)
  				{
*************** AutoVacLauncherMain(int argc, char *argv
*** 513,520 ****
--- 522,532 ----
  					worker->wi_workerpid = 0;
  					worker->wi_finished = false;
  					free_workers++;
+ 					rebalance = true;
  				}
  			}
+ 			if (rebalance)
+ 				autovac_balance_cost();
  			LWLockRelease(AutovacuumLock);
  		}
  
*************** do_start_worker(void)
*** 840,846 ****
  {
  	List	   *dblist;
  	WorkerInfo *worker;
- 	int			i;
  	ListCell   *cell;
  	TransactionId xidForceLimit;
  	bool		for_xid_wrap;
--- 852,857 ----
*************** do_start_worker(void)
*** 858,864 ****
  	 * use that pointer while we weren't looking.
  	 */
  	LWLockAcquire(AutovacuumLock, LW_SHARED);
! 	foreach_worker(i, worker)
  	{
  		/* Invalid database OID means unused worker entry; use it */
  		if (!OidIsValid(worker->wi_dboid))
--- 869,875 ----
  	 * use that pointer while we weren't looking.
  	 */
  	LWLockAcquire(AutovacuumLock, LW_SHARED);
! 	foreach_worker(worker)
  	{
  		/* Invalid database OID means unused worker entry; use it */
  		if (!OidIsValid(worker->wi_dboid))
*************** do_start_worker(void)
*** 867,873 ****
  	LWLockRelease(AutovacuumLock);
  
  	/* they're all used up */
! 	if (i >= autovacuum_max_workers)
  		return InvalidOid;
  
  	/* Get a list of databases */
--- 878,884 ----
  	LWLockRelease(AutovacuumLock);
  
  	/* they're all used up */
! 	if (worker >= AutoVacuumShmem->av_workers + autovacuum_max_workers)
  		return InvalidOid;
  
  	/* Get a list of databases */
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 1198,1204 ****
  	sigjmp_buf	local_sigjmp_buf;
  	Oid			dbid = InvalidOid;
  	WorkerInfo *worker;
- 	int			i;
  
  	/* we are a postmaster subprocess now */
  	IsUnderPostmaster = true;
--- 1209,1214 ----
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 1307,1313 ****
  	 * on it, thus marking it used.
  	 */
  	LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
! 	foreach_worker(i, worker)
  	{
  		if (worker->wi_workerpid == 0)
  		{
--- 1317,1323 ----
  	 * on it, thus marking it used.
  	 */
  	LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
! 	foreach_worker(worker)
  	{
  		if (worker->wi_workerpid == 0)
  		{
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 1355,1360 ****
--- 1365,1371 ----
  
  		/* And do an appropriate amount of work */
  		recentXid = ReadNewTransactionId();
+ 		MyAutoVacWorker = worker;
  		do_autovacuum(worker);
  	}
  
*************** do_autovacuum(WorkerInfo *worker)
*** 1600,1606 ****
  		autovac_table *tab;
  		char   *relname;
  		WorkerInfo *other_worker;
- 		int         i;
  		bool        skipit;
  
  		CHECK_FOR_INTERRUPTS();
--- 1611,1616 ----
*************** do_autovacuum(WorkerInfo *worker)
*** 1616,1622 ****
  		 * worker.
  		 */
  		skipit = false;
! 		foreach_worker(i, other_worker)
  		{
  			/*
  			 * ignore not-yet-registered or not running workers, and workers in
--- 1626,1632 ----
  		 * worker.
  		 */
  		skipit = false;
! 		foreach_worker(other_worker)
  		{
  			/*
  			 * ignore not-yet-registered or not running workers, and workers in
*************** do_autovacuum(WorkerInfo *worker)
*** 1660,1667 ****
  		LWLockRelease(AutovacuumScheduleLock);
  
  		/* Set the vacuum cost parameters for this table */
! 		VacuumCostDelay = tab->at_vacuum_cost_delay;
! 		VacuumCostLimit = tab->at_vacuum_cost_limit;
  
  		relname = get_rel_name(relid);
  		elog(DEBUG2, "autovac: will%s%s %s",
--- 1670,1678 ----
  		LWLockRelease(AutovacuumScheduleLock);
  
  		/* Set the vacuum cost parameters for this table */
! 		VacuumCostDelay = worker->wi_cost_delay = tab->at_vacuum_cost_delay;
! 		VacuumCostLimit = worker->wi_cost_limit_base =
! 			worker->wi_cost_limit = tab->at_vacuum_cost_limit;
  
  		relname = get_rel_name(relid);
  		elog(DEBUG2, "autovac: will%s%s %s",
*************** do_autovacuum(WorkerInfo *worker)
*** 1669,1674 ****
--- 1680,1689 ----
  			 (tab->at_doanalyze ? " ANALYZE" : ""),
  			 relname);
  
+ 		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
+ 		autovac_balance_cost();
+ 		LWLockRelease(AutovacuumLock);
+ 
  		autovacuum_do_vac_analyze(tab->at_relid,
  								  tab->at_dovacuum,
  								  tab->at_doanalyze,
*************** AutoVacuumShmemInit(void)
*** 2258,2260 ****
--- 2273,2338 ----
  
  	MemSet(AutoVacuumShmem, 0, AutoVacuumShmemSize());
  }
+ 
+ /*
+  * AutoVacUpdateDelay
+  */
+ void
+ AutoVacUpdateDelay(void)
+ {
+ 	if (MyAutoVacWorker)
+ 	{
+ 		VacuumCostDelay = MyAutoVacWorker->wi_cost_delay;
+ 		VacuumCostLimit = MyAutoVacWorker->wi_cost_limit;
+ 	}
+ }
+ 
+ /*
+  * autovac_balance_cost
+  *    recaculate the cost limit setting for each active workers.
+  */
+ static void
+ autovac_balance_cost(void)
+ {
+ 	WorkerInfo *worker;
+ 	int			vac_cost_limit = (autovacuum_vac_cost_limit >= 0 ?
+ 							autovacuum_vac_cost_limit : VacuumCostLimit);
+ 	int			vac_cost_delay = (autovacuum_vac_cost_delay >= 0 ?
+ 							autovacuum_vac_cost_delay : VacuumCostDelay);
+ 	double		cost_total;
+ 	double		cost_avail;
+ 
+ 	if (vac_cost_limit <= 0 || vac_cost_delay <= 0)
+ 		return;
+ 
+ 	/* caculate the total base cost limit of active workers.*/
+ 	cost_total = 0.0;
+ 	foreach_worker(worker)	
+ 	{
+ 		if (worker->wi_workerpid != 0 &&
+ 			worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
+ 			cost_total +=
+ 				(double) worker->wi_cost_limit_base / worker->wi_cost_delay;
+ 	}
+ 	if (cost_total <= 0)
+ 		return;
+ 		
+ 	/*
+ 	 * Adjust each cost limit of active workers to balance the total of
+ 	 * cost limit to autovacuum_vacuum_cost_limit.
+ 	 */
+ 	cost_avail = (double) vac_cost_limit / vac_cost_delay;
+ 	foreach_worker(worker)	
+ 	{
+ 		if (worker->wi_workerpid != 0 &&
+ 			worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
+ 		{
+ 			int		limit = (int)
+ 				(cost_avail * worker->wi_cost_limit_base / cost_total);
+ 			worker->wi_cost_limit = Min(limit, worker->wi_cost_limit_base);
+ 			elog(DEBUG1, "autovac_balance_cost(pid=%u db=%u, rel=%u, cost_limit=%d, cost_delay=%d)",
+ 				worker->wi_workerpid, worker->wi_dboid,
+ 				worker->wi_tableoid, worker->wi_cost_limit, worker->wi_cost_delay);
+ 		}
+ 	}
+ }
diff -cpr HEAD/src/backend/utils/misc/postgresql.conf.sample autovacuum/src/backend/utils/misc/postgresql.conf.sample
*** HEAD/src/backend/utils/misc/postgresql.conf.sample	Thu Mar 22 09:42:41 2007
--- autovacuum/src/backend/utils/misc/postgresql.conf.sample	Thu Apr  5 18:13:32 2007
***************
*** 376,381 ****
--- 376,382 ----
  #autovacuum = on			# enable autovacuum subprocess?
  					# 'on' requires stats_start_collector
  					# and stats_row_level to also be on
+ #autovacuum_max_workers = 10	# max # of autovacuum subprocess
  #autovacuum_naptime = 1min		# time between autovacuum runs
  #autovacuum_vacuum_threshold = 500	# min # of tuple updates before
  					# vacuum
diff -cpr HEAD/src/include/postmaster/autovacuum.h autovacuum/src/include/postmaster/autovacuum.h
*** HEAD/src/include/postmaster/autovacuum.h	Mon Apr  9 19:34:56 2007
--- autovacuum/src/include/postmaster/autovacuum.h	Thu Apr  5 18:13:32 2007
*************** extern int	autovacuum_vac_cost_limit;
*** 30,35 ****
--- 30,36 ----
  extern bool AutoVacuumingActive(void);
  extern bool IsAutoVacuumLauncherProcess(void);
  extern bool IsAutoVacuumWorkerProcess(void);
+ extern void AutoVacUpdateDelay(void);
  
  /* Functions to start autovacuum process, called from postmaster */
  extern void autovac_init(void);
