postgresql/src/backend/access/transam/varsup.c

335 lines
11 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* varsup.c
2000-11-30 09:46:26 +01:00
* postgres OID & XID variables support routines
*
2005-01-01 06:43:09 +01:00
* Copyright (c) 2000-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.62 2005/02/20 21:46:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
2000-11-30 09:46:26 +01:00
#include "postgres.h"
1996-10-21 09:15:18 +02:00
#include "access/clog.h"
#include "access/subtrans.h"
2000-11-30 09:46:26 +01:00
#include "access/transam.h"
#include "miscadmin.h"
#include "storage/ipc.h"
#include "storage/proc.h"
#include "utils/builtins.h"
/* Number of OIDs to prefetch (preallocate) per XLOG write */
XLOG (and related) changes: * Store two past checkpoint locations, not just one, in pg_control. On startup, we fall back to the older checkpoint if the newer one is unreadable. Also, a physical copy of the newest checkpoint record is kept in pg_control for possible use in disaster recovery (ie, complete loss of pg_xlog). Also add a version number for pg_control itself. Remove archdir from pg_control; it ought to be a GUC parameter, not a special case (not that it's implemented yet anyway). * Suppress successive checkpoint records when nothing has been entered in the WAL log since the last one. This is not so much to avoid I/O as to make it actually useful to keep track of the last two checkpoints. If the things are right next to each other then there's not a lot of redundancy gained... * Change CRC scheme to a true 64-bit CRC, not a pair of 32-bit CRCs on alternate bytes. Polynomial borrowed from ECMA DLT1 standard. * Fix XLOG record length handling so that it will work at BLCKSZ = 32k. * Change XID allocation to work more like OID allocation. (This is of dubious necessity, but I think it's a good idea anyway.) * Fix a number of minor bugs, such as off-by-one logic for XLOG file wraparound at the 4 gig mark. * Add documentation and clean up some coding infelicities; move file format declarations out to include files where planned contrib utilities can get at them. * Checkpoint will now occur every CHECKPOINT_SEGMENTS log segments or every CHECKPOINT_TIMEOUT seconds, whichever comes first. It is also possible to force a checkpoint by sending SIGUSR1 to the postmaster (undocumented feature...) * Defend against kill -9 postmaster by storing shmem block's key and ID in postmaster.pid lockfile, and checking at startup to ensure that no processes are still connected to old shmem block (if it still exists). * Switch backends to accept SIGQUIT rather than SIGUSR1 for emergency stop, for symmetry with postmaster and xlog utilities. Clean up signal handling in bootstrap.c so that xlog utilities launched by postmaster will react to signals better. * Standalone bootstrap now grabs lockfile in target directory, as added insurance against running it in parallel with live postmaster.
2001-03-13 02:17:06 +01:00
#define VAR_OID_PREFETCH 8192
2000-11-30 09:46:26 +01:00
/* pointer to "variable cache" in shared memory (set up by shmem.c) */
VariableCache ShmemVariableCache = NULL;
/*
* Allocate the next XID for my new transaction.
*/
TransactionId
GetNewTransactionId(bool isSubXact)
{
TransactionId xid;
2000-11-30 09:46:26 +01:00
/*
2001-03-22 05:01:46 +01:00
* During bootstrap initialization, we return the special bootstrap
* transaction id.
*/
if (IsBootstrapProcessingMode())
return BootstrapTransactionId;
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
XLOG (and related) changes: * Store two past checkpoint locations, not just one, in pg_control. On startup, we fall back to the older checkpoint if the newer one is unreadable. Also, a physical copy of the newest checkpoint record is kept in pg_control for possible use in disaster recovery (ie, complete loss of pg_xlog). Also add a version number for pg_control itself. Remove archdir from pg_control; it ought to be a GUC parameter, not a special case (not that it's implemented yet anyway). * Suppress successive checkpoint records when nothing has been entered in the WAL log since the last one. This is not so much to avoid I/O as to make it actually useful to keep track of the last two checkpoints. If the things are right next to each other then there's not a lot of redundancy gained... * Change CRC scheme to a true 64-bit CRC, not a pair of 32-bit CRCs on alternate bytes. Polynomial borrowed from ECMA DLT1 standard. * Fix XLOG record length handling so that it will work at BLCKSZ = 32k. * Change XID allocation to work more like OID allocation. (This is of dubious necessity, but I think it's a good idea anyway.) * Fix a number of minor bugs, such as off-by-one logic for XLOG file wraparound at the 4 gig mark. * Add documentation and clean up some coding infelicities; move file format declarations out to include files where planned contrib utilities can get at them. * Checkpoint will now occur every CHECKPOINT_SEGMENTS log segments or every CHECKPOINT_TIMEOUT seconds, whichever comes first. It is also possible to force a checkpoint by sending SIGUSR1 to the postmaster (undocumented feature...) * Defend against kill -9 postmaster by storing shmem block's key and ID in postmaster.pid lockfile, and checking at startup to ensure that no processes are still connected to old shmem block (if it still exists). * Switch backends to accept SIGQUIT rather than SIGUSR1 for emergency stop, for symmetry with postmaster and xlog utilities. Clean up signal handling in bootstrap.c so that xlog utilities launched by postmaster will react to signals better. * Standalone bootstrap now grabs lockfile in target directory, as added insurance against running it in parallel with live postmaster.
2001-03-13 02:17:06 +01:00
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).
*
* 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.
*/
if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidWarnLimit) &&
TransactionIdIsValid(ShmemVariableCache->xidWarnLimit))
{
if (IsUnderPostmaster &&
TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidStopLimit))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("database is shut down to avoid wraparound data loss in database \"%s\"",
NameStr(ShmemVariableCache->limit_datname)),
errhint("Stop the postmaster and use a standalone backend to VACUUM in \"%s\".",
NameStr(ShmemVariableCache->limit_datname))));
else
ereport(WARNING,
(errmsg("database \"%s\" must be vacuumed within %u transactions",
NameStr(ShmemVariableCache->limit_datname),
ShmemVariableCache->xidWrapLimit - xid),
errhint("To avoid a database shutdown, execute a full-database VACUUM in \"%s\".",
NameStr(ShmemVariableCache->limit_datname))));
}
/*
2004-08-29 07:07:03 +02:00
* If we are allocating the first XID of a new page of the commit log,
* zero out that commit-log page before returning. We must do this
* while holding XidGenLock, else another xact could acquire and
* commit a later XID before we zero the page. Fortunately, a page of
* the commit log holds 32K or more transactions, so we don't have to
* do this very often.
*
* Extend pg_subtrans too.
*/
ExtendCLOG(xid);
ExtendSUBTRANS(xid);
/*
* Now advance the nextXid counter. This must not happen until after
2004-08-29 07:07:03 +02:00
* we have successfully completed ExtendCLOG() --- if that routine
* fails, we want the next incoming transaction to try it again. We
* cannot assign more XIDs until there is CLOG space for them.
*/
TransactionIdAdvance(ShmemVariableCache->nextXid);
/*
2004-08-29 07:07:03 +02:00
* We must store the new XID into the shared PGPROC array before
* releasing XidGenLock. This ensures that when GetSnapshotData calls
* ReadNewTransactionId, all active XIDs before the returned value of
2004-08-29 07:07:03 +02:00
* nextXid are already present in PGPROC. Else we have a race
* condition.
*
* XXX by storing xid into MyProc without acquiring SInvalLock, we are
* relying on fetch/store of an xid to be atomic, else other backends
* might see a partially-set xid here. But holding both locks at once
* would be a nasty concurrency hit (and in fact could cause a
* deadlock against GetSnapshotData). So for now, assume atomicity.
2002-09-04 22:31:48 +02:00
* Note that readers of PGPROC xid field should be careful to fetch
* the value only once, rather than assume they can read it multiple
* times and get the same answer each time.
*
* The same comments apply to the subxact xid count and overflow fields.
*
2002-09-04 22:31:48 +02:00
* A solution to the atomic-store problem would be to give each PGPROC
* its own spinlock used only for fetching/storing that PGPROC's xid
2004-08-29 07:07:03 +02:00
* and related fields. (SInvalLock would then mean primarily that
* PGPROCs couldn't be added/removed while holding the lock.)
*
* If there's no room to fit a subtransaction XID into PGPROC, set the
* cache-overflowed flag instead. This forces readers to look in
2004-08-29 07:07:03 +02:00
* pg_subtrans to map subtransaction XIDs up to top-level XIDs. There
* is a race-condition window, in that the new XID will not appear as
* running until its parent link has been placed into pg_subtrans.
* However, that will happen before anyone could possibly have a
* reason to inquire about the status of the XID, so it seems OK.
* (Snapshots taken during this window *will* include the parent XID,
* so they will deliver the correct answer later on when someone does
* have a reason to inquire.)
*/
if (MyProc != NULL)
{
if (!isSubXact)
MyProc->xid = xid;
else
{
if (MyProc->subxids.nxids < PGPROC_MAX_CACHED_SUBXIDS)
{
MyProc->subxids.xids[MyProc->subxids.nxids] = xid;
MyProc->subxids.nxids++;
}
else
MyProc->subxids.overflowed = true;
}
}
LWLockRelease(XidGenLock);
return xid;
}
/*
XLOG (and related) changes: * Store two past checkpoint locations, not just one, in pg_control. On startup, we fall back to the older checkpoint if the newer one is unreadable. Also, a physical copy of the newest checkpoint record is kept in pg_control for possible use in disaster recovery (ie, complete loss of pg_xlog). Also add a version number for pg_control itself. Remove archdir from pg_control; it ought to be a GUC parameter, not a special case (not that it's implemented yet anyway). * Suppress successive checkpoint records when nothing has been entered in the WAL log since the last one. This is not so much to avoid I/O as to make it actually useful to keep track of the last two checkpoints. If the things are right next to each other then there's not a lot of redundancy gained... * Change CRC scheme to a true 64-bit CRC, not a pair of 32-bit CRCs on alternate bytes. Polynomial borrowed from ECMA DLT1 standard. * Fix XLOG record length handling so that it will work at BLCKSZ = 32k. * Change XID allocation to work more like OID allocation. (This is of dubious necessity, but I think it's a good idea anyway.) * Fix a number of minor bugs, such as off-by-one logic for XLOG file wraparound at the 4 gig mark. * Add documentation and clean up some coding infelicities; move file format declarations out to include files where planned contrib utilities can get at them. * Checkpoint will now occur every CHECKPOINT_SEGMENTS log segments or every CHECKPOINT_TIMEOUT seconds, whichever comes first. It is also possible to force a checkpoint by sending SIGUSR1 to the postmaster (undocumented feature...) * Defend against kill -9 postmaster by storing shmem block's key and ID in postmaster.pid lockfile, and checking at startup to ensure that no processes are still connected to old shmem block (if it still exists). * Switch backends to accept SIGQUIT rather than SIGUSR1 for emergency stop, for symmetry with postmaster and xlog utilities. Clean up signal handling in bootstrap.c so that xlog utilities launched by postmaster will react to signals better. * Standalone bootstrap now grabs lockfile in target directory, as added insurance against running it in parallel with live postmaster.
2001-03-13 02:17:06 +01:00
* Read nextXid but don't allocate it.
*/
TransactionId
ReadNewTransactionId(void)
{
TransactionId xid;
LWLockAcquire(XidGenLock, LW_SHARED);
xid = ShmemVariableCache->nextXid;
LWLockRelease(XidGenLock);
return xid;
}
/*
* Determine the last safe XID to allocate given the currently oldest
* datfrozenxid (ie, the oldest XID that might exist in any database
* of our cluster).
*/
void
SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
Name oldest_datname)
{
TransactionId xidWarnLimit;
TransactionId xidStopLimit;
TransactionId xidWrapLimit;
TransactionId curXid;
Assert(TransactionIdIsValid(oldest_datfrozenxid));
/*
* The place where we actually get into deep trouble is halfway around
* 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_datfrozenxid + (MaxTransactionId >> 1);
if (xidWrapLimit < FirstNormalTransactionId)
xidWrapLimit += FirstNormalTransactionId;
/*
* We'll refuse to continue assigning XIDs in interactive mode once
* we get within 1M transactions of data loss. This leaves lots
* of room for the DBA to fool around fixing things in a standalone
* backend, while not being significant compared to total XID space.
* (Note that since vacuuming requires one transaction per table
* cleaned, we had better be sure there's lots of XIDs left...)
*/
xidStopLimit = xidWrapLimit - 1000000;
if (xidStopLimit < FirstNormalTransactionId)
xidStopLimit -= FirstNormalTransactionId;
/*
* We'll start complaining loudly when we get within 10M transactions
* of the stop point. This is kind of arbitrary, but if you let your
* gas gauge get down to 1% of full, would you be looking for the
* next gas station? We need to be fairly liberal about this number
* because there are lots of scenarios where most transactions are
* done by automatic clients that won't pay attention to warnings.
* (No, we're not gonna make this configurable. If you know enough to
* configure it, you know enough to not get in this kind of trouble in
* the first place.)
*/
xidWarnLimit = xidStopLimit - 10000000;
if (xidWarnLimit < FirstNormalTransactionId)
xidWarnLimit -= FirstNormalTransactionId;
/* Grab lock for just long enough to set the new limit values */
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
ShmemVariableCache->xidWarnLimit = xidWarnLimit;
ShmemVariableCache->xidStopLimit = xidStopLimit;
ShmemVariableCache->xidWrapLimit = xidWrapLimit;
namecpy(&ShmemVariableCache->limit_datname, oldest_datname);
curXid = ShmemVariableCache->nextXid;
LWLockRelease(XidGenLock);
/* Log the info */
ereport(LOG,
(errmsg("transaction ID wrap limit is %u, limited by database \"%s\"",
xidWrapLimit, NameStr(*oldest_datname))));
/* Give an immediate warning if past the wrap warn point */
if (TransactionIdFollowsOrEquals(curXid, xidWarnLimit))
ereport(WARNING,
(errmsg("database \"%s\" must be vacuumed within %u transactions",
NameStr(*oldest_datname),
xidWrapLimit - curXid),
errhint("To avoid a database shutdown, execute a full-database VACUUM in \"%s\".",
NameStr(*oldest_datname))));
}
/* ----------------------------------------------------------------
* object id generation support
* ----------------------------------------------------------------
*/
2001-03-22 05:01:46 +01:00
static Oid lastSeenOid = InvalidOid;
Oid
GetNewObjectId(void)
{
Oid result;
LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
/*
* Check for wraparound of the OID counter. We *must* not return 0
* (InvalidOid); and as long as we have to check that, it seems a good
* idea to skip over everything below BootstrapObjectIdData too. (This
* basically just reduces the odds of OID collision right after a wrap
* occurs.) Note we are relying on unsigned comparison here.
*/
if (ShmemVariableCache->nextOid < ((Oid) BootstrapObjectIdData))
{
ShmemVariableCache->nextOid = BootstrapObjectIdData;
ShmemVariableCache->oidCount = 0;
}
XLOG (and related) changes: * Store two past checkpoint locations, not just one, in pg_control. On startup, we fall back to the older checkpoint if the newer one is unreadable. Also, a physical copy of the newest checkpoint record is kept in pg_control for possible use in disaster recovery (ie, complete loss of pg_xlog). Also add a version number for pg_control itself. Remove archdir from pg_control; it ought to be a GUC parameter, not a special case (not that it's implemented yet anyway). * Suppress successive checkpoint records when nothing has been entered in the WAL log since the last one. This is not so much to avoid I/O as to make it actually useful to keep track of the last two checkpoints. If the things are right next to each other then there's not a lot of redundancy gained... * Change CRC scheme to a true 64-bit CRC, not a pair of 32-bit CRCs on alternate bytes. Polynomial borrowed from ECMA DLT1 standard. * Fix XLOG record length handling so that it will work at BLCKSZ = 32k. * Change XID allocation to work more like OID allocation. (This is of dubious necessity, but I think it's a good idea anyway.) * Fix a number of minor bugs, such as off-by-one logic for XLOG file wraparound at the 4 gig mark. * Add documentation and clean up some coding infelicities; move file format declarations out to include files where planned contrib utilities can get at them. * Checkpoint will now occur every CHECKPOINT_SEGMENTS log segments or every CHECKPOINT_TIMEOUT seconds, whichever comes first. It is also possible to force a checkpoint by sending SIGUSR1 to the postmaster (undocumented feature...) * Defend against kill -9 postmaster by storing shmem block's key and ID in postmaster.pid lockfile, and checking at startup to ensure that no processes are still connected to old shmem block (if it still exists). * Switch backends to accept SIGQUIT rather than SIGUSR1 for emergency stop, for symmetry with postmaster and xlog utilities. Clean up signal handling in bootstrap.c so that xlog utilities launched by postmaster will react to signals better. * Standalone bootstrap now grabs lockfile in target directory, as added insurance against running it in parallel with live postmaster.
2001-03-13 02:17:06 +01:00
/* If we run out of logged for use oids then we must log more */
2000-11-30 09:46:26 +01:00
if (ShmemVariableCache->oidCount == 0)
{
2000-11-30 09:46:26 +01:00
XLogPutNextOid(ShmemVariableCache->nextOid + VAR_OID_PREFETCH);
ShmemVariableCache->oidCount = VAR_OID_PREFETCH;
}
result = ShmemVariableCache->nextOid;
2000-11-30 09:46:26 +01:00
(ShmemVariableCache->nextOid)++;
(ShmemVariableCache->oidCount)--;
LWLockRelease(OidGenLock);
lastSeenOid = result;
return result;
}
void
CheckMaxObjectId(Oid assigned_oid)
{
2000-11-30 09:46:26 +01:00
if (lastSeenOid != InvalidOid && assigned_oid < lastSeenOid)
return;
LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
XLOG (and related) changes: * Store two past checkpoint locations, not just one, in pg_control. On startup, we fall back to the older checkpoint if the newer one is unreadable. Also, a physical copy of the newest checkpoint record is kept in pg_control for possible use in disaster recovery (ie, complete loss of pg_xlog). Also add a version number for pg_control itself. Remove archdir from pg_control; it ought to be a GUC parameter, not a special case (not that it's implemented yet anyway). * Suppress successive checkpoint records when nothing has been entered in the WAL log since the last one. This is not so much to avoid I/O as to make it actually useful to keep track of the last two checkpoints. If the things are right next to each other then there's not a lot of redundancy gained... * Change CRC scheme to a true 64-bit CRC, not a pair of 32-bit CRCs on alternate bytes. Polynomial borrowed from ECMA DLT1 standard. * Fix XLOG record length handling so that it will work at BLCKSZ = 32k. * Change XID allocation to work more like OID allocation. (This is of dubious necessity, but I think it's a good idea anyway.) * Fix a number of minor bugs, such as off-by-one logic for XLOG file wraparound at the 4 gig mark. * Add documentation and clean up some coding infelicities; move file format declarations out to include files where planned contrib utilities can get at them. * Checkpoint will now occur every CHECKPOINT_SEGMENTS log segments or every CHECKPOINT_TIMEOUT seconds, whichever comes first. It is also possible to force a checkpoint by sending SIGUSR1 to the postmaster (undocumented feature...) * Defend against kill -9 postmaster by storing shmem block's key and ID in postmaster.pid lockfile, and checking at startup to ensure that no processes are still connected to old shmem block (if it still exists). * Switch backends to accept SIGQUIT rather than SIGUSR1 for emergency stop, for symmetry with postmaster and xlog utilities. Clean up signal handling in bootstrap.c so that xlog utilities launched by postmaster will react to signals better. * Standalone bootstrap now grabs lockfile in target directory, as added insurance against running it in parallel with live postmaster.
2001-03-13 02:17:06 +01:00
2000-11-30 09:46:26 +01:00
if (assigned_oid < ShmemVariableCache->nextOid)
{
lastSeenOid = ShmemVariableCache->nextOid - 1;
LWLockRelease(OidGenLock);
2000-11-30 09:46:26 +01:00
return;
}
2000-11-30 09:46:26 +01:00
/* If we are in the logged oid range, just bump nextOid up */
2001-03-22 05:01:46 +01:00
if (assigned_oid <= ShmemVariableCache->nextOid +
ShmemVariableCache->oidCount - 1)
{
2001-03-22 05:01:46 +01:00
ShmemVariableCache->oidCount -=
2000-11-30 09:46:26 +01:00
assigned_oid - ShmemVariableCache->nextOid + 1;
ShmemVariableCache->nextOid = assigned_oid + 1;
LWLockRelease(OidGenLock);
return;
}
2000-11-30 09:46:26 +01:00
/*
2001-03-22 05:01:46 +01:00
* We have exceeded the logged oid range. We should lock the database
* and kill all other backends but we are loading oid's that we can
* not guarantee are unique anyway, so we must rely on the user.
*/
2000-11-30 09:46:26 +01:00
XLogPutNextOid(assigned_oid + VAR_OID_PREFETCH);
ShmemVariableCache->nextOid = assigned_oid + 1;
ShmemVariableCache->oidCount = VAR_OID_PREFETCH - 1;
2000-11-30 09:46:26 +01:00
LWLockRelease(OidGenLock);
}