Fix vacuum so that autovacuum is really not cancelled when doing an emergency

job (i.e. to prevent Xid wraparound problems.)  Bug reported by ITAGAKI
Takahiro in 20080314103837.63D3.52131E4D@oss.ntt.co.jp, though I didn't use his
patch.
This commit is contained in:
Alvaro Herrera 2008-03-14 17:25:59 +00:00
parent be5d6df346
commit adc4e1e635
4 changed files with 26 additions and 21 deletions

View File

@ -13,7 +13,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.366 2008/03/10 02:04:08 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.367 2008/03/14 17:25:58 alvherre Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -209,7 +209,8 @@ static BufferAccessStrategy vac_strategy;
static List *get_rel_oids(List *relids, const RangeVar *vacrel, static List *get_rel_oids(List *relids, const RangeVar *vacrel,
const char *stmttype); const char *stmttype);
static void vac_truncate_clog(TransactionId frozenXID); static void vac_truncate_clog(TransactionId frozenXID);
static void vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind); static void vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind,
bool for_wraparound);
static void full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt); static void full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt);
static void scan_heap(VRelStats *vacrelstats, Relation onerel, static void scan_heap(VRelStats *vacrelstats, Relation onerel,
VacPageList vacuum_pages, VacPageList fraged_pages); VacPageList vacuum_pages, VacPageList fraged_pages);
@ -263,6 +264,9 @@ static Size PageGetFreeSpaceWithFillFactor(Relation relation, Page page);
* relation OIDs to be processed, and vacstmt->relation is ignored. * relation OIDs to be processed, and vacstmt->relation is ignored.
* (The non-NIL case is currently only used by autovacuum.) * (The non-NIL case is currently only used by autovacuum.)
* *
* for_wraparound is used by autovacuum to let us know when it's forcing
* a vacuum for wraparound, which should not be auto-cancelled.
*
* bstrategy is normally given as NULL, but in autovacuum it can be passed * bstrategy is normally given as NULL, but in autovacuum it can be passed
* in to use the same buffer strategy object across multiple vacuum() calls. * in to use the same buffer strategy object across multiple vacuum() calls.
* *
@ -274,7 +278,7 @@ static Size PageGetFreeSpaceWithFillFactor(Relation relation, Page page);
*/ */
void void
vacuum(VacuumStmt *vacstmt, List *relids, vacuum(VacuumStmt *vacstmt, List *relids,
BufferAccessStrategy bstrategy, bool isTopLevel) BufferAccessStrategy bstrategy, bool for_wraparound, bool isTopLevel)
{ {
const char *stmttype = vacstmt->vacuum ? "VACUUM" : "ANALYZE"; const char *stmttype = vacstmt->vacuum ? "VACUUM" : "ANALYZE";
volatile MemoryContext anl_context = NULL; volatile MemoryContext anl_context = NULL;
@ -421,7 +425,7 @@ vacuum(VacuumStmt *vacstmt, List *relids,
Oid relid = lfirst_oid(cur); Oid relid = lfirst_oid(cur);
if (vacstmt->vacuum) if (vacstmt->vacuum)
vacuum_rel(relid, vacstmt, RELKIND_RELATION); vacuum_rel(relid, vacstmt, RELKIND_RELATION, for_wraparound);
if (vacstmt->analyze) if (vacstmt->analyze)
{ {
@ -966,7 +970,8 @@ vac_truncate_clog(TransactionId frozenXID)
* At entry and exit, we are not inside a transaction. * At entry and exit, we are not inside a transaction.
*/ */
static void static void
vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind) vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind,
bool for_wraparound)
{ {
LOCKMODE lmode; LOCKMODE lmode;
Relation onerel; Relation onerel;
@ -999,6 +1004,10 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
* contents of other tables is arguably broken, but we won't break it * contents of other tables is arguably broken, but we won't break it
* here by violating transaction semantics.) * here by violating transaction semantics.)
* *
* We also set the VACUUM_FOR_WRAPAROUND flag, which is passed down
* by autovacuum; it's used to avoid cancelling a vacuum that was
* invoked in an emergency.
*
* Note: this 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 * AbortTransaction. We don't want to clear it until we reset
* MyProc->xid/xmin, else OldestXmin might appear to go backwards, * MyProc->xid/xmin, else OldestXmin might appear to go backwards,
@ -1006,6 +1015,8 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
*/ */
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
MyProc->vacuumFlags |= PROC_IN_VACUUM; MyProc->vacuumFlags |= PROC_IN_VACUUM;
if (for_wraparound)
MyProc->vacuumFlags |= PROC_VACUUM_FOR_WRAPAROUND;
LWLockRelease(ProcArrayLock); LWLockRelease(ProcArrayLock);
} }
@ -1147,7 +1158,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
* totally unimportant for toast relations. * totally unimportant for toast relations.
*/ */
if (toast_relid != InvalidOid) if (toast_relid != InvalidOid)
vacuum_rel(toast_relid, vacstmt, RELKIND_TOASTVALUE); vacuum_rel(toast_relid, vacstmt, RELKIND_TOASTVALUE, for_wraparound);
/* /*
* Now release the session-level lock on the master table. * Now release the session-level lock on the master table.

View File

@ -55,7 +55,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.72 2008/02/20 14:01:45 alvherre Exp $ * $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.73 2008/03/14 17:25:58 alvherre Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -285,6 +285,7 @@ static void relation_needs_vacanalyze(Oid relid, Form_pg_autovacuum avForm,
static void autovacuum_do_vac_analyze(Oid relid, bool dovacuum, static void autovacuum_do_vac_analyze(Oid relid, bool dovacuum,
bool doanalyze, int freeze_min_age, bool doanalyze, int freeze_min_age,
bool for_wraparound,
BufferAccessStrategy bstrategy); BufferAccessStrategy bstrategy);
static HeapTuple get_pg_autovacuum_tuple_relid(Relation avRel, Oid relid); static HeapTuple get_pg_autovacuum_tuple_relid(Relation avRel, Oid relid);
static PgStat_StatTabEntry *get_pgstat_tabentry_relid(Oid relid, bool isshared, static PgStat_StatTabEntry *get_pgstat_tabentry_relid(Oid relid, bool isshared,
@ -2095,14 +2096,6 @@ do_autovacuum(void)
/* clean up memory before each iteration */ /* clean up memory before each iteration */
MemoryContextResetAndDeleteChildren(PortalContext); 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);
}
/* /*
* Save the relation name for a possible error message, to avoid a * Save the relation name for a possible error message, to avoid a
* catalog lookup in case of an error. Note: they must live in a * catalog lookup in case of an error. Note: they must live in a
@ -2126,6 +2119,7 @@ do_autovacuum(void)
tab->at_dovacuum, tab->at_dovacuum,
tab->at_doanalyze, tab->at_doanalyze,
tab->at_freeze_min_age, tab->at_freeze_min_age,
tab->at_wraparound,
bstrategy); bstrategy);
/* /*
@ -2604,7 +2598,7 @@ relation_needs_vacanalyze(Oid relid,
*/ */
static void static void
autovacuum_do_vac_analyze(Oid relid, bool dovacuum, bool doanalyze, autovacuum_do_vac_analyze(Oid relid, bool dovacuum, bool doanalyze,
int freeze_min_age, int freeze_min_age, bool for_wraparound,
BufferAccessStrategy bstrategy) BufferAccessStrategy bstrategy)
{ {
VacuumStmt vacstmt; VacuumStmt vacstmt;
@ -2631,7 +2625,7 @@ autovacuum_do_vac_analyze(Oid relid, bool dovacuum, bool doanalyze,
/* Let pgstat know what we're doing */ /* Let pgstat know what we're doing */
autovac_report_activity(&vacstmt, relid); autovac_report_activity(&vacstmt, relid);
vacuum(&vacstmt, list_make1_oid(relid), bstrategy, true); vacuum(&vacstmt, list_make1_oid(relid), bstrategy, for_wraparound, true);
MemoryContextSwitchTo(old_cxt); MemoryContextSwitchTo(old_cxt);
} }

View File

@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.289 2008/01/01 19:45:52 momjian Exp $ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.290 2008/03/14 17:25:58 alvherre Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -1032,7 +1032,7 @@ ProcessUtility(Node *parsetree,
break; break;
case T_VacuumStmt: case T_VacuumStmt:
vacuum((VacuumStmt *) parsetree, NIL, NULL, isTopLevel); vacuum((VacuumStmt *) parsetree, NIL, NULL, false, isTopLevel);
break; break;
case T_ExplainStmt: case T_ExplainStmt:

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/commands/vacuum.h,v 1.75 2008/01/01 19:45:57 momjian Exp $ * $PostgreSQL: pgsql/src/include/commands/vacuum.h,v 1.76 2008/03/14 17:25:59 alvherre Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -114,7 +114,7 @@ extern int vacuum_freeze_min_age;
/* in commands/vacuum.c */ /* in commands/vacuum.c */
extern void vacuum(VacuumStmt *vacstmt, List *relids, extern void vacuum(VacuumStmt *vacstmt, List *relids,
BufferAccessStrategy bstrategy, bool isTopLevel); BufferAccessStrategy bstrategy, bool for_wraparound, bool isTopLevel);
extern void vac_open_indexes(Relation relation, LOCKMODE lockmode, extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
int *nindexes, Relation **Irel); int *nindexes, Relation **Irel);
extern void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode); extern void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode);