Initialize the minimum frozen Xid in vac_update_datfrozenxid using

GetOldestXmin() instead of RecentGlobalXmin; this is safer because we do not
depend on the latter being correctly set elsewhere, and while it is more
expensive, this code path is not performance-critical.  This is a real
risk for autovacuum, because it can execute whole cycles without doing
a single vacuum, which would mean that RecentGlobalXmin would stay at its
initialization value, FirstNormalTransactionId, causing a bogus value to be
inserted in pg_database.  This bug could explain some recent reports of
failure to truncate pg_clog.

At the same time, change the initialization of RecentGlobalXmin to
InvalidTransactionId, and ensure that it's set to something else whenever
it's going to be used.  Using it as FirstNormalTransactionId in HOT page
pruning could incur in data loss.  InitPostgres takes care of setting it
to a valid value, but the extra checks are there to prevent "special"
backends from behaving in unusual ways.

Per Tom Lane's detailed problem dissection in 29544.1221061979@sss.pgh.pa.us
This commit is contained in:
Alvaro Herrera 2008-09-11 14:01:16 +00:00
parent d22e47dba8
commit 49b505fd6c
2 changed files with 22 additions and 20 deletions

View File

@ -13,7 +13,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.342.2.5 2008/02/11 19:14:38 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.342.2.6 2008/09/11 14:01:16 alvherre Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -764,14 +764,12 @@ vac_update_datfrozenxid(void)
bool dirty = false; bool dirty = false;
/* /*
* Initialize the "min" calculation with RecentGlobalXmin. Any * Initialize the "min" calculation with GetOldestXmin, which is a
* not-yet-committed pg_class entries for new tables must have * reasonable approximation to the minimum relfrozenxid for not-yet-
* relfrozenxid at least this high, because any other open xact must have * committed pg_class entries for new tables; see AddNewRelationTuple().
* RecentXmin >= its PGPROC.xmin >= our RecentGlobalXmin; see * Se we cannot produce a wrong minimum by starting with this.
* AddNewRelationTuple(). So we cannot produce a wrong minimum by
* starting with this.
*/ */
newFrozenXid = RecentGlobalXmin; newFrozenXid = GetOldestXmin(true, true);
/* /*
* We must seqscan pg_class to find the minimum Xid, because there is no * We must seqscan pg_class to find the minimum Xid, because there is no
@ -965,18 +963,16 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
/* Begin a transaction for vacuuming this relation */ /* Begin a transaction for vacuuming this relation */
StartTransactionCommand(); StartTransactionCommand();
if (vacstmt->full) /*
{ * Functions in indexes may want a snapshot set. Also, setting
/* functions in indexes may want a snapshot set */ * a snapshot ensures that RecentGlobalXmin is kept truly recent.
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); */
} ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
else
if (!vacstmt->full)
{ {
/* /*
* During a lazy VACUUM we do not run any user-supplied functions, and * During a lazy VACUUM we can set the inVacuum flag, which lets other
* so it should be safe to not create a transaction snapshot.
*
* We can furthermore set the inVacuum flag, which lets other
* concurrent VACUUMs know that they can ignore this one while * 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 inVacuum
* during a full VACUUM is exactly that we may have to run user- * during a full VACUUM is exactly that we may have to run user-

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.172 2006/11/05 22:42:09 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.172.2.1 2008/09/11 14:01:16 alvherre Exp $
* *
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
@ -42,6 +42,7 @@
#include "utils/guc.h" #include "utils/guc.h"
#include "utils/portal.h" #include "utils/portal.h"
#include "utils/relcache.h" #include "utils/relcache.h"
#include "utils/tqual.h"
#include "utils/syscache.h" #include "utils/syscache.h"
#include "pgstat.h" #include "pgstat.h"
@ -380,10 +381,15 @@ InitPostgres(const char *dbname, const char *username)
on_shmem_exit(ShutdownPostgres, 0); on_shmem_exit(ShutdownPostgres, 0);
/* /*
* Start a new transaction here before first access to db * Start a new transaction here before first access to db, and get a
* snapshot. We don't have a use for the snapshot itself, but we're
* interested in the secondary effect that it sets RecentGlobalXmin.
*/ */
if (!bootstrap) if (!bootstrap)
{
StartTransactionCommand(); StartTransactionCommand();
(void) GetTransactionSnapshot();
}
/* /*
* Now that we have a transaction, we can take locks. Take a writer's * Now that we have a transaction, we can take locks. Take a writer's