diff options
Diffstat (limited to 'src/backend/access/transam/varsup.c')
-rw-r--r-- | src/backend/access/transam/varsup.c | 82 |
1 files changed, 64 insertions, 18 deletions
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c index 04e9840cb5d..a8abaaea35c 100644 --- a/src/backend/access/transam/varsup.c +++ b/src/backend/access/transam/varsup.c @@ -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, |