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:
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user