1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-14 08:21:07 +03:00

Replace implementation of pg_log as a relation accessed through the

buffer manager with 'pg_clog', a specialized access method modeled
on pg_xlog.  This simplifies startup (don't need to play games to
open pg_log; among other things, OverrideTransactionSystem goes away),
should improve performance a little, and opens the door to recycling
commit log space by removing no-longer-needed segments of the commit
log.  Actual recycling is not there yet, but I felt I should commit
this part separately since it'd still be useful if we chose not to
do transaction ID wraparound.
This commit is contained in:
Tom Lane
2001-08-25 18:52:43 +00:00
parent 4699d81dc9
commit 2589735da0
39 changed files with 1248 additions and 1128 deletions

View File

@ -1,14 +1,14 @@
/*-------------------------------------------------------------------------
*
* transam.c
* postgres transaction log/time interface routines
* postgres transaction log interface routines
*
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/transam/transam.c,v 1.46 2001/08/23 23:06:37 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/access/transam/transam.c,v 1.47 2001/08/25 18:52:41 tgl Exp $
*
* NOTES
* This file contains the high level access-method interface to the
@ -19,27 +19,14 @@
#include "postgres.h"
#include "access/heapam.h"
#include "access/clog.h"
#include "access/transam.h"
#include "catalog/catname.h"
#include "miscadmin.h"
static int RecoveryCheckingEnabled(void);
static void TransRecover(Relation logRelation);
static bool TransactionLogTest(TransactionId transactionId, XidStatus status);
static void TransactionLogUpdate(TransactionId transactionId,
XidStatus status);
/* ----------------
* global variables holding pointers to relations used
* by the transaction system. These are initialized by
* InitializeTransactionLog().
* ----------------
*/
Relation LogRelation = (Relation) NULL;
/* ----------------
* Single-item cache for results of TransactionLogTest.
* ----------------
@ -47,48 +34,12 @@ Relation LogRelation = (Relation) NULL;
static TransactionId cachedTestXid = InvalidTransactionId;
static XidStatus cachedTestXidStatus;
/* ----------------
* transaction recovery state variables
*
* When the transaction system is initialized, we may
* need to do recovery checking. This decision is decided
* by the postmaster or the user by supplying the backend
* with a special flag. In general, we want to do recovery
* checking whenever we are running without a postmaster
* or when the number of backends running under the postmaster
* goes from zero to one. -cim 3/21/90
* ----------------
*/
static int RecoveryCheckingEnableState = 0;
/* ----------------
* recovery checking accessors
* ----------------
*/
static int
RecoveryCheckingEnabled(void)
{
return RecoveryCheckingEnableState;
}
#ifdef NOT_USED
static void
SetRecoveryCheckingEnabled(bool state)
{
RecoveryCheckingEnableState = (state == true);
}
#endif
/* ----------------------------------------------------------------
* postgres log access method interface
*
* TransactionLogTest
* TransactionLogUpdate
* ========
* these functions do work for the interface
* functions - they search/retrieve and append/update
* information in the log and time relations.
* ----------------------------------------------------------------
*/
@ -102,59 +53,42 @@ static bool /* true/false: does transaction id have
TransactionLogTest(TransactionId transactionId, /* transaction id to test */
XidStatus status) /* transaction status */
{
BlockNumber blockNumber;
XidStatus xidstatus; /* recorded status of xid */
bool fail = false; /* success/failure */
/*
* during initialization consider all transactions as having been
* committed
*/
if (!RelationIsValid(LogRelation))
return (bool) (status == XID_COMMIT);
/*
* before going to the buffer manager, check our single item cache to
* Before going to the commit log manager, check our single item cache to
* see if we didn't just check the transaction status a moment ago.
*/
if (TransactionIdEquals(transactionId, cachedTestXid))
return (bool)
(status == cachedTestXidStatus);
return (status == cachedTestXidStatus);
/*
* compute the item pointer corresponding to the page containing our
* transaction id. We save the item in our cache to speed up things
* if we happen to ask for the same xid's status more than once.
* Also, check to see if the transaction ID is a permanent one.
*/
TransComputeBlockNumber(LogRelation, transactionId, &blockNumber);
xidstatus = TransBlockNumberGetXidStatus(LogRelation,
blockNumber,
transactionId,
&fail);
if (!fail)
if (! TransactionIdIsNormal(transactionId))
{
/*
* DO NOT cache status for transactions in unknown state !!!
*/
if (xidstatus == XID_COMMIT || xidstatus == XID_ABORT)
{
TransactionIdStore(transactionId, &cachedTestXid);
cachedTestXidStatus = xidstatus;
}
return (bool) (status == xidstatus);
if (TransactionIdEquals(transactionId, BootstrapTransactionId))
return (status == TRANSACTION_STATUS_COMMITTED);
if (TransactionIdEquals(transactionId, FrozenTransactionId))
return (status == TRANSACTION_STATUS_COMMITTED);
return (status == TRANSACTION_STATUS_ABORTED);
}
/*
* here the block didn't contain the information we wanted
* Get the status.
*/
elog(ERROR, "TransactionLogTest: failed to get xidstatus");
xidstatus = TransactionIdGetStatus(transactionId);
/*
* so lint is happy...
*/
return false;
/*
* DO NOT cache status for unfinished transactions!
*/
if (xidstatus != TRANSACTION_STATUS_IN_PROGRESS)
{
TransactionIdStore(transactionId, &cachedTestXid);
cachedTestXidStatus = xidstatus;
}
return (status == xidstatus);
}
/* --------------------------------
@ -165,24 +99,10 @@ static void
TransactionLogUpdate(TransactionId transactionId, /* trans id to update */
XidStatus status) /* new trans status */
{
BlockNumber blockNumber;
bool fail = false; /* success/failure */
/*
* during initialization we don't record any updates.
* update the commit log
*/
if (!RelationIsValid(LogRelation))
return;
/*
* update the log relation
*/
TransComputeBlockNumber(LogRelation, transactionId, &blockNumber);
TransBlockNumberSetXidStatus(LogRelation,
blockNumber,
transactionId,
status,
&fail);
TransactionIdSetStatus(transactionId, status);
/*
* update (invalidate) our single item TransactionLogTest cache.
@ -191,85 +111,21 @@ TransactionLogUpdate(TransactionId transactionId, /* trans id to update */
cachedTestXidStatus = status;
}
/* ----------------------------------------------------------------
* transaction recovery code
* ----------------------------------------------------------------
*/
/* --------------------------------
* TransRecover
* AmiTransactionOverride
*
* preform transaction recovery checking.
*
* Note: this should only be preformed if no other backends
* are running. This is known by the postmaster and
* conveyed by the postmaster passing a "do recovery checking"
* flag to the backend.
*
* here we get the last recorded transaction from the log,
* get the "last" and "next" transactions from the variable relation
* and then preform some integrity tests:
*
* 1) No transaction may exist higher then the "next" available
* transaction recorded in the variable relation. If this is the
* case then it means either the log or the variable relation
* has become corrupted.
*
* 2) The last committed transaction may not be higher then the
* next available transaction for the same reason.
*
* 3) The last recorded transaction may not be lower then the
* last committed transaction. (the reverse is ok - it means
* that some transactions have aborted since the last commit)
*
* Here is what the proper situation looks like. The line
* represents the data stored in the log. 'c' indicates the
* transaction was recorded as committed, 'a' indicates an
* abortted transaction and '.' represents information not
* recorded. These may correspond to in progress transactions.
*
* c c a c . . a . . . . . . . . . .
* | |
* last next
*
* Since "next" is only incremented by GetNewTransactionId() which
* is called when transactions are started. Hence if there
* are commits or aborts after "next", then it means we committed
* or aborted BEFORE we started the transaction. This is the
* rational behind constraint (1).
*
* Likewise, "last" should never greater then "next" for essentially
* the same reason - it would imply we committed before we started.
* This is the reasoning for (2).
*
* (3) implies we may never have a situation such as:
*
* c c a c . . a c . . . . . . . . .
* | |
* last next
*
* where there is a 'c' greater then "last".
*
* Recovery checking is more difficult in the case where
* several backends are executing concurrently because the
* transactions may be executing in the other backends.
* So, we only do recovery stuff when the backend is explicitly
* passed a flag on the command line.
* This function is used to manipulate the bootstrap flag.
* --------------------------------
*/
static void
TransRecover(Relation logRelation)
void
AmiTransactionOverride(bool flag)
{
AMI_OVERRIDE = flag;
}
/* ----------------------------------------------------------------
* Interface functions
*
* InitializeTransactionLog
* ========
* this function (called near cinit) initializes
* the transaction log, time and variable relations.
*
* TransactionId DidCommit
* TransactionId DidAbort
* TransactionId IsInProgress
@ -279,104 +135,13 @@ TransRecover(Relation logRelation)
*
* TransactionId Commit
* TransactionId Abort
* TransactionId SetInProgress
* ========
* these functions set the transaction status
* of the specified xid. TransactionIdCommit() also
* records the current time in the time relation
* and updates the variable relation counter.
* of the specified xid.
*
* ----------------------------------------------------------------
*/
/*
* InitializeTransactionLog
* Initializes transaction logging.
*/
void
InitializeTransactionLog(void)
{
Relation logRelation;
MemoryContext oldContext;
/*
* don't do anything during bootstrapping
*/
if (AMI_OVERRIDE)
return;
/*
* disable the transaction system so the access methods don't
* interfere during initialization.
*/
OverrideTransactionSystem(true);
/*
* make sure allocations occur within the top memory context so that
* our log management structures are protected from garbage collection
* at the end of every transaction.
*/
oldContext = MemoryContextSwitchTo(TopMemoryContext);
/*
* first open the log and time relations (these are created by amiint
* so they are guaranteed to exist)
*/
logRelation = heap_openr(LogRelationName, NoLock);
/*
* XXX TransactionLogUpdate requires that LogRelation is valid so we
* temporarily set it so we can initialize things properly. This could
* be done cleaner.
*/
LogRelation = logRelation;
/*
* if we have a virgin database, we initialize the log relation by
* committing the BootstrapTransactionId and we initialize the
* variable relation by setting the next available transaction id to
* FirstNormalTransactionId. OID initialization happens as a side
* effect of bootstrapping in varsup.c.
*/
SpinAcquire(OidGenLockId);
if (!TransactionIdDidCommit(BootstrapTransactionId))
{
TransactionLogUpdate(BootstrapTransactionId, XID_COMMIT);
Assert(!IsUnderPostmaster &&
TransactionIdEquals(ShmemVariableCache->nextXid,
FirstNormalTransactionId));
ShmemVariableCache->nextXid = FirstNormalTransactionId;
}
else if (RecoveryCheckingEnabled())
{
/*
* if we have a pre-initialized database and if the perform
* recovery checking flag was passed then we do our database
* integrity checking.
*/
TransRecover(logRelation);
}
LogRelation = (Relation) NULL;
SpinRelease(OidGenLockId);
/*
* now re-enable the transaction system
*/
OverrideTransactionSystem(false);
/*
* instantiate the global variables
*/
LogRelation = logRelation;
/*
* restore the memory context to the previous context before we return
* from initialization.
*/
MemoryContextSwitchTo(oldContext);
}
/* --------------------------------
* TransactionId DidCommit
* TransactionId DidAbort
@ -397,16 +162,15 @@ TransactionIdDidCommit(TransactionId transactionId)
if (AMI_OVERRIDE)
return true;
return TransactionLogTest(transactionId, XID_COMMIT);
return TransactionLogTest(transactionId, TRANSACTION_STATUS_COMMITTED);
}
/*
* TransactionIdDidAborted
* TransactionIdDidAbort
* True iff transaction associated with the identifier did abort.
*
* Note:
* Assumes transaction identifier is valid.
* XXX Is this unneeded?
*/
bool /* true if given transaction aborted */
TransactionIdDidAbort(TransactionId transactionId)
@ -414,7 +178,7 @@ TransactionIdDidAbort(TransactionId transactionId)
if (AMI_OVERRIDE)
return false;
return TransactionLogTest(transactionId, XID_ABORT);
return TransactionLogTest(transactionId, TRANSACTION_STATUS_ABORTED);
}
/*
@ -422,22 +186,22 @@ TransactionIdDidAbort(TransactionId transactionId)
* PROC structures of all running backend. - vadim 11/26/96
*
* Old comments:
* true if given transaction neither committed nor aborted
* true if given transaction has neither committed nor aborted
*/
#ifdef NOT_USED
bool
TransactionIdIsInProgress(TransactionId transactionId)
{
if (AMI_OVERRIDE)
return false;
return TransactionLogTest(transactionId, XID_INPROGRESS);
return TransactionLogTest(transactionId, TRANSACTION_STATUS_IN_PROGRESS);
}
*/
#endif /* NOT_USED */
/* --------------------------------
* TransactionId Commit
* TransactionId Abort
* TransactionId SetInProgress
* --------------------------------
*/
@ -454,7 +218,7 @@ TransactionIdCommit(TransactionId transactionId)
if (AMI_OVERRIDE)
return;
TransactionLogUpdate(transactionId, XID_COMMIT);
TransactionLogUpdate(transactionId, TRANSACTION_STATUS_COMMITTED);
}
/*
@ -470,5 +234,5 @@ TransactionIdAbort(TransactionId transactionId)
if (AMI_OVERRIDE)
return;
TransactionLogUpdate(transactionId, XID_ABORT);
TransactionLogUpdate(transactionId, TRANSACTION_STATUS_ABORTED);
}