Restructure autovacuum in two processes: a dummy process, which runs

continuously, and requests vacuum runs of "autovacuum workers" to postmaster.
The workers do the actual vacuum work.  This allows for future improvements,
like allowing multiple autovacuum jobs running in parallel.

For now, the code keeps the original behavior of having a single autovac
process at any time by sleeping until the previous worker has finished.
This commit is contained in:
Alvaro Herrera 2007-02-15 23:23:23 +00:00
parent eecbb33267
commit 1820650934
16 changed files with 807 additions and 250 deletions

View File

@ -6,7 +6,7 @@
* Copyright (c) 2000-2007, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.77 2007/01/05 22:19:23 momjian Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.78 2007/02/15 23:23:22 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
@ -72,7 +72,7 @@ GetNewTransactionId(bool isSubXact)
* still gives plenty of chances before we get into real trouble.
*/
if (IsUnderPostmaster && (xid % 65536) == 0)
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC);
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
if (IsUnderPostmaster &&
TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidStopLimit))
@ -286,7 +286,7 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
*/
if (TransactionIdFollowsOrEquals(curXid, xidVacLimit) &&
IsUnderPostmaster)
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC);
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
/* Give an immediate warning if past the wrap warn point */
if (TransactionIdFollowsOrEquals(curXid, xidWarnLimit))

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.230 2007/02/10 14:58:54 petere Exp $
* $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.231 2007/02/15 23:23:22 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
@ -449,7 +449,7 @@ BootstrapMain(int argc, char *argv[])
* Do backend-like initialization for bootstrap mode
*/
InitProcess();
(void) InitPostgres(dbname, NULL);
(void) InitPostgres(dbname, InvalidOid, NULL, NULL);
/*
* In NOP mode, all we really want to do is create shared memory and

View File

@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.345 2007/02/05 04:22:18 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.346 2007/02/15 23:23:22 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
@ -303,7 +303,7 @@ vacuum(VacuumStmt *vacstmt, List *relids)
* Send info about dead objects to the statistics collector, unless we are
* in autovacuum --- autovacuum.c does this for itself.
*/
if (vacstmt->vacuum && !IsAutoVacuumProcess())
if (vacstmt->vacuum && !IsAutoVacuumWorkerProcess())
pgstat_vacuum_tabstat();
/*
@ -472,7 +472,7 @@ vacuum(VacuumStmt *vacstmt, List *relids)
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
}
if (vacstmt->vacuum && !IsAutoVacuumProcess())
if (vacstmt->vacuum && !IsAutoVacuumWorkerProcess())
{
/*
* Update pg_database.datfrozenxid, and truncate pg_clog if possible.

View File

@ -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);
/*
* 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;
/*
* 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.
*/
curtime = time(NULL);
if ((unsigned int) (curtime - last_autovac_stop_time) <
(unsigned int) autovacuum_naptime)
return 0;
if ((unsigned int) (curtime - last_autovac_start_time) <
(unsigned int) autovacuum_naptime)
return 0;
last_autovac_start_time = curtime;
/********************************************************************
* AUTOVACUUM LAUNCHER CODE
********************************************************************/
#ifdef EXEC_BACKEND
switch ((AutoVacPID = autovac_forkexec()))
/*
* 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)
{
pid_t AutoVacPID;
#ifdef EXEC_BACKEND
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 = true;
am_autovacuum_worker = true;
}
#endif /* EXEC_BACKEND */
#endif
/*
* AutoVacMain
* 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
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)
dbid = AutoVacuumShmem->process_db;
AutoVacuumShmem->process_db = InvalidOid;
AutoVacuumShmem->worker_pid = MyProcPid;
LWLockRelease(AutovacuumLock);
if (OidIsValid(dbid))
{
autovac_dbase *tmp = lfirst(cell);
char *dbname;
PgStat_StatDBEntry *dbentry;
/* 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
@ -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;
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));
}

View File

@ -13,7 +13,7 @@
*
* Copyright (c) 2001-2007, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.146 2007/02/09 16:12:18 tgl Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.147 2007/02/15 23:23:23 alvherre Exp $
* ----------
*/
#include "postgres.h"
@ -930,7 +930,7 @@ pgstat_report_vacuum(Oid tableoid, bool shared,
msg.m_databaseid = shared ? InvalidOid : MyDatabaseId;
msg.m_tableoid = tableoid;
msg.m_analyze = analyze;
msg.m_autovacuum = IsAutoVacuumProcess(); /* is this autovacuum? */
msg.m_autovacuum = IsAutoVacuumWorkerProcess(); /* is this autovacuum? */
msg.m_vacuumtime = GetCurrentTimestamp();
msg.m_tuples = tuples;
pgstat_send(&msg, sizeof(msg));
@ -955,7 +955,7 @@ pgstat_report_analyze(Oid tableoid, bool shared, PgStat_Counter livetuples,
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_autovacuum = IsAutoVacuumWorkerProcess(); /* is this autovacuum? */
msg.m_analyzetime = GetCurrentTimestamp();
msg.m_live_tuples = livetuples;
msg.m_dead_tuples = deadtuples;
@ -2280,8 +2280,8 @@ backend_read_statsfile(void)
return;
Assert(!pgStatRunningInCollector);
/* Autovacuum wants stats about all databases */
if (IsAutoVacuumProcess())
/* Autovacuum launcher wants stats about all databases */
if (IsAutoVacuumLauncherProcess())
pgStatDBHash = pgstat_read_statsfile(InvalidOid);
else
pgStatDBHash = pgstat_read_statsfile(MyDatabaseId);
@ -2319,8 +2319,8 @@ pgstat_setup_memcxt(void)
void
pgstat_clear_snapshot(void)
{
/* In an autovacuum process we keep the stats forever */
if (IsAutoVacuumProcess())
/* In an autovacuum worker process we keep the stats forever */
if (IsAutoVacuumWorkerProcess())
return;
/* Release memory, if any was allocated */

View File

@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.521 2007/02/13 19:18:54 tgl Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.522 2007/02/15 23:23:23 alvherre Exp $
*
* NOTES
*
@ -129,13 +129,14 @@
* 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.
* "Special" children such as the startup, bgwriter and autovacuum launcher
* tasks are not in this list. Autovacuum worker processes 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 process */
} Backend;
static Dllist *BackendList;
@ -217,7 +218,8 @@ static bool FatalError = false; /* T if recovering from backend crash */
bool ClientAuthInProgress = false; /* T during new-client
* authentication */
static bool force_autovac = false; /* received START_AUTOVAC signal */
/* received START_AUTOVAC_LAUNCHER signal */
static bool start_autovac_launcher = false;
/*
* State for assigning random salts and cancel keys.
@ -270,9 +272,11 @@ static long PostmasterRandom(void);
static void RandomSalt(char *cryptSalt, char *md5Salt);
static void signal_child(pid_t pid, int signal);
static void SignalChildren(int signal);
static void SignalSomeChildren(int signal, bool only_autovac);
static int CountChildren(void);
static bool CreateOptsFile(int argc, char *argv[], char *fullprogname);
static pid_t StartChildProcess(int xlop);
static void StartAutovacuumWorker(void);
#ifdef EXEC_BACKEND
@ -1145,13 +1149,12 @@ ServerLoop(void)
/*
* 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.
* 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 = Min(60, autovacuum_naptime);
timeout.tv_sec = 60;
timeout.tv_usec = 0;
PG_SETMASK(&UnBlockSig);
@ -1238,17 +1241,13 @@ ServerLoop(void)
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 &&
/* If we have lost the autovacuum launcher, try to start a new one */
if ((AutoVacuumingActive() || start_autovac_launcher) && AutoVacPID == 0 &&
StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
{
AutoVacPID = autovac_start();
AutoVacPID = StartAutoVacLauncher();
if (AutoVacPID != 0)
force_autovac = false; /* signal successfully processed */
start_autovac_launcher = false; /* signal successfully processed */
}
/* If we have lost the archiver, try to start a new one */
@ -1874,15 +1873,9 @@ pmdie(SIGNAL_ARGS)
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 */
}
/* autovacuum workers are shut down immediately */
if (DLGetHead(BackendList))
SignalSomeChildren(SIGINT, true);
if (DLGetHead(BackendList))
break; /* let reaper() handle this */
@ -1904,6 +1897,9 @@ pmdie(SIGNAL_ARGS)
/* 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:
@ -1920,15 +1916,13 @@ pmdie(SIGNAL_ARGS)
ereport(LOG,
(errmsg("received fast shutdown request")));
if (DLGetHead(BackendList) || AutoVacPID != 0)
if (DLGetHead(BackendList))
{
if (!FatalError)
{
ereport(LOG,
(errmsg("aborting any active transactions")));
SignalChildren(SIGTERM);
if (AutoVacPID != 0)
signal_child(AutoVacPID, SIGTERM);
/* reaper() does the rest */
}
break;
@ -1959,6 +1953,9 @@ pmdie(SIGNAL_ARGS)
/* 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:
@ -2073,8 +2070,8 @@ reaper(SIGNAL_ARGS)
/*
* 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.)
* Otherwise, try to start the archiver, stats collector and
* autovacuum launcher.
*/
if (Shutdown > NoShutdown && BgWriterPID != 0)
signal_child(BgWriterPID, SIGUSR2);
@ -2084,6 +2081,9 @@ reaper(SIGNAL_ARGS)
PgArchPID = pgarch_start();
if (PgStatPID == 0)
PgStatPID = pgstat_start();
if (AutoVacuumingActive() && AutoVacPID == 0)
AutoVacPID = StartAutoVacLauncher();
/* at this point we are really open for business */
ereport(LOG,
(errmsg("database system is ready to accept connections")));
@ -2141,18 +2141,16 @@ reaper(SIGNAL_ARGS)
}
/*
* 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.
* 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;
autovac_stopped();
if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus))
if (!EXIT_STATUS_0(exitstatus))
HandleChildCrash(pid, exitstatus,
_("autovacuum process"));
_("autovacuum launcher process"));
continue;
}
@ -2230,7 +2228,7 @@ reaper(SIGNAL_ARGS)
if (Shutdown > NoShutdown)
{
if (DLGetHead(BackendList) || StartupPID != 0 || AutoVacPID != 0)
if (DLGetHead(BackendList) || StartupPID != 0)
goto reaper_done;
/* Start the bgwriter if not running */
if (BgWriterPID == 0)
@ -2244,6 +2242,9 @@ reaper(SIGNAL_ARGS)
/* 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:
@ -2371,7 +2372,7 @@ HandleChildCrash(int pid, int exitstatus, const char *procname)
signal_child(BgWriterPID, (SendStop ? SIGSTOP : SIGQUIT));
}
/* Take care of the autovacuum daemon too */
/* Take care of the autovacuum launcher too */
if (pid == AutoVacPID)
AutoVacPID = 0;
else if (AutoVacPID != 0 && !FatalError)
@ -2501,10 +2502,22 @@ signal_child(pid_t pid, int signal)
}
/*
* Send a signal to all backend children (but NOT special children)
* Send a signal to all backend children, including autovacuum workers (but NOT
* special children).
*/
static void
SignalChildren(int signal)
{
SignalSomeChildren(signal, false);
}
/*
* Send a signal to all backend children, including autovacuum workers (but NOT
* special children). If only_autovac is TRUE, only the autovacuum worker
* processes are signalled.
*/
static void
SignalSomeChildren(int signal, bool only_autovac)
{
Dlelem *curr;
@ -2512,6 +2525,9 @@ SignalChildren(int signal)
{
Backend *bp = (Backend *) DLE_VAL(curr);
if (only_autovac && !bp->is_autovacuum)
continue;
ereport(DEBUG4,
(errmsg_internal("sending signal %d to process %d",
signal, (int) bp->pid)));
@ -2523,6 +2539,8 @@ SignalChildren(int signal)
* BackendStartup -- start backend process
*
* returns: STATUS_ERROR if the fork failed, STATUS_OK otherwise.
*
* Note: if you change this code, also consider StartAutovacuumWorker.
*/
static int
BackendStartup(Port *port)
@ -3319,13 +3337,16 @@ SubPostmasterMain(int argc, char *argv[])
* same address the postmaster used.
*/
if (strcmp(argv[1], "--forkbackend") == 0 ||
strcmp(argv[1], "--forkautovac") == 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], "--forkautovac") == 0)
AutovacuumIAm();
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
@ -3420,7 +3441,24 @@ SubPostmasterMain(int argc, char *argv[])
BootstrapMain(argc - 2, argv + 2);
proc_exit(0);
}
if (strcmp(argv[1], "--forkautovac") == 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);
@ -3434,7 +3472,7 @@ SubPostmasterMain(int argc, char *argv[])
/* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, 0);
AutoVacMain(argc - 2, argv + 2);
AutoVacWorkerMain(argc - 2, argv + 2);
proc_exit(0);
}
if (strcmp(argv[1], "--forkarch") == 0)
@ -3518,11 +3556,7 @@ sigusr1_handler(SIGNAL_ARGS)
* 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) &&
@ -3542,7 +3576,7 @@ sigusr1_handler(SIGNAL_ARGS)
signal_child(SysLoggerPID, SIGUSR1);
}
if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC))
if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER))
{
/*
* Start one iteration of the autovacuum daemon, even if autovacuuming
@ -3553,9 +3587,13 @@ sigusr1_handler(SIGNAL_ARGS)
* that by launching another iteration as soon as the current one
* completes.
*/
force_autovac = true;
start_autovac_launcher = true;
}
/* The autovacuum launcher wants us to start a worker process. */
if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER))
StartAutovacuumWorker();
PG_SETMASK(&UnBlockSig);
errno = save_errno;
@ -3759,6 +3797,56 @@ StartChildProcess(int xlop)
return pid;
}
/*
* StartAutovacuumWorker
* Start an autovac worker process.
*
* This function is here because it enters the resulting PID into the
* postmaster's private backends list.
*
* NB -- this code very roughly matches BackendStartup.
*/
static void
StartAutovacuumWorker(void)
{
Backend *bn;
/*
* do nothing if not in condition to run a process. This should not
* actually happen, since the signal is only supposed to be sent by
* autovacuum launcher when it's OK to do it, but test for it just in case.
*/
if (StartupPID != 0 || FatalError || Shutdown != NoShutdown)
return;
bn = (Backend *) malloc(sizeof(Backend));
if (!bn)
{
ereport(LOG,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
return;
}
bn->pid = StartAutoVacWorker();
bn->is_autovacuum = true;
/* we don't need a cancel key */
if (bn->pid > 0)
{
DLAddHead(BackendList, DLNewElem(bn));
#ifdef EXEC_BACKEND
ShmemBackendArrayAdd(bn);
#endif
}
else
{
/* not much we can do */
ereport(LOG,
(errmsg("could not fork new process for autovacuum: %m")));
free(bn);
}
}
/*
* Create the opts file

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.90 2007/01/05 22:19:37 momjian Exp $
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.91 2007/02/15 23:23:23 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
@ -21,6 +21,7 @@
#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"
@ -109,6 +110,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
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());
@ -208,6 +210,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
*/
PMSignalInit();
BgWriterShmemInit();
AutoVacuumShmemInit();
/*
* Set up other modules that need some shared memory space

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.183 2007/01/16 13:28:56 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.184 2007/02/15 23:23:23 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
@ -259,7 +259,7 @@ InitProcess(void)
MyProc->databaseId = InvalidOid;
MyProc->roleId = InvalidOid;
MyProc->inVacuum = false;
MyProc->isAutovacuum = IsAutoVacuumProcess();
MyProc->isAutovacuum = IsAutoVacuumWorkerProcess();
MyProc->lwWaiting = false;
MyProc->lwExclusive = false;
MyProc->lwWaitLink = NULL;
@ -392,7 +392,7 @@ InitDummyProcess(void)
MyProc->databaseId = InvalidOid;
MyProc->roleId = InvalidOid;
MyProc->inVacuum = false;
MyProc->isAutovacuum = false;
MyProc->isAutovacuum = IsAutoVacuumLauncherProcess(); /* is this needed? */
MyProc->lwWaiting = false;
MyProc->lwExclusive = false;
MyProc->lwWaitLink = NULL;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.522 2007/02/10 14:58:55 petere Exp $
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.523 2007/02/15 23:23:23 alvherre Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@ -3139,7 +3139,7 @@ PostgresMain(int argc, char *argv[], const char *username)
*/
ereport(DEBUG3,
(errmsg_internal("InitPostgres")));
am_superuser = InitPostgres(dbname, username);
am_superuser = InitPostgres(dbname, InvalidOid, username, NULL);
SetProcessingMode(NormalProcessing);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.161 2007/02/01 19:10:28 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.162 2007/02/15 23:23:23 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
@ -401,7 +401,7 @@ InitializeSessionUserId(const char *rolename)
*
* We do not enforce them for the autovacuum process either.
*/
if (IsUnderPostmaster && !IsAutoVacuumProcess())
if (IsUnderPostmaster && !IsAutoVacuumWorkerProcess())
{
/*
* Is role allowed to login at all?
@ -462,7 +462,7 @@ void
InitializeSessionUserIdStandalone(void)
{
/* This function should only be called in a single-user backend. */
AssertState(!IsUnderPostmaster || IsAutoVacuumProcess());
AssertState(!IsUnderPostmaster || IsAutoVacuumWorkerProcess());
/* call only once */
AssertState(!OidIsValid(AuthenticatedUserId));

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.173 2007/01/05 22:19:44 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.174 2007/02/15 23:23:23 alvherre Exp $
*
*
*-------------------------------------------------------------------------
@ -47,6 +47,7 @@
static bool FindMyDatabase(const char *name, Oid *db_id, Oid *db_tablespace);
static bool FindMyDatabaseByOid(Oid dbid, char *dbname, Oid *db_tablespace);
static void CheckMyDatabase(const char *name, bool am_superuser);
static void InitCommunication(void);
static void ShutdownPostgres(int code, Datum arg);
@ -102,6 +103,48 @@ FindMyDatabase(const char *name, Oid *db_id, Oid *db_tablespace)
return result;
}
/*
* FindMyDatabaseByOid
*
* As above, but the actual database Id is known. Return its name and the
* tablespace OID. Return TRUE if found, FALSE if not. The same restrictions
* as FindMyDatabase apply.
*/
static bool
FindMyDatabaseByOid(Oid dbid, char *dbname, Oid *db_tablespace)
{
bool result = false;
char *filename;
FILE *db_file;
Oid db_id;
char thisname[NAMEDATALEN];
TransactionId db_frozenxid;
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, &db_id,
db_tablespace, &db_frozenxid))
{
if (dbid == db_id)
{
result = true;
strlcpy(dbname, thisname, NAMEDATALEN);
break;
}
}
FreeFile(db_file);
pfree(filename);
return result;
}
/*
* CheckMyDatabase -- fetch information from the pg_database entry for our DB
*/
@ -135,9 +178,9 @@ CheckMyDatabase(const char *name, bool am_superuser)
* 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.
* We do not enforce them for the autovacuum worker processes either.
*/
if (IsUnderPostmaster && !IsAutoVacuumProcess())
if (IsUnderPostmaster && !IsAutoVacuumWorkerProcess())
{
/*
* Check that the database is currently allowing connections.
@ -270,8 +313,11 @@ BaseInit(void)
* InitPostgres
* Initialize POSTGRES.
*
* In bootstrap mode neither of the parameters are used. In autovacuum
* mode, the username parameter is not used.
* The database can be specified by name, using the in_dbname parameter, or by
* OID, using the dboid parameter. In the latter case, the computed database
* name is passed out to the caller as a palloc'ed string in out_dbname.
*
* In bootstrap mode no parameters are used.
*
* The return value indicates whether the userID is a superuser. (That
* can only be tested inside a transaction, so we want to do it during
@ -285,12 +331,14 @@ BaseInit(void)
* --------------------------------
*/
bool
InitPostgres(const char *dbname, const char *username)
InitPostgres(const char *in_dbname, Oid dboid, const char *username,
char **out_dbname)
{
bool bootstrap = IsBootstrapProcessingMode();
bool autovacuum = IsAutoVacuumProcess();
bool autovacuum = IsAutoVacuumWorkerProcess();
bool am_superuser;
char *fullpath;
char dbname[NAMEDATALEN];
/*
* Set up the global variables holding database id and path. But note we
@ -307,15 +355,32 @@ InitPostgres(const char *dbname, const char *username)
else
{
/*
* Find oid and tablespace of the database we're about to open. Since
* we're not yet up and running we have to use the hackish
* FindMyDatabase, which looks in the flat-file copy of pg_database.
* Find tablespace of the database we're about to open. Since we're not
* yet up and running we have to use one of the hackish FindMyDatabase
* variants, which look in the flat-file copy of pg_database.
*
* If the in_dbname param is NULL, lookup database by OID.
*/
if (!FindMyDatabase(dbname, &MyDatabaseId, &MyDatabaseTableSpace))
ereport(FATAL,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("database \"%s\" does not exist",
dbname)));
if (in_dbname == NULL)
{
if (!FindMyDatabaseByOid(dboid, dbname, &MyDatabaseTableSpace))
ereport(FATAL,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("database %u does not exist", dboid)));
MyDatabaseId = dboid;
/* pass the database name to the caller */
*out_dbname = pstrdup(dbname);
}
else
{
if (!FindMyDatabase(in_dbname, &MyDatabaseId, &MyDatabaseTableSpace))
ereport(FATAL,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("database \"%s\" does not exist",
in_dbname)));
/* our database name is gotten from the caller */
strlcpy(dbname, in_dbname, NAMEDATALEN);
}
}
fullpath = GetDatabasePath(MyDatabaseId, MyDatabaseTableSpace);

View File

@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.191 2007/01/05 22:19:50 momjian Exp $
* $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.192 2007/02/15 23:23:23 alvherre Exp $
*
* NOTES
* some of the information in this file should be moved to other files.
@ -302,7 +302,8 @@ extern ProcessingMode Mode;
*****************************************************************************/
/* in utils/init/postinit.c */
extern bool InitPostgres(const char *dbname, const char *username);
extern bool InitPostgres(const char *in_dbname, Oid dboid, const char *username,
char **out_dbname);
extern void BaseInit(void);
/* in utils/init/miscinit.c */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/postmaster/autovacuum.h,v 1.7 2007/01/16 13:28:57 alvherre Exp $
* $PostgreSQL: pgsql/src/include/postmaster/autovacuum.h,v 1.8 2007/02/15 23:23:23 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
@ -27,16 +27,23 @@ extern int autovacuum_vac_cost_limit;
/* Status inquiry functions */
extern bool AutoVacuumingActive(void);
extern bool IsAutoVacuumProcess(void);
extern bool IsAutoVacuumLauncherProcess(void);
extern bool IsAutoVacuumWorkerProcess(void);
/* Functions to start autovacuum process, called from postmaster */
extern void autovac_init(void);
extern int autovac_start(void);
extern void autovac_stopped(void);
extern int StartAutoVacLauncher(void);
extern int StartAutoVacWorker(void);
#ifdef EXEC_BACKEND
extern void AutoVacMain(int argc, char *argv[]);
extern void AutovacuumIAm(void);
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);
#endif /* AUTOVACUUM_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/storage/lwlock.h,v 1.33 2007/01/05 22:19:58 momjian Exp $
* $PostgreSQL: pgsql/src/include/storage/lwlock.h,v 1.34 2007/02/15 23:23:23 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
@ -61,6 +61,8 @@ typedef enum LWLockId
TablespaceCreateLock,
BtreeVacuumLock,
AddinShmemInitLock,
AutovacuumLock,
/* Individual lock IDs end here */
FirstBufMappingLock,
FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/storage/pmsignal.h,v 1.16 2007/01/05 22:19:58 momjian Exp $
* $PostgreSQL: pgsql/src/include/storage/pmsignal.h,v 1.17 2007/02/15 23:23:23 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
@ -26,7 +26,8 @@ typedef enum
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 */
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;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.93 2007/01/16 13:28:57 alvherre Exp $
* $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.94 2007/02/15 23:23:23 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
@ -121,7 +121,7 @@ typedef struct PROC_HDR
* 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
#define NUM_DUMMY_PROCS 3
/* configurable options */