aboutsummaryrefslogtreecommitdiff
path: root/src/backend/postmaster/autovacuum.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/postmaster/autovacuum.c')
-rw-r--r--src/backend/postmaster/autovacuum.c668
1 files changed, 529 insertions, 139 deletions
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 130ad45fd8b..672c3d5aa40 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.31 2007/01/16 13:28:56 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.32 2007/02/15 23:23:23 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
@@ -39,7 +39,9 @@
#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,6 +52,9 @@
#include "utils/syscache.h"
+static volatile sig_atomic_t got_SIGHUP = false;
+static volatile sig_atomic_t avlauncher_shutdown_request = false;
+
/*
* GUC parameters
*/
@@ -65,11 +70,8 @@ int autovacuum_vac_cost_delay;
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;
-static time_t last_autovac_stop_time = 0;
+static bool am_autovacuum_launcher = false;
+static bool am_autovacuum_worker = false;
/* Comparison point for determining whether freeze_max_age is exceeded */
static TransactionId recentXid;
@@ -101,11 +103,21 @@ typedef struct autovac_table
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 autovac_forkexec(void);
+static pid_t avlauncher_forkexec(void);
+static pid_t avworker_forkexec(void);
#endif
-NON_EXEC_STATIC void AutoVacMain(int argc, char *argv[]);
+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 void test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
@@ -116,47 +128,59 @@ static void test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
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 avl_sighup_handler(SIGNAL_ARGS);
+static void avlauncher_shutdown(SIGNAL_ARGS);
+static void avl_quickdie(SIGNAL_ARGS);
+
+/********************************************************************
+ * AUTOVACUUM LAUNCHER CODE
+ ********************************************************************/
+
+#ifdef EXEC_BACKEND
/*
- * Main entry point for autovacuum controller process.
+ * forkexec routine for the autovacuum launcher process.
*
- * This code is heavily based on pgarch.c, q.v.
+ * Format up the arglist, then fork and exec.
*/
-int
-autovac_start(void)
+static pid_t
+avlauncher_forkexec(void)
{
- time_t curtime;
- pid_t AutoVacPID;
+ char *av[10];
+ int ac = 0;
- /*
- * Do nothing if too soon since last autovacuum exit. This limits how
- * often the daemon runs. Since the time per iteration can be quite
- * variable, it seems more useful to measure/control the time since last
- * subprocess exit than since last subprocess launch.
- *
- * However, we *also* check the time since last subprocess launch; this
- * prevents thrashing under fork-failure conditions.
- *
- * Note that since we will be re-called from the postmaster main loop, we
- * will get another chance later if we do nothing now.
- *
- * XXX todo: implement sleep scale factor that existed in contrib code.
- */
+ av[ac++] = "postgres";
+ av[ac++] = "--forkavlauncher";
+ av[ac++] = NULL; /* filled in by postmaster_forkexec */
+ av[ac] = NULL;
- curtime = time(NULL);
- if ((unsigned int) (curtime - last_autovac_stop_time) <
- (unsigned int) autovacuum_naptime)
- return 0;
+ Assert(ac < lengthof(av));
- if ((unsigned int) (curtime - last_autovac_start_time) <
- (unsigned int) autovacuum_naptime)
- return 0;
+ return postmaster_forkexec(ac, av);
+}
- last_autovac_start_time = curtime;
+/*
+ * 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)
+{
+ pid_t AutoVacPID;
#ifdef EXEC_BACKEND
- switch ((AutoVacPID = autovac_forkexec()))
+ switch ((AutoVacPID = avlauncher_forkexec()))
#else
switch ((AutoVacPID = fork_process()))
#endif
@@ -175,7 +199,7 @@ autovac_start(void)
/* Lose the postmaster's on-exit routines */
on_exit_reset();
- AutoVacMain(0, NULL);
+ AutoVacLauncherMain(0, NULL);
break;
#endif
default:
@@ -187,28 +211,362 @@ autovac_start(void)
}
/*
- * autovac_stopped --- called by postmaster when subprocess exit is detected
+ * Main loop for the autovacuum launcher process.
*/
-void
-autovac_stopped(void)
+NON_EXEC_STATIC void
+AutoVacLauncherMain(int argc, char *argv[])
{
- last_autovac_stop_time = time(NULL);
+ 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.
+ *
+ * XXX It may be a good idea to receive signals when an avworker process
+ * finishes.
+ */
+ pqsignal(SIGHUP, avl_sighup_handler);
+
+ pqsignal(SIGINT, SIG_IGN);
+ pqsignal(SIGTERM, avlauncher_shutdown);
+ 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_clear_snapshot();
+
+ /* 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);
+
+ /*
+ * take a nap before executing the first iteration, unless we were
+ * requested an emergency run.
+ */
+ if (autovacuum_start_daemon)
+ pg_usleep(autovacuum_naptime * 1000000L);
+
+ 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_shutdown_request)
+ break;
+
+ if (got_SIGHUP)
+ {
+ got_SIGHUP = false;
+ ProcessConfigFile(PGC_SIGHUP);
+ }
+
+ /*
+ * 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:
+ /*
+ * in emergency mode, exit immediately so that the postmaster can
+ * request another run right away if needed.
+ *
+ * XXX -- maybe it would be better to handle this inside the launcher
+ * itself.
+ */
+ if (!autovacuum_start_daemon)
+ break;
+
+ /* have pgstat read the file again next time */
+ pgstat_clear_snapshot();
+
+ /* now sleep until the next autovac iteration */
+ pg_usleep(autovacuum_naptime * 1000000L);
+ }
+
+ /* Normal exit from the autovac launcher is here */
+ ereport(LOG,
+ (errmsg("autovacuum launcher shutting down")));
+
+ proc_exit(0); /* done */
+}
+
+/* SIGHUP: set flag to re-read config file at next convenient time */
+static void
+avl_sighup_handler(SIGNAL_ARGS)
+{
+ got_SIGHUP = true;
}
+static void
+avlauncher_shutdown(SIGNAL_ARGS)
+{
+ avlauncher_shutdown_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
/*
- * autovac_forkexec()
+ * forkexec routines for the autovacuum worker.
*
- * Format up the arglist for the autovacuum process, then fork and exec.
+ * Format up the arglist, then fork and exec.
*/
static pid_t
-autovac_forkexec(void)
+avworker_forkexec(void)
{
char *av[10];
int ac = 0;
av[ac++] = "postgres";
- av[ac++] = "--forkautovac";
+ av[ac++] = "--forkavworker";
av[ac++] = NULL; /* filled in by postmaster_forkexec */
av[ac] = NULL;
@@ -221,34 +579,71 @@ autovac_forkexec(void)
* We need this set from the outside, before InitProcess is called
*/
void
-AutovacuumIAm(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)
{
- am_autovacuum = true;
+ 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;
}
-#endif /* EXEC_BACKEND */
/*
- * AutoVacMain
+ * AutoVacWorkerMain
*/
NON_EXEC_STATIC void
-AutoVacMain(int argc, char *argv[])
+AutoVacWorkerMain(int argc, char *argv[])
{
- ListCell *cell;
- List *dblist;
- autovac_dbase *db;
- TransactionId xidForceLimit;
- bool for_xid_wrap;
sigjmp_buf local_sigjmp_buf;
+ Oid dbid;
/* we are a postmaster subprocess now */
IsUnderPostmaster = true;
- am_autovacuum = true;
+ am_autovacuum_worker = true;
/* reset MyProcPid */
MyProcPid = getpid();
/* Identify myself via ps */
- init_ps_display("autovacuum process", "", "", "");
+ init_ps_display("autovacuum worker process", "", "", "");
SetProcessingMode(InitProcessing);
@@ -335,78 +730,24 @@ AutoVacMain(int argc, char *argv[])
*/
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.)
+ * 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.
*/
- recentXid = ReadNewTransactionId();
- xidForceLimit = recentXid - autovacuum_freeze_max_age;
- /* ensure it's a "normal" XID, else TransactionIdPrecedes misbehaves */
- if (xidForceLimit < FirstNormalTransactionId)
- xidForceLimit -= FirstNormalTransactionId;
+ LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
- /*
- * 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);
+ dbid = AutoVacuumShmem->process_db;
+ AutoVacuumShmem->process_db = InvalidOid;
+ AutoVacuumShmem->worker_pid = MyProcPid;
- /* Find pgstat entry if any */
- tmp->entry = pgstat_fetch_stat_dbentry(tmp->oid);
+ LWLockRelease(AutovacuumLock);
- /* 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)
+ 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
@@ -415,7 +756,7 @@ AutoVacMain(int argc, char *argv[])
* database, rather than making any progress on stuff it can connect
* to.
*/
- pgstat_report_autovac(db->oid);
+ pgstat_report_autovac(dbid);
/*
* Connect to the selected database
@@ -423,11 +764,11 @@ AutoVacMain(int argc, char *argv[])
* 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);
+ InitPostgres(NULL, dbid, NULL, &dbname);
SetProcessingMode(NormalProcessing);
- set_ps_display(db->name, false);
+ set_ps_display(dbname, false);
ereport(DEBUG1,
- (errmsg("autovacuum: processing database \"%s\"", db->name)));
+ (errmsg("autovacuum: processing database \"%s\"", dbname)));
/* Create the memory context where cross-transaction state is stored */
AutovacMemCxt = AllocSetContextCreate(TopMemoryContext,
@@ -436,13 +777,21 @@ AutoVacMain(int argc, char *argv[])
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
- /*
- * And do an appropriate amount of work
- */
- do_autovacuum(db->entry);
+ /* And do an appropriate amount of work */
+ recentXid = ReadNewTransactionId();
+ dbentry = pgstat_fetch_stat_dbentry(dbid);
+ do_autovacuum(dbentry);
}
- /* One iteration done, go away */
+ /*
+ * 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);
}
@@ -450,7 +799,7 @@ AutoVacMain(int argc, char *argv[])
* 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.
+ * because we aren't connected; we use the flat database file.
*/
static List *
autovac_get_database_list(void)
@@ -912,7 +1261,7 @@ autovacuum_do_vac_analyze(Oid relid, bool dovacuum, bool doanalyze,
vacstmt->analyze = doanalyze;
vacstmt->freeze_min_age = freeze_min_age;
vacstmt->verbose = false;
- vacstmt->relation = NULL; /* not used since we pass relids list */
+ vacstmt->relation = NULL; /* not used since we pass a relids list */
vacstmt->va_cols = NIL;
/* Let pgstat know what we're doing */
@@ -1011,11 +1360,52 @@ autovac_init(void)
}
/*
- * IsAutoVacuumProcess
- * Return whether this process is an autovacuum process.
+ * IsAutoVacuum functions
+ * Return whether this is either a launcher autovacuum process or a worker
+ * process.
*/
bool
-IsAutoVacuumProcess(void)
+IsAutoVacuumLauncherProcess(void)
+{
+ return am_autovacuum_launcher;
+}
+
+bool
+IsAutoVacuumWorkerProcess(void)
{
- return am_autovacuum;
+ 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));
}