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