diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c index ae2b8dcd69..6ce9d1b586 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.36 2007/09/21 16:32:19 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.37 2007/10/24 20:55:36 alvherre Exp $ * * NOTES * Each global transaction is associated with a global transaction @@ -283,8 +283,7 @@ MarkAsPreparing(TransactionId xid, const char *gid, gxact->proc.databaseId = databaseid; gxact->proc.roleId = owner; gxact->proc.inCommit = false; - gxact->proc.inVacuum = false; - gxact->proc.isAutovacuum = false; + gxact->proc.vacuumFlags = 0; gxact->proc.lwWaiting = false; gxact->proc.lwExclusive = false; gxact->proc.lwWaitLink = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 418dbaa108..51944c54c2 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.109 2007/09/24 03:12:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.110 2007/10/24 20:55:36 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -31,6 +31,7 @@ #include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" +#include "storage/proc.h" #include "utils/acl.h" #include "utils/datum.h" #include "utils/lsyscache.h" @@ -201,6 +202,11 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, return; } + /* let others know what I'm doing */ + LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); + MyProc->vacuumFlags |= PROC_IN_ANALYZE; + LWLockRelease(ProcArrayLock); + /* measure elapsed time iff autovacuum logging requires it */ if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0) { @@ -484,6 +490,14 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, RelationGetRelationName(onerel), pg_rusage_show(&ru0)))); } + + /* + * Reset my PGPROC flag. Note: we need this here, and not in vacuum_rel, + * because the vacuum flag is cleared by the end-of-xact code. + */ + LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); + MyProc->vacuumFlags &= ~PROC_IN_ANALYZE; + LWLockRelease(ProcArrayLock); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 5630fc2730..55ce0dbade 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -13,7 +13,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.359 2007/09/20 17:56:31 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.360 2007/10/24 20:55:36 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -660,9 +660,9 @@ vacuum_set_xid_limits(int freeze_min_age, bool sharedRel, * fixed-size never-null columns, but these are. * * Another reason for doing it this way is that when we are in a lazy - * VACUUM and have inVacuum set, we mustn't do any updates --- somebody - * vacuuming pg_class might think they could delete a tuple marked with - * xmin = our xid. + * VACUUM and have PROC_IN_VACUUM set, we mustn't do any updates --- + * somebody vacuuming pg_class might think they could delete a tuple + * marked with xmin = our xid. * * This routine is shared by full VACUUM, lazy VACUUM, and stand-alone * ANALYZE. @@ -987,9 +987,9 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind) * During a lazy VACUUM we do not run any user-supplied functions, and * so it should be safe to not create a transaction snapshot. * - * We can furthermore set the inVacuum flag, which lets other + * We can furthermore set the PROC_IN_VACUUM flag, which lets other * concurrent VACUUMs know that they can ignore this one while - * determining their OldestXmin. (The reason we don't set inVacuum + * determining their OldestXmin. (The reason we don't set it * during a full VACUUM is exactly that we may have to run user- * defined functions for functional indexes, and we want to make sure * that if they use the snapshot set above, any tuples it requires @@ -997,12 +997,14 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind) * depends on the contents of other tables is arguably broken, but we * won't break it here by violating transaction semantics.) * - * Note: the inVacuum flag remains set until CommitTransaction or + * Note: this flag remains set until CommitTransaction or * AbortTransaction. We don't want to clear it until we reset * MyProc->xid/xmin, else OldestXmin might appear to go backwards, * which is probably Not Good. */ - MyProc->inVacuum = true; + LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); + MyProc->vacuumFlags |= PROC_IN_VACUUM; + LWLockRelease(ProcArrayLock); } /* diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 9e472d02ff..ea586f899a 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -55,7 +55,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.62 2007/10/24 19:08:25 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.63 2007/10/24 20:55:36 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -172,6 +172,7 @@ typedef struct autovac_table int at_freeze_min_age; int at_vacuum_cost_delay; int at_vacuum_cost_limit; + bool at_wraparound; } autovac_table; /*------------- @@ -280,7 +281,7 @@ static autovac_table *table_recheck_autovac(Oid relid); static void relation_needs_vacanalyze(Oid relid, Form_pg_autovacuum avForm, Form_pg_class classForm, PgStat_StatTabEntry *tabentry, bool *dovacuum, - bool *doanalyze); + bool *doanalyze, bool *wraparound); static void autovacuum_do_vac_analyze(Oid relid, bool dovacuum, bool doanalyze, int freeze_min_age, @@ -1440,9 +1441,6 @@ AutoVacWorkerMain(int argc, char *argv[]) /* Identify myself via ps */ init_ps_display("autovacuum worker process", "", "", ""); - if (PostAuthDelay) - pg_usleep(PostAuthDelay * 1000000L); - SetProcessingMode(InitProcessing); /* @@ -1601,6 +1599,9 @@ AutoVacWorkerMain(int argc, char *argv[]) ereport(DEBUG1, (errmsg("autovacuum: processing database \"%s\"", dbname))); + if (PostAuthDelay) + pg_usleep(PostAuthDelay * 1000000L); + /* And do an appropriate amount of work */ recentXid = ReadNewTransactionId(); do_autovacuum(); @@ -2085,6 +2086,14 @@ next_worker: /* clean up memory before each iteration */ MemoryContextResetAndDeleteChildren(PortalContext); + /* set the "vacuum for wraparound" flag in PGPROC */ + if (tab->at_wraparound) + { + LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); + MyProc->vacuumFlags |= PROC_VACUUM_FOR_WRAPAROUND; + LWLockRelease(ProcArrayLock); + } + /* * We will abort vacuuming the current table if something errors out, * and continue with the next one in schedule; in particular, this @@ -2119,6 +2128,7 @@ next_worker: get_rel_name(tab->at_relid)); EmitErrorReport(); + /* this resets the PGPROC flags too */ AbortOutOfAnyTransaction(); FlushErrorState(); MemoryContextResetAndDeleteChildren(PortalContext); @@ -2129,6 +2139,14 @@ next_worker: } PG_END_TRY(); + /* reset my PGPROC flag */ + if (tab->at_wraparound) + { + LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); + MyProc->vacuumFlags &= ~PROC_VACUUM_FOR_WRAPAROUND; + LWLockRelease(ProcArrayLock); + } + /* be tidy */ pfree(tab); @@ -2223,9 +2241,10 @@ relation_check_autovac(Oid relid, Form_pg_class classForm, { bool dovacuum; bool doanalyze; + bool dummy; relation_needs_vacanalyze(relid, avForm, classForm, tabentry, - &dovacuum, &doanalyze); + &dovacuum, &doanalyze, &dummy); if (classForm->relkind == RELKIND_TOASTVALUE) { @@ -2272,6 +2291,8 @@ table_recheck_autovac(Oid relid) bool doit = false; PgStat_StatDBEntry *shared; PgStat_StatDBEntry *dbentry; + bool wraparound, + toast_wraparound = false; /* use fresh stats */ autovac_refresh_stats(); @@ -2298,7 +2319,7 @@ table_recheck_autovac(Oid relid) shared, dbentry); relation_needs_vacanalyze(relid, avForm, classForm, tabentry, - &dovacuum, &doanalyze); + &dovacuum, &doanalyze, &wraparound); /* OK, it needs vacuum by itself */ if (dovacuum) @@ -2316,6 +2337,7 @@ table_recheck_autovac(Oid relid) { bool toast_dovacuum; bool toast_doanalyze; + bool toast_wraparound; Form_pg_class toastClassForm; PgStat_StatTabEntry *toasttabentry; @@ -2325,9 +2347,10 @@ table_recheck_autovac(Oid relid) shared, dbentry); /* note we use the pg_autovacuum entry for the main table */ - relation_needs_vacanalyze(toastrelid, avForm, toastClassForm, - toasttabentry, &toast_dovacuum, - &toast_doanalyze); + relation_needs_vacanalyze(toastrelid, avForm, + toastClassForm, toasttabentry, + &toast_dovacuum, &toast_doanalyze, + &toast_wraparound); /* we only consider VACUUM for toast tables */ if (toast_dovacuum) { @@ -2389,6 +2412,7 @@ table_recheck_autovac(Oid relid) tab->at_freeze_min_age = freeze_min_age; tab->at_vacuum_cost_limit = vac_cost_limit; tab->at_vacuum_cost_delay = vac_cost_delay; + tab->at_wraparound = wraparound || toast_wraparound; } heap_close(avRel, AccessShareLock); @@ -2403,7 +2427,8 @@ table_recheck_autovac(Oid relid) * relation_needs_vacanalyze * * Check whether a relation needs to be vacuumed or analyzed; return each into - * "dovacuum" and "doanalyze", respectively. avForm and tabentry can be NULL, + * "dovacuum" and "doanalyze", respectively. Also return whether the vacuum is + * being forced because of Xid wraparound. avForm and tabentry can be NULL, * classForm shouldn't. * * A table needs to be vacuumed if the number of dead tuples exceeds a @@ -2437,7 +2462,8 @@ relation_needs_vacanalyze(Oid relid, PgStat_StatTabEntry *tabentry, /* output params below */ bool *dovacuum, - bool *doanalyze) + bool *doanalyze, + bool *wraparound) { bool force_vacuum; float4 reltuples; /* pg_class.reltuples */ @@ -2499,6 +2525,7 @@ relation_needs_vacanalyze(Oid relid, force_vacuum = (TransactionIdIsNormal(classForm->relfrozenxid) && TransactionIdPrecedes(classForm->relfrozenxid, xidForceLimit)); + *wraparound = force_vacuum; /* User disabled it in pg_autovacuum? (But ignore if at risk) */ if (avForm && !avForm->enabled && !force_vacuum) diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 90ba09d5a8..c455c89f99 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -23,7 +23,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.35 2007/09/23 18:50:38 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.36 2007/10/24 20:55:36 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -242,7 +242,8 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid) proc->xid = InvalidTransactionId; proc->lxid = InvalidLocalTransactionId; proc->xmin = InvalidTransactionId; - proc->inVacuum = false; /* must be cleared with xid/xmin */ + /* must be cleared with xid/xmin: */ + proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK; proc->inCommit = false; /* be sure this is cleared in abort */ /* Clear the subtransaction-XID cache too while holding the lock */ @@ -267,7 +268,8 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid) proc->lxid = InvalidLocalTransactionId; proc->xmin = InvalidTransactionId; - proc->inVacuum = false; /* must be cleared with xid/xmin */ + /* must be cleared with xid/xmin: */ + proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK; proc->inCommit = false; /* be sure this is cleared in abort */ Assert(proc->subxids.nxids == 0); @@ -296,8 +298,10 @@ ProcArrayClearTransaction(PGPROC *proc) proc->xid = InvalidTransactionId; proc->lxid = InvalidLocalTransactionId; proc->xmin = InvalidTransactionId; - proc->inVacuum = false; /* redundant, but just in case */ - proc->inCommit = false; /* ditto */ + + /* redundant, but just in case */ + proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK; + proc->inCommit = false; /* Clear the subtransaction-XID cache too */ proc->subxids.nxids = 0; @@ -546,7 +550,8 @@ TransactionIdIsActive(TransactionId xid) * If allDbs is TRUE then all backends are considered; if allDbs is FALSE * then only backends running in my own database are considered. * - * If ignoreVacuum is TRUE then backends with inVacuum set are ignored. + * If ignoreVacuum is TRUE then backends with the PROC_IN_VACUUM flag set are + * ignored. * * This is used by VACUUM to decide which deleted tuples must be preserved * in a table. allDbs = TRUE is needed for shared relations, but allDbs = @@ -586,7 +591,7 @@ GetOldestXmin(bool allDbs, bool ignoreVacuum) { volatile PGPROC *proc = arrayP->procs[index]; - if (ignoreVacuum && proc->inVacuum) + if (ignoreVacuum && (proc->vacuumFlags & PROC_IN_VACUUM)) continue; if (allDbs || proc->databaseId == MyDatabaseId) @@ -723,7 +728,7 @@ GetSnapshotData(Snapshot snapshot, bool serializable) TransactionId xid; /* Ignore procs running LAZY VACUUM */ - if (proc->inVacuum) + if (proc->vacuumFlags & PROC_IN_VACUUM) continue; /* Update globalxmin to be the smallest valid xmin */ @@ -1193,7 +1198,7 @@ CheckOtherDBBackends(Oid databaseId) found = true; - if (proc->isAutovacuum) + if (proc->vacuumFlags & PROC_IS_AUTOVACUUM) { /* an autovacuum --- send it SIGTERM before sleeping */ int autopid = proc->pid; diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index ddebdcc5e2..fdf089f836 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.194 2007/09/08 20:31:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.195 2007/10/24 20:55:36 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -291,8 +291,9 @@ InitProcess(void) MyProc->databaseId = InvalidOid; MyProc->roleId = InvalidOid; MyProc->inCommit = false; - MyProc->inVacuum = false; - MyProc->isAutovacuum = IsAutoVacuumWorkerProcess(); + MyProc->vacuumFlags = 0; + if (IsAutoVacuumWorkerProcess()) + MyProc->vacuumFlags |= PROC_IS_AUTOVACUUM; MyProc->lwWaiting = false; MyProc->lwExclusive = false; MyProc->lwWaitLink = NULL; @@ -429,8 +430,8 @@ InitAuxiliaryProcess(void) MyProc->databaseId = InvalidOid; MyProc->roleId = InvalidOid; MyProc->inCommit = false; - MyProc->inVacuum = false; - MyProc->isAutovacuum = IsAutoVacuumLauncherProcess(); /* is this needed? */ + /* we don't set the "is autovacuum" flag in the launcher */ + MyProc->vacuumFlags = 0; MyProc->lwWaiting = false; MyProc->lwExclusive = false; MyProc->lwWaitLink = NULL; diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 9fefa0a5a9..4ffb51b434 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -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.100 2007/09/05 18:10:48 tgl Exp $ + * $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.101 2007/10/24 20:55:36 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -38,6 +38,15 @@ struct XidCache TransactionId xids[PGPROC_MAX_CACHED_SUBXIDS]; }; +/* Flags for PGPROC->vacuumFlags */ +#define PROC_IS_AUTOVACUUM 0x01 /* is it an autovac worker? */ +#define PROC_IN_VACUUM 0x02 /* currently running lazy vacuum */ +#define PROC_IN_ANALYZE 0x04 /* currently running analyze */ +#define PROC_VACUUM_FOR_WRAPAROUND 0x08 /* set by autovac only */ + +/* flags reset at EOXact */ +#define PROC_VACUUM_STATE_MASK (0x0E) + /* * Each backend has a PGPROC struct in shared memory. There is also a list of * currently-unused PGPROC structs that will be reallocated to new backends. @@ -82,8 +91,7 @@ struct PGPROC bool inCommit; /* true if within commit critical section */ - bool inVacuum; /* true if current xact is a LAZY VACUUM */ - bool isAutovacuum; /* true if it's autovacuum */ + uint8 vacuumFlags; /* vacuum-related flags, see above */ /* Info about LWLock the process is currently waiting for, if any. */ bool lwWaiting; /* true if waiting for an LW lock */