mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Fix recently-understood problems with handling of XID freezing, particularly
in PITR scenarios. We now WAL-log the replacement of old XIDs with FrozenTransactionId, so that such replacement is guaranteed to propagate to PITR slave databases. Also, rather than relying on hint-bit updates to be preserved, pg_clog is not truncated until all instances of an XID are known to have been replaced by FrozenTransactionId. Add new GUC variables and pg_autovacuum columns to allow management of the freezing policy, so that users can trade off the size of pg_clog against the amount of freezing work done. Revise the already-existing code that forces autovacuum of tables approaching the wraparound point to make it more bulletproof; also, revise the autovacuum logic so that anti-wraparound vacuuming is done per-table rather than per-database. initdb forced because of changes in pg_class, pg_database, and pg_autovacuum catalogs. Heikki Linnakangas, Simon Riggs, and Tom Lane.
This commit is contained in:
@ -6,7 +6,7 @@
|
||||
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.75 2006/10/04 00:29:49 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.76 2006/11/05 22:42:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -17,6 +17,8 @@
|
||||
#include "access/subtrans.h"
|
||||
#include "access/transam.h"
|
||||
#include "miscadmin.h"
|
||||
#include "postmaster/autovacuum.h"
|
||||
#include "storage/pmsignal.h"
|
||||
#include "storage/proc.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
@ -47,20 +49,31 @@ GetNewTransactionId(bool isSubXact)
|
||||
|
||||
xid = ShmemVariableCache->nextXid;
|
||||
|
||||
/*
|
||||
/*----------
|
||||
* Check to see if it's safe to assign another XID. This protects against
|
||||
* catastrophic data loss due to XID wraparound. The basic rules are:
|
||||
* warn if we're past xidWarnLimit, and refuse to execute transactions if
|
||||
* we're past xidStopLimit, unless we are running in a standalone backend
|
||||
* (which gives an escape hatch to the DBA who ignored all those
|
||||
* warnings).
|
||||
*
|
||||
* If we're past xidVacLimit, start trying to force autovacuum cycles.
|
||||
* If we're past xidWarnLimit, start issuing warnings.
|
||||
* If we're past xidStopLimit, refuse to execute transactions, unless
|
||||
* we are running in a standalone backend (which gives an escape hatch
|
||||
* to the DBA who somehow got past the earlier defenses).
|
||||
*
|
||||
* Test is coded to fall out as fast as possible during normal operation,
|
||||
* ie, when the warn limit is set and we haven't violated it.
|
||||
* ie, when the vac limit is set and we haven't violated it.
|
||||
*----------
|
||||
*/
|
||||
if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidWarnLimit) &&
|
||||
TransactionIdIsValid(ShmemVariableCache->xidWarnLimit))
|
||||
if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidVacLimit) &&
|
||||
TransactionIdIsValid(ShmemVariableCache->xidVacLimit))
|
||||
{
|
||||
/*
|
||||
* To avoid swamping the postmaster with signals, we issue the
|
||||
* autovac request only once per 64K transaction starts. This
|
||||
* still gives plenty of chances before we get into real trouble.
|
||||
*/
|
||||
if (IsUnderPostmaster && (xid % 65536) == 0)
|
||||
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC);
|
||||
|
||||
if (IsUnderPostmaster &&
|
||||
TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidStopLimit))
|
||||
ereport(ERROR,
|
||||
@ -69,7 +82,7 @@ GetNewTransactionId(bool isSubXact)
|
||||
NameStr(ShmemVariableCache->limit_datname)),
|
||||
errhint("Stop the postmaster and use a standalone backend to vacuum database \"%s\".",
|
||||
NameStr(ShmemVariableCache->limit_datname))));
|
||||
else
|
||||
else if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidWarnLimit))
|
||||
ereport(WARNING,
|
||||
(errmsg("database \"%s\" must be vacuumed within %u transactions",
|
||||
NameStr(ShmemVariableCache->limit_datname),
|
||||
@ -178,28 +191,29 @@ ReadNewTransactionId(void)
|
||||
|
||||
/*
|
||||
* Determine the last safe XID to allocate given the currently oldest
|
||||
* datminxid (ie, the oldest XID that might exist in any database
|
||||
* datfrozenxid (ie, the oldest XID that might exist in any database
|
||||
* of our cluster).
|
||||
*/
|
||||
void
|
||||
SetTransactionIdLimit(TransactionId oldest_datminxid,
|
||||
SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
|
||||
Name oldest_datname)
|
||||
{
|
||||
TransactionId xidVacLimit;
|
||||
TransactionId xidWarnLimit;
|
||||
TransactionId xidStopLimit;
|
||||
TransactionId xidWrapLimit;
|
||||
TransactionId curXid;
|
||||
|
||||
Assert(TransactionIdIsValid(oldest_datminxid));
|
||||
Assert(TransactionIdIsNormal(oldest_datfrozenxid));
|
||||
|
||||
/*
|
||||
* The place where we actually get into deep trouble is halfway around
|
||||
* from the oldest existing XID. (This calculation is probably off by one
|
||||
* or two counts, because the special XIDs reduce the size of the loop a
|
||||
* little bit. But we throw in plenty of slop below, so it doesn't
|
||||
* matter.)
|
||||
* from the oldest potentially-existing XID. (This calculation is
|
||||
* probably off by one or two counts, because the special XIDs reduce the
|
||||
* size of the loop a little bit. But we throw in plenty of slop below,
|
||||
* so it doesn't matter.)
|
||||
*/
|
||||
xidWrapLimit = oldest_datminxid + (MaxTransactionId >> 1);
|
||||
xidWrapLimit = oldest_datfrozenxid + (MaxTransactionId >> 1);
|
||||
if (xidWrapLimit < FirstNormalTransactionId)
|
||||
xidWrapLimit += FirstNormalTransactionId;
|
||||
|
||||
@ -229,8 +243,28 @@ SetTransactionIdLimit(TransactionId oldest_datminxid,
|
||||
if (xidWarnLimit < FirstNormalTransactionId)
|
||||
xidWarnLimit -= FirstNormalTransactionId;
|
||||
|
||||
/*
|
||||
* We'll start trying to force autovacuums when oldest_datfrozenxid
|
||||
* gets to be more than autovacuum_freeze_max_age transactions old.
|
||||
*
|
||||
* Note: guc.c ensures that autovacuum_freeze_max_age is in a sane
|
||||
* range, so that xidVacLimit will be well before xidWarnLimit.
|
||||
*
|
||||
* Note: autovacuum_freeze_max_age is a PGC_POSTMASTER parameter so that
|
||||
* we don't have to worry about dealing with on-the-fly changes in its
|
||||
* value. It doesn't look practical to update shared state from a GUC
|
||||
* assign hook (too many processes would try to execute the hook,
|
||||
* resulting in race conditions as well as crashes of those not
|
||||
* connected to shared memory). Perhaps this can be improved someday.
|
||||
*/
|
||||
xidVacLimit = oldest_datfrozenxid + autovacuum_freeze_max_age;
|
||||
if (xidVacLimit < FirstNormalTransactionId)
|
||||
xidVacLimit += FirstNormalTransactionId;
|
||||
|
||||
/* Grab lock for just long enough to set the new limit values */
|
||||
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
|
||||
ShmemVariableCache->oldestXid = oldest_datfrozenxid;
|
||||
ShmemVariableCache->xidVacLimit = xidVacLimit;
|
||||
ShmemVariableCache->xidWarnLimit = xidWarnLimit;
|
||||
ShmemVariableCache->xidStopLimit = xidStopLimit;
|
||||
ShmemVariableCache->xidWrapLimit = xidWrapLimit;
|
||||
@ -242,6 +276,18 @@ SetTransactionIdLimit(TransactionId oldest_datminxid,
|
||||
ereport(DEBUG1,
|
||||
(errmsg("transaction ID wrap limit is %u, limited by database \"%s\"",
|
||||
xidWrapLimit, NameStr(*oldest_datname))));
|
||||
|
||||
/*
|
||||
* If past the autovacuum force point, immediately signal an autovac
|
||||
* request. The reason for this is that autovac only processes one
|
||||
* database per invocation. Once it's finished cleaning up the oldest
|
||||
* database, it'll call here, and we'll signal the postmaster to start
|
||||
* another iteration immediately if there are still any old databases.
|
||||
*/
|
||||
if (TransactionIdFollowsOrEquals(curXid, xidVacLimit) &&
|
||||
IsUnderPostmaster)
|
||||
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC);
|
||||
|
||||
/* Give an immediate warning if past the wrap warn point */
|
||||
if (TransactionIdFollowsOrEquals(curXid, xidWarnLimit))
|
||||
ereport(WARNING,
|
||||
|
Reference in New Issue
Block a user