Index: src/backend/access/transam/varsup.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/varsup.c,v retrieving revision 1.77 diff -c -p -r1.77 varsup.c *** src/backend/access/transam/varsup.c 5 Jan 2007 22:19:23 -0000 1.77 --- src/backend/access/transam/varsup.c 26 Jan 2007 23:13:35 -0000 *************** GetNewTransactionId(bool isSubXact) *** 72,78 **** * still gives plenty of chances before we get into real trouble. */ if (IsUnderPostmaster && (xid % 65536) == 0) ! SendPostmasterSignal(PMSIGNAL_START_AUTOVAC); if (IsUnderPostmaster && TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidStopLimit)) --- 72,78 ---- * 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, ShmemVariableCache->xidStopLimit)) *************** SetTransactionIdLimit(TransactionId olde *** 286,292 **** */ if (TransactionIdFollowsOrEquals(curXid, xidVacLimit) && IsUnderPostmaster) ! SendPostmasterSignal(PMSIGNAL_START_AUTOVAC); /* Give an immediate warning if past the wrap warn point */ if (TransactionIdFollowsOrEquals(curXid, xidWarnLimit)) --- 286,292 ---- */ if (TransactionIdFollowsOrEquals(curXid, xidVacLimit) && IsUnderPostmaster) ! SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER); /* Give an immediate warning if past the wrap warn point */ if (TransactionIdFollowsOrEquals(curXid, xidWarnLimit)) Index: src/backend/commands/vacuum.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/vacuum.c,v retrieving revision 1.343 diff -c -p -r1.343 vacuum.c *** src/backend/commands/vacuum.c 5 Jan 2007 22:19:26 -0000 1.343 --- src/backend/commands/vacuum.c 26 Jan 2007 17:23:00 -0000 *************** vacuum(VacuumStmt *vacstmt, List *relids *** 303,309 **** * Send info about dead objects to the statistics collector, unless we are * in autovacuum --- autovacuum.c does this for itself. */ ! if (vacstmt->vacuum && !IsAutoVacuumProcess()) pgstat_vacuum_tabstat(); /* --- 303,309 ---- * Send info about dead objects to the statistics collector, unless we are * in autovacuum --- autovacuum.c does this for itself. */ ! if (vacstmt->vacuum && !IsAutoVacuumWorkerProcess()) pgstat_vacuum_tabstat(); /* *************** vacuum(VacuumStmt *vacstmt, List *relids *** 472,478 **** ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); } ! if (vacstmt->vacuum && !IsAutoVacuumProcess()) { /* * Update pg_database.datfrozenxid, and truncate pg_clog if possible. --- 472,478 ---- ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); } ! if (vacstmt->vacuum && !IsAutoVacuumWorkerProcess()) { /* * Update pg_database.datfrozenxid, and truncate pg_clog if possible. Index: src/backend/postmaster/autovacuum.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/postmaster/autovacuum.c,v retrieving revision 1.31 diff -c -p -r1.31 autovacuum.c *** src/backend/postmaster/autovacuum.c 16 Jan 2007 13:28:56 -0000 1.31 --- src/backend/postmaster/autovacuum.c 26 Jan 2007 23:07:29 -0000 *************** *** 39,45 **** --- 39,47 ---- #include "postmaster/postmaster.h" #include "storage/fd.h" #include "storage/ipc.h" + #include "storage/pmsignal.h" #include "storage/proc.h" + #include "storage/procarray.h" #include "storage/sinval.h" #include "tcop/tcopprot.h" #include "utils/flatfiles.h" *************** *** 50,55 **** --- 52,59 ---- #include "utils/syscache.h" + static bool avlauncher_exit_request = false; + /* * GUC parameters */ *************** int autovacuum_vac_cost_delay; *** 65,71 **** int autovacuum_vac_cost_limit; /* Flag to tell if we are in the autovacuum daemon process */ ! static bool am_autovacuum = false; /* Last time autovac daemon started/stopped (only valid in postmaster) */ static time_t last_autovac_start_time = 0; --- 69,76 ---- int autovacuum_vac_cost_limit; /* Flag to tell if we are in the autovacuum daemon process */ ! static bool am_autovacuum_launcher = false; ! static bool am_autovacuum_worker = false; /* Last time autovac daemon started/stopped (only valid in postmaster) */ static time_t last_autovac_start_time = 0; *************** static int default_freeze_min_age; *** 80,85 **** --- 85,93 ---- /* Memory context for long-lived data */ static MemoryContext AutovacMemCxt; + /* Memory context for stat data */ + static MemoryContext AutoVacStatContext; + /* struct to keep list of candidate databases for vacuum */ typedef struct autovac_dbase { *************** typedef struct autovac_table *** 101,113 **** int vacuum_cost_limit; } autovac_table; #ifdef EXEC_BACKEND ! static pid_t autovac_forkexec(void); #endif ! NON_EXEC_STATIC void AutoVacMain(int argc, char *argv[]); static void do_autovacuum(PgStat_StatDBEntry *dbentry); static List *autovac_get_database_list(void); static void test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry, Form_pg_class classForm, Form_pg_autovacuum avForm, --- 109,133 ---- int vacuum_cost_limit; } autovac_table; + typedef struct + { + Oid process_db; /* OID of database to process */ + int worker_pid; /* PID of the worker process, if any */ + } AutoVacuumShmemStruct; + + static AutoVacuumShmemStruct *AutoVacuumShmem; + #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 void do_autovacuum(PgStat_StatDBEntry *dbentry); static List *autovac_get_database_list(void); + static char *autovac_get_database_name(Oid dbid); static void test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry, Form_pg_class classForm, Form_pg_autovacuum avForm, *************** static void test_rel_for_autovac(Oid rel *** 116,130 **** static void autovacuum_do_vac_analyze(Oid relid, bool dovacuum, bool doanalyze, int freeze_min_age); static void autovac_report_activity(VacuumStmt *vacstmt, Oid relid); /* ! * Main entry point for autovacuum controller process. * ! * This code is heavily based on pgarch.c, q.v. */ int ! autovac_start(void) { time_t curtime; pid_t AutoVacPID; --- 136,188 ---- static void autovacuum_do_vac_analyze(Oid relid, bool dovacuum, bool doanalyze, int freeze_min_age); static void autovac_report_activity(VacuumStmt *vacstmt, Oid relid); + static void avlauncher_exit(SIGNAL_ARGS); + static void avl_quickdie(SIGNAL_ARGS); + + + /******************************************************************** + * AUTOVACUUM LAUNCHER CODE + ********************************************************************/ + #ifdef EXEC_BACKEND /* ! * forkexec routine for the autovacuum launcher process. * ! * Format up the arglist, then fork and exec. ! */ ! static pid_t ! avlauncher_forkexec(void) ! { ! char *av[10]; ! int ac = 0; ! ! av[ac++] = "postgres"; ! av[ac++] = "--forkavlauncher"; ! av[ac++] = NULL; /* filled in by postmaster_forkexec */ ! av[ac] = NULL; ! ! Assert(ac < lengthof(av)); ! ! return postmaster_forkexec(ac, av); ! } ! ! /* ! * We need this set from the outside, before InitProcess is called ! */ ! void ! AutovacuumLauncherIAm(void) ! { ! am_autovacuum_launcher = true; ! } ! #endif ! ! /* ! * Main entry point for autovacuum launcher process, to be called from the ! * postmaster. */ int ! StartAutoVacLauncher(void) { time_t curtime; pid_t AutoVacPID; *************** autovac_start(void) *** 142,147 **** --- 200,209 ---- * will get another chance later if we do nothing now. * * XXX todo: implement sleep scale factor that existed in contrib code. + * + * FIXME -- figure out how much of this is still relevant. It's leftover + * from before we had a separation between launcher and worker, probably + * most of it is obsolete. */ curtime = time(NULL); *************** autovac_start(void) *** 156,162 **** last_autovac_start_time = curtime; #ifdef EXEC_BACKEND ! switch ((AutoVacPID = autovac_forkexec())) #else switch ((AutoVacPID = fork_process())) #endif --- 218,224 ---- last_autovac_start_time = curtime; #ifdef EXEC_BACKEND ! switch ((AutoVacPID = avlauncher_forkexec())) #else switch ((AutoVacPID = fork_process())) #endif *************** autovac_start(void) *** 175,181 **** /* Lose the postmaster's on-exit routines */ on_exit_reset(); ! AutoVacMain(0, NULL); break; #endif default: --- 237,243 ---- /* Lose the postmaster's on-exit routines */ on_exit_reset(); ! AutoVacLauncherMain(0, NULL); break; #endif default: *************** autovac_start(void) *** 187,214 **** } /* ! * autovac_stopped --- called by postmaster when subprocess exit is detected */ ! void ! autovac_stopped(void) { ! last_autovac_stop_time = time(NULL); } #ifdef EXEC_BACKEND /* ! * autovac_forkexec() * ! * Format up the arglist for the autovacuum process, then fork and exec. */ static pid_t ! autovac_forkexec(void) { char *av[10]; int ac = 0; av[ac++] = "postgres"; ! av[ac++] = "--forkautovac"; av[ac++] = NULL; /* filled in by postmaster_forkexec */ av[ac] = NULL; --- 249,596 ---- } /* ! * Main loop for the autovacuum launcher process. */ ! NON_EXEC_STATIC void ! AutoVacLauncherMain(int argc, char *argv[]) ! { ! sigjmp_buf local_sigjmp_buf; ! List *dblist; ! bool for_xid_wrap; ! autovac_dbase *db; ! MemoryContext avlauncher_cxt; ! ! /* we are a postmaster subprocess now */ ! IsUnderPostmaster = true; ! am_autovacuum_launcher = true; ! ! /* reset MyProcPid */ ! MyProcPid = getpid(); ! ! /* Identify myself via ps */ ! init_ps_display("autovacuum launcher process", "", "", ""); ! ! SetProcessingMode(InitProcessing); ! ! /* ! * If possible, make this process a group leader, so that the postmaster ! * can signal any child processes too. (autovacuum probably never has ! * any child processes, but for consistency we make all postmaster ! * child processes do this.) ! */ ! #ifdef HAVE_SETSID ! if (setsid() < 0) ! elog(FATAL, "setsid() failed: %m"); ! #endif ! ! /* ! * Set up signal handlers. Since this is a "dummy" process, it has ! * particular signal requirements -- no deadlock checker or sinval ! * catchup, for example. ! * ! * Currently, we don't pay attention to postgresql.conf changes that ! * happen during a single daemon iteration, so we can ignore SIGHUP. ! * ! * FIXME -- this was correct for the old autovacuum, but the avlauncher ! * should be paying attention. Additionally, it may be a good idea to ! * receive signals when an avworker process finishes. ! */ ! pqsignal(SIGHUP, SIG_IGN); ! ! pqsignal(SIGINT, SIG_IGN); ! pqsignal(SIGTERM, avlauncher_exit); ! pqsignal(SIGQUIT, avl_quickdie); ! pqsignal(SIGALRM, SIG_IGN); ! ! pqsignal(SIGPIPE, SIG_IGN); ! pqsignal(SIGUSR1, SIG_IGN); ! /* We don't listen for async notifies */ ! pqsignal(SIGUSR2, SIG_IGN); ! pqsignal(SIGFPE, FloatExceptionHandler); ! pqsignal(SIGCHLD, SIG_DFL); ! ! /* Early initialization */ ! BaseInit(); ! ! /* ! * Create a per-backend PGPROC struct in shared memory, except in the ! * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do ! * this before we can use LWLocks (and in the EXEC_BACKEND case we already ! * had to do some stuff with LWLocks). ! */ ! #ifndef EXEC_BACKEND ! InitDummyProcess(); ! #endif ! ! /* ! * Create a memory context that we will do all our work in. We do this so ! * that we can reset the context during error recovery and thereby avoid ! * possible memory leaks. ! */ ! avlauncher_cxt = AllocSetContextCreate(TopMemoryContext, ! "Autovacuum Launcher", ! ALLOCSET_DEFAULT_MINSIZE, ! ALLOCSET_DEFAULT_INITSIZE, ! ALLOCSET_DEFAULT_MAXSIZE); ! MemoryContextSwitchTo(avlauncher_cxt); ! ! ! /* ! * If an exception is encountered, processing resumes here. ! * ! * This code is heavily based on bgwriter.c, q.v. ! */ ! if (sigsetjmp(local_sigjmp_buf, 1) != 0) ! { ! /* since not using PG_TRY, must reset error stack by hand */ ! error_context_stack = NULL; ! ! /* Prevents interrupts while cleaning up */ ! HOLD_INTERRUPTS(); ! ! /* Report the error to the server log */ ! EmitErrorReport(); ! ! /* ! * These operations are really just a minimal subset of ! * AbortTransaction(). We don't have very many resources to worry ! * about, but we do have LWLocks. ! */ ! LWLockReleaseAll(); ! AtEOXact_Files(); ! ! /* ! * Now return to normal top-level context and clear ErrorContext for ! * next time. ! */ ! MemoryContextSwitchTo(avlauncher_cxt); ! FlushErrorState(); ! ! /* Flush any leaked data in the top-level context */ ! MemoryContextResetAndDeleteChildren(avlauncher_cxt); ! /* Make sure pgstat also considers our stat data as gone */ ! pgstat_stathash_reset(); ! ! /* Now we can allow interrupts again */ ! RESUME_INTERRUPTS(); ! ! /* ! * Sleep at least 1 second after any error. We don't want to be ! * filling the error logs as fast as we can. ! */ ! pg_usleep(1000000L); ! } ! ! /* We can now handle ereport(ERROR) */ ! PG_exception_stack = &local_sigjmp_buf; ! ! ereport(LOG, ! (errmsg("autovacuum launcher started"))); ! ! PG_SETMASK(&UnBlockSig); ! ! AutoVacStatContext = AllocSetContextCreate(avlauncher_cxt, ! "AVLauncher Stat Hash", ! ALLOCSET_DEFAULT_MINSIZE, ! ALLOCSET_DEFAULT_INITSIZE, ! ALLOCSET_DEFAULT_MAXSIZE); ! ! for (;;) ! { ! TransactionId xidForceLimit; ! ListCell *cell; ! int worker_pid; ! ! /* ! * Emergency bailout if postmaster has died. This is to avoid the ! * necessity for manual cleanup of all postmaster children. ! */ ! if (!PostmasterIsAlive(true)) ! exit(1); ! ! if (avlauncher_exit_request) ! { ! ereport(LOG, ! (errmsg("autovacuum launcher shutting down"))); ! ! /* Normal exit from the autovac launcher is here */ ! proc_exit(0); /* done */ ! } ! ! /* ! * if there's a worker already running, sleep until it ! * disappears. ! */ ! LWLockAcquire(AutovacuumLock, LW_SHARED); ! worker_pid = AutoVacuumShmem->worker_pid; ! LWLockRelease(AutovacuumLock); ! ! if (worker_pid != 0) ! { ! PGPROC *proc = BackendPidGetProc(worker_pid); ! ! if (proc != NULL && proc->isAutovacuum) ! goto sleep; ! else ! { ! /* ! * if the worker is not really running (or it's a process ! * that's not an autovacuum worker), remove the PID from shmem. ! * This should not happen, because either the worker exits ! * cleanly, in which case it'll remove the PID, or it dies, in ! * which case postmaster will cause a system reset cycle. ! */ ! LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE); ! worker_pid = 0; ! LWLockRelease(AutovacuumLock); ! } ! } ! ! /* Get a list of databases */ ! dblist = autovac_get_database_list(); ! ! /* ! * Determine the oldest datfrozenxid/relfrozenxid that we will allow ! * to pass without forcing a vacuum. (This limit can be tightened for ! * particular tables, but not loosened.) ! */ ! recentXid = ReadNewTransactionId(); ! xidForceLimit = recentXid - autovacuum_freeze_max_age; ! /* ensure it's a "normal" XID, else TransactionIdPrecedes misbehaves */ ! if (xidForceLimit < FirstNormalTransactionId) ! xidForceLimit -= FirstNormalTransactionId; ! ! /* ! * Choose a database to connect to. We pick the database that was least ! * recently auto-vacuumed, or one that needs vacuuming to prevent Xid ! * wraparound-related data loss. If any db at risk of wraparound is ! * found, we pick the one with oldest datfrozenxid, independently of ! * autovacuum times. ! * ! * Note that a database with no stats entry is not considered, except for ! * Xid wraparound purposes. The theory is that if no one has ever ! * connected to it since the stats were last initialized, it doesn't need ! * vacuuming. ! * ! * XXX This could be improved if we had more info about whether it needs ! * vacuuming before connecting to it. Perhaps look through the pgstats ! * data for the database's tables? One idea is to keep track of the ! * number of new and dead tuples per database in pgstats. However it ! * isn't clear how to construct a metric that measures that and not cause ! * starvation for less busy databases. ! */ ! db = NULL; ! for_xid_wrap = false; ! foreach(cell, dblist) ! { ! autovac_dbase *tmp = lfirst(cell); ! ! /* Find pgstat entry if any */ ! tmp->entry = pgstat_fetch_stat_dbentry(tmp->oid); ! ! /* Check to see if this one is at risk of wraparound */ ! if (TransactionIdPrecedes(tmp->frozenxid, xidForceLimit)) ! { ! if (db == NULL || ! TransactionIdPrecedes(tmp->frozenxid, db->frozenxid)) ! db = tmp; ! for_xid_wrap = true; ! continue; ! } ! else if (for_xid_wrap) ! continue; /* ignore not-at-risk DBs */ ! ! /* ! * Otherwise, skip a database with no pgstat entry; it means it ! * hasn't seen any activity. ! */ ! if (!tmp->entry) ! continue; ! ! /* ! * Remember the db with oldest autovac time. (If we are here, ! * both tmp->entry and db->entry must be non-null.) ! */ ! if (db == NULL || ! tmp->entry->last_autovac_time < db->entry->last_autovac_time) ! db = tmp; ! } ! ! /* Found a database -- process it */ ! if (db != NULL) ! { ! LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE); ! AutoVacuumShmem->process_db = db->oid; ! LWLockRelease(AutovacuumLock); ! ! SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER); ! } ! ! sleep: ! /* have pgstat read the file again next time */ ! MemoryContextReset(AutoVacStatContext); ! pgstat_stathash_reset(); ! ! /* now sleep until the next autovac iteration */ ! pg_usleep(autovacuum_naptime * 1000000L); ! } ! } ! ! MemoryContext ! AutoVacGetStatsContext(void) ! { ! return AutoVacStatContext; ! } ! ! static void ! avlauncher_exit(SIGNAL_ARGS) ! { ! avlauncher_exit_request = true; ! } ! ! /* ! * avl_quickdie occurs when signalled SIGQUIT from postmaster. ! * ! * Some backend has bought the farm, so we need to stop what we're doing ! * and exit. ! */ ! static void ! avl_quickdie(SIGNAL_ARGS) { ! PG_SETMASK(&BlockSig); ! ! /* ! * DO NOT proc_exit() -- we're here because shared memory may be ! * corrupted, so we don't want to try to clean up our transaction. Just ! * nail the windows shut and get out of town. ! * ! * Note we do exit(2) not exit(0). This is to force the postmaster into a ! * system reset cycle if some idiot DBA sends a manual SIGQUIT to a random ! * backend. This is necessary precisely because we don't clean up our ! * shared memory state. ! */ ! exit(2); } + + /******************************************************************** + * AUTOVACUUM WORKER CODE + ********************************************************************/ + #ifdef EXEC_BACKEND /* ! * forkexec routines for the autovacuum worker. * ! * Format up the arglist, then fork and exec. */ static pid_t ! avworker_forkexec(void) { char *av[10]; int ac = 0; av[ac++] = "postgres"; ! av[ac++] = "--forkavworker"; av[ac++] = NULL; /* filled in by postmaster_forkexec */ av[ac] = NULL; *************** autovac_forkexec(void) *** 221,254 **** * We need this set from the outside, before InitProcess is called */ void ! AutovacuumIAm(void) { ! am_autovacuum = true; } - #endif /* EXEC_BACKEND */ /* ! * AutoVacMain */ NON_EXEC_STATIC void ! AutoVacMain(int argc, char *argv[]) { - ListCell *cell; - List *dblist; - autovac_dbase *db; - TransactionId xidForceLimit; - bool for_xid_wrap; sigjmp_buf local_sigjmp_buf; /* we are a postmaster subprocess now */ IsUnderPostmaster = true; ! am_autovacuum = true; /* reset MyProcPid */ MyProcPid = getpid(); /* Identify myself via ps */ ! init_ps_display("autovacuum process", "", "", ""); SetProcessingMode(InitProcessing); --- 603,673 ---- * We need this set from the outside, before InitProcess is called */ void ! AutovacuumWorkerIAm(void) ! { ! am_autovacuum_worker = true; ! } ! #endif ! ! /* ! * Main entry point for autovacuum worker process. ! * ! * This code is heavily based on pgarch.c, q.v. ! */ ! int ! StartAutoVacWorker(void) { ! pid_t worker_pid; ! ! #ifdef EXEC_BACKEND ! switch ((worker_pid = avworker_forkexec())) ! #else ! switch ((worker_pid = fork_process())) ! #endif ! { ! case -1: ! ereport(LOG, ! (errmsg("could not fork autovacuum process: %m"))); ! return 0; ! ! #ifndef EXEC_BACKEND ! case 0: ! /* in postmaster child ... */ ! /* Close the postmaster's sockets */ ! ClosePostmasterPorts(false); ! ! /* Lose the postmaster's on-exit routines */ ! on_exit_reset(); ! ! AutoVacWorkerMain(0, NULL); ! break; ! #endif ! default: ! return (int) worker_pid; ! } ! ! /* shouldn't get here */ ! return 0; } /* ! * AutoVacWorkerMain */ NON_EXEC_STATIC void ! AutoVacWorkerMain(int argc, char *argv[]) { sigjmp_buf local_sigjmp_buf; + Oid dbid; /* we are a postmaster subprocess now */ IsUnderPostmaster = true; ! am_autovacuum_worker = true; /* reset MyProcPid */ MyProcPid = getpid(); /* Identify myself via ps */ ! init_ps_display("autovacuum worker process", "", "", ""); SetProcessingMode(InitProcessing); *************** AutoVacMain(int argc, char *argv[]) *** 335,412 **** */ SetConfigOption("zero_damaged_pages", "false", PGC_SUSET, PGC_S_OVERRIDE); - /* Get a list of databases */ - dblist = autovac_get_database_list(); - /* ! * Determine the oldest datfrozenxid/relfrozenxid that we will allow ! * to pass without forcing a vacuum. (This limit can be tightened for ! * particular tables, but not loosened.) */ ! recentXid = ReadNewTransactionId(); ! xidForceLimit = recentXid - autovacuum_freeze_max_age; ! /* ensure it's a "normal" XID, else TransactionIdPrecedes misbehaves */ ! if (xidForceLimit < FirstNormalTransactionId) ! xidForceLimit -= FirstNormalTransactionId; ! /* ! * Choose a database to connect to. We pick the database that was least ! * recently auto-vacuumed, or one that needs vacuuming to prevent Xid ! * wraparound-related data loss. If any db at risk of wraparound is ! * found, we pick the one with oldest datfrozenxid, ! * independently of autovacuum times. ! * ! * Note that a database with no stats entry is not considered, except for ! * Xid wraparound purposes. The theory is that if no one has ever ! * connected to it since the stats were last initialized, it doesn't need ! * vacuuming. ! * ! * XXX This could be improved if we had more info about whether it needs ! * vacuuming before connecting to it. Perhaps look through the pgstats ! * data for the database's tables? One idea is to keep track of the ! * number of new and dead tuples per database in pgstats. However it ! * isn't clear how to construct a metric that measures that and not cause ! * starvation for less busy databases. ! */ ! db = NULL; ! for_xid_wrap = false; ! foreach(cell, dblist) ! { ! autovac_dbase *tmp = lfirst(cell); ! /* Find pgstat entry if any */ ! tmp->entry = pgstat_fetch_stat_dbentry(tmp->oid); ! /* Check to see if this one is at risk of wraparound */ ! if (TransactionIdPrecedes(tmp->frozenxid, xidForceLimit)) ! { ! if (db == NULL || ! TransactionIdPrecedes(tmp->frozenxid, db->frozenxid)) ! db = tmp; ! for_xid_wrap = true; ! continue; ! } ! else if (for_xid_wrap) ! continue; /* ignore not-at-risk DBs */ ! ! /* ! * Otherwise, skip a database with no pgstat entry; it means it ! * hasn't seen any activity. ! */ ! if (!tmp->entry) ! continue; ! ! /* ! * Remember the db with oldest autovac time. (If we are here, ! * both tmp->entry and db->entry must be non-null.) ! */ ! if (db == NULL || ! tmp->entry->last_autovac_time < db->entry->last_autovac_time) ! db = tmp; ! } ! ! if (db) { /* * Report autovac startup to the stats collector. We deliberately do * this before InitPostgres, so that the last_autovac_time will get --- 754,777 ---- */ SetConfigOption("zero_damaged_pages", "false", PGC_SUSET, PGC_S_OVERRIDE); /* ! * Get the database Id we're going to work on, and announce our PID ! * in the shared memory area. We remove the database OID immediately ! * from the shared memory area. */ ! LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE); ! dbid = AutoVacuumShmem->process_db; ! AutoVacuumShmem->process_db = InvalidOid; ! AutoVacuumShmem->worker_pid = MyProcPid; ! LWLockRelease(AutovacuumLock); ! if (OidIsValid(dbid)) { + char *dbname; + PgStat_StatDBEntry *dbentry; + /* * Report autovac startup to the stats collector. We deliberately do * this before InitPostgres, so that the last_autovac_time will get *************** AutoVacMain(int argc, char *argv[]) *** 415,421 **** * database, rather than making any progress on stuff it can connect * to. */ ! pgstat_report_autovac(db->oid); /* * Connect to the selected database --- 780,793 ---- * database, rather than making any progress on stuff it can connect * to. */ ! pgstat_report_autovac(dbid); ! ! /* ! * FIXME -- maybe it's better to change InitPostgres to be able to ! * receive an OID, so that we don't have to read the pg_database ! * flatfile again? ! */ ! dbname = autovac_get_database_name(dbid); /* * Connect to the selected database *************** AutoVacMain(int argc, char *argv[]) *** 423,433 **** * Note: if we have selected a just-deleted database (due to using * stale stats info), we'll fail and exit here. */ ! InitPostgres(db->name, NULL); SetProcessingMode(NormalProcessing); ! set_ps_display(db->name, false); ereport(DEBUG1, ! (errmsg("autovacuum: processing database \"%s\"", db->name))); /* Create the memory context where cross-transaction state is stored */ AutovacMemCxt = AllocSetContextCreate(TopMemoryContext, --- 795,805 ---- * Note: if we have selected a just-deleted database (due to using * stale stats info), we'll fail and exit here. */ ! InitPostgres(dbname, NULL); SetProcessingMode(NormalProcessing); ! set_ps_display(dbname, false); ereport(DEBUG1, ! (errmsg("autovacuum: processing database \"%s\"", dbname))); /* Create the memory context where cross-transaction state is stored */ AutovacMemCxt = AllocSetContextCreate(TopMemoryContext, *************** AutoVacMain(int argc, char *argv[]) *** 439,448 **** /* * And do an appropriate amount of work */ ! do_autovacuum(db->entry); } ! /* One iteration done, go away */ proc_exit(0); } --- 811,829 ---- /* * And do an appropriate amount of work */ ! dbentry = pgstat_fetch_stat_dbentry(dbid); ! do_autovacuum(dbentry); } ! /* ! * Now remove our PID from shared memory, so that the launcher can start ! * another worker as soon as appropriate. ! */ ! LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE); ! AutoVacuumShmem->worker_pid = 0; ! LWLockRelease(AutovacuumLock); ! ! /* All done, go away */ proc_exit(0); } *************** AutoVacMain(int argc, char *argv[]) *** 450,456 **** * autovac_get_database_list * * Return a list of all databases. Note we cannot use pg_database, ! * because we aren't connected yet; we use the flat database file. */ static List * autovac_get_database_list(void) --- 831,837 ---- * autovac_get_database_list * * Return a list of all databases. Note we cannot use pg_database, ! * because we aren't connected; we use the flat database file. */ static List * autovac_get_database_list(void) *************** autovac_get_database_list(void) *** 493,498 **** --- 874,920 ---- } /* + * autovac_get_database_name + * + * Given a database OID, get its name from the flat database file. + */ + static char * + autovac_get_database_name(Oid dbid) + { + char *filename; + char *dbname = NULL; + Oid this_oid; + Oid dummy_oid; + FILE *db_file; + char thisname[NAMEDATALEN]; + + filename = database_getflatfilename(); + db_file = AllocateFile(filename, "r"); + if (db_file == NULL) + ereport(FATAL, + (errcode_for_file_access(), + errmsg("could not open file \"%s\": %m", filename))); + + while (read_pg_database_line(db_file, thisname, &this_oid, + &dummy_oid, &dummy_oid)) + { + if (this_oid == dbid) + { + dbname = pstrdup(thisname); + break; + } + } + + if (dbname == NULL) + elog(FATAL, "could not find name for database %u", dbid); + + FreeFile(db_file); + pfree(filename); + + return dbname; + } + + /* * Process a database table-by-table * * dbentry is either a pointer to the database entry in the stats databases *************** autovac_init(void) *** 1011,1021 **** } /* ! * IsAutoVacuumProcess ! * Return whether this process is an autovacuum process. */ bool ! IsAutoVacuumProcess(void) { ! return am_autovacuum; } --- 1433,1484 ---- } /* ! * IsAutoVacuum functions ! * Return whether this is either a launcher autovacuum process or a worker ! * process. */ bool ! IsAutoVacuumLauncherProcess(void) ! { ! return am_autovacuum_launcher; ! } ! ! bool ! IsAutoVacuumWorkerProcess(void) ! { ! return am_autovacuum_worker; ! } ! ! ! /* ! * AutoVacuumShmemSize ! * Compute space needed for autovacuum-related shared memory ! */ ! Size ! AutoVacuumShmemSize(void) { ! return sizeof(AutoVacuumShmemStruct); ! } ! ! /* ! * AutoVacuumShmemInit ! * Allocate and initialize autovacuum-related shared memory ! */ ! void ! AutoVacuumShmemInit(void) ! { ! bool found; ! ! AutoVacuumShmem = (AutoVacuumShmemStruct *) ! ShmemInitStruct("AutoVacuum Data", ! AutoVacuumShmemSize(), ! &found); ! if (AutoVacuumShmem == NULL) ! ereport(FATAL, ! (errcode(ERRCODE_OUT_OF_MEMORY), ! errmsg("not enough shared memory for autovacuum"))); ! if (found) ! return; /* already initialized */ ! ! MemSet(AutoVacuumShmem, 0, sizeof(AutoVacuumShmemStruct)); } Index: src/backend/postmaster/pgstat.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/postmaster/pgstat.c,v retrieving revision 1.143 diff -c -p -r1.143 pgstat.c *** src/backend/postmaster/pgstat.c 11 Jan 2007 23:06:03 -0000 1.143 --- src/backend/postmaster/pgstat.c 26 Jan 2007 17:23:00 -0000 *************** static time_t last_pgstat_start_time; *** 110,115 **** --- 110,116 ---- static bool pgStatRunningInCollector = false; + /* * Place where backends store per-table info to be sent to the collector. * We store shared relations separately from non-shared ones, to be able to *************** static TabStatArray SharedTabStat = {0, *** 130,135 **** --- 131,137 ---- static int pgStatXactCommit = 0; static int pgStatXactRollback = 0; + static bool stathash_set = false; static TransactionId pgStatDBHashXact = InvalidTransactionId; static HTAB *pgStatDBHash = NULL; static TransactionId pgStatLocalStatusXact = InvalidTransactionId; *************** pgstat_report_vacuum(Oid tableoid, bool *** 930,936 **** msg.m_databaseid = shared ? InvalidOid : MyDatabaseId; msg.m_tableoid = tableoid; msg.m_analyze = analyze; ! msg.m_autovacuum = IsAutoVacuumProcess(); /* is this autovacuum? */ msg.m_vacuumtime = GetCurrentTimestamp(); msg.m_tuples = tuples; pgstat_send(&msg, sizeof(msg)); --- 932,938 ---- msg.m_databaseid = shared ? InvalidOid : MyDatabaseId; msg.m_tableoid = tableoid; msg.m_analyze = analyze; ! msg.m_autovacuum = IsAutoVacuumWorkerProcess(); /* is this autovacuum? */ msg.m_vacuumtime = GetCurrentTimestamp(); msg.m_tuples = tuples; pgstat_send(&msg, sizeof(msg)); *************** pgstat_report_analyze(Oid tableoid, bool *** 955,961 **** pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE); msg.m_databaseid = shared ? InvalidOid : MyDatabaseId; msg.m_tableoid = tableoid; ! msg.m_autovacuum = IsAutoVacuumProcess(); /* is this autovacuum? */ msg.m_analyzetime = GetCurrentTimestamp(); msg.m_live_tuples = livetuples; msg.m_dead_tuples = deadtuples; --- 957,963 ---- pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE); msg.m_databaseid = shared ? InvalidOid : MyDatabaseId; msg.m_tableoid = tableoid; ! msg.m_autovacuum = IsAutoVacuumWorkerProcess(); /* is this autovacuum? */ msg.m_analyzetime = GetCurrentTimestamp(); msg.m_live_tuples = livetuples; msg.m_dead_tuples = deadtuples; *************** pgstat_read_statsfile(HTAB **dbhash, Oid *** 2098,2110 **** int mcxt_flags; /* ! * If running in the collector or the autovacuum process, we use the ! * DynaHashCxt memory context. If running in a backend, we use the ! * TopTransactionContext instead, so the caller must only know the last ! * XactId when this call happened to know if his tables are still valid or ! * already gone! */ ! if (pgStatRunningInCollector || IsAutoVacuumProcess()) { use_mcxt = NULL; mcxt_flags = 0; --- 2100,2118 ---- int mcxt_flags; /* ! * If running in the collector or an autovacuum worker process, we use the ! * DynaHashCxt memory context. If running in the autovacuum launcher, we ! * use a context that process will provide us. If running in a backend, we ! * use the TopTransactionContext instead, so the caller must only know the ! * last XactId when this call happened to know if his tables are still ! * valid or already gone! */ ! if (IsAutoVacuumLauncherProcess()) ! { ! use_mcxt = AutoVacGetStatsContext(); ! mcxt_flags = HASH_CONTEXT; ! } ! else if (pgStatRunningInCollector || IsAutoVacuumWorkerProcess()) { use_mcxt = NULL; mcxt_flags = 0; *************** done: *** 2269,2289 **** } /* * If not done for this transaction, read the statistics collector * stats file into some hash tables. * * Because we store the tables in TopTransactionContext, the result * is good for the entire current main transaction. * ! * Inside the autovacuum process, the statfile is assumed to be valid * "forever", that is one iteration, within one database. This means * we only consider the statistics as they were when the autovacuum * iteration started. */ static void backend_read_statsfile(void) { ! if (IsAutoVacuumProcess()) { /* already read it? */ if (pgStatDBHash) --- 2277,2319 ---- } /* + * Called when the memory context that holds the stathash has been reset, + * i.e., we have to read the stats file again. + */ + void + pgstat_stathash_reset(void) + { + stathash_set = false; + } + + /* * If not done for this transaction, read the statistics collector * stats file into some hash tables. * * Because we store the tables in TopTransactionContext, the result * is good for the entire current main transaction. * ! * Inside an autovacuum worker process, the statfile is assumed to be valid * "forever", that is one iteration, within one database. This means * we only consider the statistics as they were when the autovacuum * iteration started. + * + * Inside the autovacuum launcher process, we store the tables in a + * memory context that's reset after each check iteration. It'll tell us + * when the context has been reset by calling pgstat_stathash_reset. */ static void backend_read_statsfile(void) { ! if (IsAutoVacuumLauncherProcess()) ! { ! if (stathash_set) ! return; ! Assert(!pgStatRunningInCollector); ! pgstat_read_statsfile(&pgStatDBHash, InvalidOid); ! stathash_set = true; ! } ! else if (IsAutoVacuumWorkerProcess()) { /* already read it? */ if (pgStatDBHash) Index: src/backend/postmaster/postmaster.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/postmaster/postmaster.c,v retrieving revision 1.512 diff -c -p -r1.512 postmaster.c *** src/backend/postmaster/postmaster.c 23 Jan 2007 03:28:49 -0000 1.512 --- src/backend/postmaster/postmaster.c 26 Jan 2007 23:13:43 -0000 *************** *** 129,141 **** * authorization phase). This is used mainly to keep track of how many * children we have and send them appropriate signals when necessary. * ! * "Special" children such as the startup and bgwriter tasks are not in ! * this list. */ typedef struct bkend { pid_t pid; /* process id of backend */ long cancel_key; /* cancel key for cancels for this backend */ } Backend; static Dllist *BackendList; --- 129,143 ---- * authorization phase). This is used mainly to keep track of how many * children we have and send them appropriate signals when necessary. * ! * "Special" children such as the startup, bgwriter and autovacuum launcher ! * tasks are not in this list. Autovacuum worker processes, however, *are* on ! * it. */ typedef struct bkend { pid_t pid; /* process id of backend */ long cancel_key; /* cancel key for cancels for this backend */ + bool is_autovacuum; /* is it an autovacuum worker? */ } Backend; static Dllist *BackendList; *************** bool ClientAuthInProgress = false; /* *** 218,223 **** --- 220,226 ---- * authentication */ static bool force_autovac = false; /* received START_AUTOVAC signal */ + static bool start_autovac_worker = false; /* * State for assigning random salts and cancel keys. *************** ServerLoop(void) *** 1145,1157 **** /* * Wait for something to happen. * ! * We wait at most one minute, or the minimum autovacuum delay, to ! * ensure that the other background tasks handled below get done even ! * when no requests are arriving. */ memcpy((char *) &rmask, (char *) &readmask, sizeof(fd_set)); ! timeout.tv_sec = Min(60, autovacuum_naptime); timeout.tv_usec = 0; PG_SETMASK(&UnBlockSig); --- 1148,1159 ---- /* * Wait for something to happen. * ! * We wait at most one minute, to ensure that the other background ! * tasks handled below get done even when no requests are arriving. */ memcpy((char *) &rmask, (char *) &readmask, sizeof(fd_set)); ! timeout.tv_sec = 60; timeout.tv_usec = 0; PG_SETMASK(&UnBlockSig); *************** ServerLoop(void) *** 1238,1256 **** signal_child(BgWriterPID, SIGUSR2); } ! /* ! * Start a new autovacuum process, if there isn't one running already. ! * (It'll die relatively quickly.) We check that it's not started too ! * frequently in autovac_start. ! */ if ((AutoVacuumingActive() || force_autovac) && AutoVacPID == 0 && StartupPID == 0 && !FatalError && Shutdown == NoShutdown) { ! AutoVacPID = autovac_start(); if (AutoVacPID != 0) force_autovac = false; /* signal successfully processed */ } /* If we have lost the archiver, try to start a new one */ if (XLogArchivingActive() && PgArchPID == 0 && StartupPID == 0 && !FatalError && Shutdown == NoShutdown) --- 1240,1279 ---- signal_child(BgWriterPID, SIGUSR2); } ! /* If we have lost the autovacuum launcher, try to start a new one */ if ((AutoVacuumingActive() || force_autovac) && AutoVacPID == 0 && StartupPID == 0 && !FatalError && Shutdown == NoShutdown) { ! AutoVacPID = StartAutoVacLauncher(); if (AutoVacPID != 0) force_autovac = false; /* signal successfully processed */ } + if (start_autovac_worker && !FatalError && Shutdown == NoShutdown) + { + Backend *bn; + + start_autovac_worker = false; + bn = (Backend *) malloc(sizeof(Backend)); + if (!bn) + { + ereport(LOG, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + } + else + { + bn->pid = StartAutoVacWorker(); + if (bn->pid > 0) + { + DLAddHead(BackendList, DLNewElem(bn)); + #ifdef EXEC_BACKEND + ShmemBackendArrayAdd(bn); + #endif + } + } + } + /* If we have lost the archiver, try to start a new one */ if (XLogArchivingActive() && PgArchPID == 0 && StartupPID == 0 && !FatalError && Shutdown == NoShutdown) *************** pmdie(SIGNAL_ARGS) *** 1873,1888 **** ereport(LOG, (errmsg("received smart shutdown request"))); - /* - * We won't wait out an autovacuum iteration ... - */ - if (AutoVacPID != 0) - { - /* Use statement cancel to shut it down */ - signal_child(AutoVacPID, SIGINT); - break; /* let reaper() handle this */ - } - if (DLGetHead(BackendList)) break; /* let reaper() handle this */ --- 1896,1901 ---- *************** pmdie(SIGNAL_ARGS) *** 1903,1908 **** --- 1916,1924 ---- /* Tell pgstat to shut down too; nothing left for it to do */ if (PgStatPID != 0) signal_child(PgStatPID, SIGQUIT); + /* Tell autovac launcher to shut down too */ + if (AutoVacPID != 0) + signal_child(AutoVacPID, SIGTERM); break; case SIGINT: *************** pmdie(SIGNAL_ARGS) *** 1919,1933 **** ereport(LOG, (errmsg("received fast shutdown request"))); ! if (DLGetHead(BackendList) || AutoVacPID != 0) { if (!FatalError) { ereport(LOG, (errmsg("aborting any active transactions"))); SignalChildren(SIGTERM); - if (AutoVacPID != 0) - signal_child(AutoVacPID, SIGTERM); /* reaper() does the rest */ } break; --- 1935,1947 ---- ereport(LOG, (errmsg("received fast shutdown request"))); ! if (DLGetHead(BackendList)) { if (!FatalError) { ereport(LOG, (errmsg("aborting any active transactions"))); SignalChildren(SIGTERM); /* reaper() does the rest */ } break; *************** pmdie(SIGNAL_ARGS) *** 1958,1963 **** --- 1972,1980 ---- /* Tell pgstat to shut down too; nothing left for it to do */ if (PgStatPID != 0) signal_child(PgStatPID, SIGQUIT); + /* Tell autovac launcher to shut down too */ + if (AutoVacPID != 0) + signal_child(AutoVacPID, SIGTERM); break; case SIGQUIT: *************** reaper(SIGNAL_ARGS) *** 2072,2079 **** /* * Go to shutdown mode if a shutdown request was pending. ! * Otherwise, try to start the archiver and stats collector too. ! * (We could, but don't, try to start autovacuum here.) */ if (Shutdown > NoShutdown && BgWriterPID != 0) signal_child(BgWriterPID, SIGUSR2); --- 2089,2096 ---- /* * Go to shutdown mode if a shutdown request was pending. ! * Otherwise, try to start the archiver, stats collector and ! * autovacuum launcher. */ if (Shutdown > NoShutdown && BgWriterPID != 0) signal_child(BgWriterPID, SIGUSR2); *************** reaper(SIGNAL_ARGS) *** 2083,2088 **** --- 2100,2107 ---- PgArchPID = pgarch_start(); if (PgStatPID == 0) PgStatPID = pgstat_start(); + if (AutoVacuumingActive() && AutoVacPID == 0) + AutoVacPID = StartAutoVacLauncher(); } continue; *************** reaper(SIGNAL_ARGS) *** 2137,2154 **** } /* ! * Was it the autovacuum process? Normal or FATAL exit can be ! * ignored; we'll start a new one at the next iteration of the ! * postmaster's main loop, if necessary. Any other exit condition ! * is treated as a crash. */ if (AutoVacPID != 0 && pid == AutoVacPID) { AutoVacPID = 0; ! autovac_stopped(); ! if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus)) HandleChildCrash(pid, exitstatus, ! _("autovacuum process")); continue; } --- 2156,2171 ---- } /* ! * Was it the autovacuum launcher? Normal exit can be ignored; we'll ! * start a new one at the next iteration of the postmaster's main loop, ! * if necessary. Any other exit condition is treated as a crash. */ if (AutoVacPID != 0 && pid == AutoVacPID) { AutoVacPID = 0; ! if (!EXIT_STATUS_0(exitstatus)) HandleChildCrash(pid, exitstatus, ! _("autovacuum launcher process")); continue; } *************** reaper(SIGNAL_ARGS) *** 2226,2232 **** if (Shutdown > NoShutdown) { ! if (DLGetHead(BackendList) || StartupPID != 0 || AutoVacPID != 0) goto reaper_done; /* Start the bgwriter if not running */ if (BgWriterPID == 0) --- 2243,2249 ---- if (Shutdown > NoShutdown) { ! if (DLGetHead(BackendList) || StartupPID != 0) goto reaper_done; /* Start the bgwriter if not running */ if (BgWriterPID == 0) *************** reaper(SIGNAL_ARGS) *** 2240,2245 **** --- 2257,2265 ---- /* Tell pgstat to shut down too; nothing left for it to do */ if (PgStatPID != 0) signal_child(PgStatPID, SIGQUIT); + /* Tell autovac launcher to shut down too */ + if (AutoVacPID != 0) + signal_child(AutoVacPID, SIGTERM); } reaper_done: *************** HandleChildCrash(int pid, int exitstatus *** 2367,2373 **** signal_child(BgWriterPID, (SendStop ? SIGSTOP : SIGQUIT)); } ! /* Take care of the autovacuum daemon too */ if (pid == AutoVacPID) AutoVacPID = 0; else if (AutoVacPID != 0 && !FatalError) --- 2387,2393 ---- signal_child(BgWriterPID, (SendStop ? SIGSTOP : SIGQUIT)); } ! /* Take care of the autovacuum launcher too */ if (pid == AutoVacPID) AutoVacPID = 0; else if (AutoVacPID != 0 && !FatalError) *************** signal_child(pid_t pid, int signal) *** 2487,2493 **** } /* ! * Send a signal to all backend children (but NOT special children) */ static void SignalChildren(int signal) --- 2507,2514 ---- } /* ! * Send a signal to all backend children, including autovacuum workers (but NOT ! * special children) */ static void SignalChildren(int signal) *************** SubPostmasterMain(int argc, char *argv[] *** 3305,3317 **** * same address the postmaster used. */ if (strcmp(argv[1], "--forkbackend") == 0 || ! strcmp(argv[1], "--forkautovac") == 0 || strcmp(argv[1], "--forkboot") == 0) PGSharedMemoryReAttach(); /* autovacuum needs this set before calling InitProcess */ ! if (strcmp(argv[1], "--forkautovac") == 0) ! AutovacuumIAm(); /* * Start our win32 signal implementation. This has to be done after we --- 3326,3341 ---- * same address the postmaster used. */ if (strcmp(argv[1], "--forkbackend") == 0 || ! strcmp(argv[1], "--forkavlauncher") == 0 || ! strcmp(argv[1], "--forkavworker") == 0 || strcmp(argv[1], "--forkboot") == 0) PGSharedMemoryReAttach(); /* autovacuum needs this set before calling InitProcess */ ! if (strcmp(argv[1], "--forkavlauncher") == 0) ! AutovacuumLauncherIAm(); ! if (strcmp(argv[1], "--forkavworker") == 0) ! AutovacuumWorkerIAm(); /* * Start our win32 signal implementation. This has to be done after we *************** SubPostmasterMain(int argc, char *argv[] *** 3397,3403 **** BootstrapMain(argc - 2, argv + 2); proc_exit(0); } ! if (strcmp(argv[1], "--forkautovac") == 0) { /* Close the postmaster's sockets */ ClosePostmasterPorts(false); --- 3421,3444 ---- BootstrapMain(argc - 2, argv + 2); proc_exit(0); } ! if (strcmp(argv[1], "--forkavlauncher") == 0) ! { ! /* Close the postmaster's sockets */ ! ClosePostmasterPorts(false); ! ! /* Restore basic shared memory pointers */ ! InitShmemAccess(UsedShmemSegAddr); ! ! /* Need a PGPROC to run CreateSharedMemoryAndSemaphores */ ! InitDummyProcess(); ! ! /* Attach process to shared data structures */ ! CreateSharedMemoryAndSemaphores(false, 0); ! ! AutoVacLauncherMain(argc - 2, argv + 2); ! proc_exit(0); ! } ! if (strcmp(argv[1], "--forkavworker") == 0) { /* Close the postmaster's sockets */ ClosePostmasterPorts(false); *************** SubPostmasterMain(int argc, char *argv[] *** 3411,3417 **** /* Attach process to shared data structures */ CreateSharedMemoryAndSemaphores(false, 0); ! AutoVacMain(argc - 2, argv + 2); proc_exit(0); } if (strcmp(argv[1], "--forkarch") == 0) --- 3452,3458 ---- /* Attach process to shared data structures */ CreateSharedMemoryAndSemaphores(false, 0); ! AutoVacWorkerMain(argc - 2, argv + 2); proc_exit(0); } if (strcmp(argv[1], "--forkarch") == 0) *************** sigusr1_handler(SIGNAL_ARGS) *** 3495,3505 **** * See storage/ipc/sinval[adt].c for the use of this. */ if (Shutdown <= SmartShutdown) - { SignalChildren(SIGUSR1); - if (AutoVacPID != 0) - signal_child(AutoVacPID, SIGUSR1); - } } if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER) && --- 3536,3542 ---- *************** sigusr1_handler(SIGNAL_ARGS) *** 3519,3525 **** signal_child(SysLoggerPID, SIGUSR1); } ! if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC)) { /* * Start one iteration of the autovacuum daemon, even if autovacuuming --- 3556,3562 ---- signal_child(SysLoggerPID, SIGUSR1); } ! if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER)) { /* * Start one iteration of the autovacuum daemon, even if autovacuuming *************** sigusr1_handler(SIGNAL_ARGS) *** 3533,3538 **** --- 3570,3584 ---- force_autovac = true; } + if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER)) + { + /* + * The autovacuum launcher wants us to start an autovacuum worker + * process. Let the main loop do it. + */ + start_autovac_worker = true; + } + PG_SETMASK(&UnBlockSig); errno = save_errno; Index: src/backend/storage/ipc/ipci.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/storage/ipc/ipci.c,v retrieving revision 1.90 diff -c -p -r1.90 ipci.c *** src/backend/storage/ipc/ipci.c 5 Jan 2007 22:19:37 -0000 1.90 --- src/backend/storage/ipc/ipci.c 26 Jan 2007 17:23:00 -0000 *************** *** 21,26 **** --- 21,27 ---- #include "access/twophase.h" #include "miscadmin.h" #include "pgstat.h" + #include "postmaster/autovacuum.h" #include "postmaster/bgwriter.h" #include "postmaster/postmaster.h" #include "storage/freespace.h" *************** CreateSharedMemoryAndSemaphores(bool mak *** 109,114 **** --- 110,116 ---- size = add_size(size, SInvalShmemSize()); size = add_size(size, FreeSpaceShmemSize()); size = add_size(size, BgWriterShmemSize()); + size = add_size(size, AutoVacuumShmemSize()); size = add_size(size, BTreeShmemSize()); #ifdef EXEC_BACKEND size = add_size(size, ShmemBackendArraySize()); *************** CreateSharedMemoryAndSemaphores(bool mak *** 208,213 **** --- 210,216 ---- */ PMSignalInit(); BgWriterShmemInit(); + AutoVacuumShmemInit(); /* * Set up other modules that need some shared memory space Index: src/backend/storage/lmgr/proc.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/storage/lmgr/proc.c,v retrieving revision 1.183 diff -c -p -r1.183 proc.c *** src/backend/storage/lmgr/proc.c 16 Jan 2007 13:28:56 -0000 1.183 --- src/backend/storage/lmgr/proc.c 26 Jan 2007 23:08:05 -0000 *************** InitProcess(void) *** 259,265 **** MyProc->databaseId = InvalidOid; MyProc->roleId = InvalidOid; MyProc->inVacuum = false; ! MyProc->isAutovacuum = IsAutoVacuumProcess(); MyProc->lwWaiting = false; MyProc->lwExclusive = false; MyProc->lwWaitLink = NULL; --- 259,265 ---- MyProc->databaseId = InvalidOid; MyProc->roleId = InvalidOid; MyProc->inVacuum = false; ! MyProc->isAutovacuum = IsAutoVacuumWorkerProcess(); MyProc->lwWaiting = false; MyProc->lwExclusive = false; MyProc->lwWaitLink = NULL; *************** InitDummyProcess(void) *** 392,398 **** MyProc->databaseId = InvalidOid; MyProc->roleId = InvalidOid; MyProc->inVacuum = false; ! MyProc->isAutovacuum = false; MyProc->lwWaiting = false; MyProc->lwExclusive = false; MyProc->lwWaitLink = NULL; --- 392,398 ---- MyProc->databaseId = InvalidOid; MyProc->roleId = InvalidOid; MyProc->inVacuum = false; ! MyProc->isAutovacuum = IsAutoVacuumLauncherProcess(); /* is this needed? */ MyProc->lwWaiting = false; MyProc->lwExclusive = false; MyProc->lwWaitLink = NULL; Index: src/backend/utils/init/miscinit.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/init/miscinit.c,v retrieving revision 1.160 diff -c -p -r1.160 miscinit.c *** src/backend/utils/init/miscinit.c 5 Jan 2007 22:19:44 -0000 1.160 --- src/backend/utils/init/miscinit.c 26 Jan 2007 23:08:32 -0000 *************** InitializeSessionUserId(const char *role *** 401,407 **** * * We do not enforce them for the autovacuum process either. */ ! if (IsUnderPostmaster && !IsAutoVacuumProcess()) { /* * Is role allowed to login at all? --- 401,407 ---- * * We do not enforce them for the autovacuum process either. */ ! if (IsUnderPostmaster && !IsAutoVacuumWorkerProcess()) { /* * Is role allowed to login at all? *************** void *** 462,468 **** InitializeSessionUserIdStandalone(void) { /* This function should only be called in a single-user backend. */ ! AssertState(!IsUnderPostmaster || IsAutoVacuumProcess()); /* call only once */ AssertState(!OidIsValid(AuthenticatedUserId)); --- 462,468 ---- InitializeSessionUserIdStandalone(void) { /* This function should only be called in a single-user backend. */ ! AssertState(!IsUnderPostmaster || IsAutoVacuumWorkerProcess()); /* call only once */ AssertState(!OidIsValid(AuthenticatedUserId)); Index: src/backend/utils/init/postinit.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/init/postinit.c,v retrieving revision 1.173 diff -c -p -r1.173 postinit.c *** src/backend/utils/init/postinit.c 5 Jan 2007 22:19:44 -0000 1.173 --- src/backend/utils/init/postinit.c 26 Jan 2007 23:05:23 -0000 *************** CheckMyDatabase(const char *name, bool a *** 135,143 **** * a way to recover from disabling all access to all databases, for * example "UPDATE pg_database SET datallowconn = false;". * ! * We do not enforce them for the autovacuum process either. */ ! if (IsUnderPostmaster && !IsAutoVacuumProcess()) { /* * Check that the database is currently allowing connections. --- 135,143 ---- * a way to recover from disabling all access to all databases, for * example "UPDATE pg_database SET datallowconn = false;". * ! * We do not enforce them for the autovacuum worker processes either. */ ! if (IsUnderPostmaster && !IsAutoVacuumWorkerProcess()) { /* * Check that the database is currently allowing connections. *************** bool *** 288,294 **** InitPostgres(const char *dbname, const char *username) { bool bootstrap = IsBootstrapProcessingMode(); ! bool autovacuum = IsAutoVacuumProcess(); bool am_superuser; char *fullpath; --- 288,294 ---- InitPostgres(const char *dbname, const char *username) { bool bootstrap = IsBootstrapProcessingMode(); ! bool autovacuum = IsAutoVacuumWorkerProcess(); bool am_superuser; char *fullpath; Index: src/include/c.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/c.h,v retrieving revision 1.217 diff -c -p -r1.217 c.h *** src/include/c.h 25 Jan 2007 03:30:43 -0000 1.217 --- src/include/c.h 26 Jan 2007 16:39:14 -0000 *************** extern int fdatasync(int fildes); *** 817,822 **** --- 817,823 ---- #define HAVE_STRTOULL 1 #endif + #define EXEC_BACKEND /* EXEC_BACKEND defines */ #ifdef EXEC_BACKEND #define NON_EXEC_STATIC Index: src/include/pgstat.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/pgstat.h,v retrieving revision 1.52 diff -c -p -r1.52 pgstat.h *** src/include/pgstat.h 5 Jan 2007 22:19:50 -0000 1.52 --- src/include/pgstat.h 26 Jan 2007 16:39:14 -0000 *************** extern PgStat_StatDBEntry *pgstat_fetch_ *** 481,485 **** --- 481,486 ---- extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry(Oid relid); extern PgBackendStatus *pgstat_fetch_stat_beentry(int beid); extern int pgstat_fetch_stat_numbackends(void); + extern void pgstat_stathash_reset(void); #endif /* PGSTAT_H */ Index: src/include/postmaster/autovacuum.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/postmaster/autovacuum.h,v retrieving revision 1.7 diff -c -p -r1.7 autovacuum.h *** src/include/postmaster/autovacuum.h 16 Jan 2007 13:28:57 -0000 1.7 --- src/include/postmaster/autovacuum.h 26 Jan 2007 23:07:09 -0000 *************** extern int autovacuum_vac_cost_limit; *** 27,42 **** /* Status inquiry functions */ extern bool AutoVacuumingActive(void); ! extern bool IsAutoVacuumProcess(void); /* Functions to start autovacuum process, called from postmaster */ extern void autovac_init(void); ! extern int autovac_start(void); ! extern void autovac_stopped(void); #ifdef EXEC_BACKEND ! extern void AutoVacMain(int argc, char *argv[]); ! extern void AutovacuumIAm(void); #endif #endif /* AUTOVACUUM_H */ --- 27,51 ---- /* Status inquiry functions */ extern bool AutoVacuumingActive(void); ! extern bool IsAutoVacuumLauncherProcess(void); ! extern bool IsAutoVacuumWorkerProcess(void); /* Functions to start autovacuum process, called from postmaster */ extern void autovac_init(void); ! extern int StartAutoVacLauncher(void); ! extern int StartAutoVacWorker(void); #ifdef EXEC_BACKEND ! extern void AutoVacLauncherMain(int argc, char *argv[]); ! extern void AutoVacWorkerMain(int argc, char *argv[]); ! extern void AutovacuumWorkerIAm(void); ! extern void AutovacuumLauncherIAm(void); #endif + /* shared memory stuff */ + extern Size AutoVacuumShmemSize(void); + extern void AutoVacuumShmemInit(void); + + extern MemoryContext AutoVacGetStatsContext(void); + #endif /* AUTOVACUUM_H */ Index: src/include/storage/lwlock.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/storage/lwlock.h,v retrieving revision 1.33 diff -c -p -r1.33 lwlock.h *** src/include/storage/lwlock.h 5 Jan 2007 22:19:58 -0000 1.33 --- src/include/storage/lwlock.h 26 Jan 2007 16:39:14 -0000 *************** typedef enum LWLockId *** 62,67 **** --- 62,68 ---- BtreeVacuumLock, AddinShmemInitLock, FirstBufMappingLock, + AutovacuumLock, FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, /* must be last except for MaxDynamicLWLock: */ Index: src/include/storage/pmsignal.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/storage/pmsignal.h,v retrieving revision 1.16 diff -c -p -r1.16 pmsignal.h *** src/include/storage/pmsignal.h 5 Jan 2007 22:19:58 -0000 1.16 --- src/include/storage/pmsignal.h 26 Jan 2007 23:10:59 -0000 *************** typedef enum *** 26,32 **** PMSIGNAL_WAKEN_CHILDREN, /* send a SIGUSR1 signal to all backends */ PMSIGNAL_WAKEN_ARCHIVER, /* send a NOTIFY signal to xlog archiver */ PMSIGNAL_ROTATE_LOGFILE, /* send SIGUSR1 to syslogger to rotate logfile */ ! PMSIGNAL_START_AUTOVAC, /* start an autovacuum iteration */ NUM_PMSIGNALS /* Must be last value of enum! */ } PMSignalReason; --- 26,33 ---- PMSIGNAL_WAKEN_CHILDREN, /* send a SIGUSR1 signal to all backends */ 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 */ NUM_PMSIGNALS /* Must be last value of enum! */ } PMSignalReason; Index: src/include/storage/proc.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/storage/proc.h,v retrieving revision 1.93 diff -c -p -r1.93 proc.h *** src/include/storage/proc.h 16 Jan 2007 13:28:57 -0000 1.93 --- src/include/storage/proc.h 26 Jan 2007 16:39:14 -0000 *************** typedef struct PROC_HDR *** 121,127 **** * We set aside some extra PGPROC structures for "dummy" processes, * ie things that aren't full-fledged backends but need shmem access. */ ! #define NUM_DUMMY_PROCS 2 /* configurable options */ --- 121,127 ---- * We set aside some extra PGPROC structures for "dummy" processes, * ie things that aren't full-fledged backends but need shmem access. */ ! #define NUM_DUMMY_PROCS 3 /* configurable options */