mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
Rework subtransaction commit protocol for hot standby.
This patch eliminates the marking of subtransactions as SUBCOMMITTED in pg_clog during their commit; instead they remain in-progress until main transaction commit. At main transaction commit, the commit protocol is atomic-by-page instead of one transaction at a time. To avoid a race condition with some subtransactions appearing committed before others in the case where they span more than one pg_clog page, we conserve the logic that marks them subcommitted before marking the parent committed. Simon Riggs with minor help from me
This commit is contained in:
parent
3afffbc902
commit
06da3c570f
@ -1,4 +1,4 @@
|
|||||||
$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.11 2008/03/21 13:23:28 momjian Exp $
|
$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.12 2008/10/20 19:18:18 alvherre Exp $
|
||||||
|
|
||||||
The Transaction System
|
The Transaction System
|
||||||
======================
|
======================
|
||||||
@ -341,11 +341,20 @@ from disk. They also allow information to be permanent across server restarts.
|
|||||||
pg_clog records the commit status for each transaction that has been assigned
|
pg_clog records the commit status for each transaction that has been assigned
|
||||||
an XID. A transaction can be in progress, committed, aborted, or
|
an XID. A transaction can be in progress, committed, aborted, or
|
||||||
"sub-committed". This last state means that it's a subtransaction that's no
|
"sub-committed". This last state means that it's a subtransaction that's no
|
||||||
longer running, but its parent has not updated its state yet (either it is
|
longer running, but its parent has not updated its state yet. It is not
|
||||||
still running, or the backend crashed without updating its status). A
|
necessary to update a subtransaction's transaction status to subcommit, so we
|
||||||
sub-committed transaction's status will be updated again to the final value as
|
can just defer it until main transaction commit. The main role of marking
|
||||||
soon as the parent commits or aborts, or when the parent is detected to be
|
transactions as sub-committed is to provide an atomic commit protocol when
|
||||||
aborted.
|
transaction status is spread across multiple clog pages. As a result, whenever
|
||||||
|
transaction status spreads across multiple pages we must use a two-phase commit
|
||||||
|
protocol: the first phase is to mark the subtransactions as sub-committed, then
|
||||||
|
we mark the top level transaction and all its subtransactions committed (in
|
||||||
|
that order). Thus, subtransactions that have not aborted appear as in-progress
|
||||||
|
even when they have already finished, and the subcommit status appears as a
|
||||||
|
very short transitory state during main transaction commit. Subtransaction
|
||||||
|
abort is always marked in clog as soon as it occurs. When the transaction
|
||||||
|
status all fit in a single CLOG page, we atomically mark them all as committed
|
||||||
|
without bothering with the intermediate sub-commit state.
|
||||||
|
|
||||||
Savepoints are implemented using subtransactions. A subtransaction is a
|
Savepoints are implemented using subtransactions. A subtransaction is a
|
||||||
transaction inside a transaction; its commit or abort status is not only
|
transaction inside a transaction; its commit or abort status is not only
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/clog.c,v 1.47 2008/08/01 13:16:08 alvherre Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/clog.c,v 1.48 2008/10/20 19:18:18 alvherre Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -80,32 +80,182 @@ static int ZeroCLOGPage(int pageno, bool writeXlog);
|
|||||||
static bool CLOGPagePrecedes(int page1, int page2);
|
static bool CLOGPagePrecedes(int page1, int page2);
|
||||||
static void WriteZeroPageXlogRec(int pageno);
|
static void WriteZeroPageXlogRec(int pageno);
|
||||||
static void WriteTruncateXlogRec(int pageno);
|
static void WriteTruncateXlogRec(int pageno);
|
||||||
|
static void TransactionIdSetPageStatus(TransactionId xid, int nsubxids,
|
||||||
|
TransactionId *subxids, XidStatus status,
|
||||||
|
XLogRecPtr lsn, int pageno);
|
||||||
|
static void TransactionIdSetStatusBit(TransactionId xid, XidStatus status,
|
||||||
|
XLogRecPtr lsn, int slotno);
|
||||||
|
static void set_status_by_pages(int nsubxids, TransactionId *subxids,
|
||||||
|
XidStatus status, XLogRecPtr lsn);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Record the final state of a transaction in the commit log.
|
* TransactionIdSetTreeStatus
|
||||||
|
*
|
||||||
|
* Record the final state of transaction entries in the commit log for
|
||||||
|
* a transaction and its subtransaction tree. Take care to ensure this is
|
||||||
|
* efficient, and as atomic as possible.
|
||||||
|
*
|
||||||
|
* xid is a single xid to set status for. This will typically be
|
||||||
|
* the top level transactionid for a top level commit or abort. It can
|
||||||
|
* also be a subtransaction when we record transaction aborts.
|
||||||
|
*
|
||||||
|
* subxids is an array of xids of length nsubxids, representing subtransactions
|
||||||
|
* in the tree of xid. In various cases nsubxids may be zero.
|
||||||
*
|
*
|
||||||
* lsn must be the WAL location of the commit record when recording an async
|
* lsn must be the WAL location of the commit record when recording an async
|
||||||
* commit. For a synchronous commit it can be InvalidXLogRecPtr, since the
|
* commit. For a synchronous commit it can be InvalidXLogRecPtr, since the
|
||||||
* caller guarantees the commit record is already flushed in that case. It
|
* caller guarantees the commit record is already flushed in that case. It
|
||||||
* should be InvalidXLogRecPtr for abort cases, too.
|
* should be InvalidXLogRecPtr for abort cases, too.
|
||||||
*
|
*
|
||||||
|
* In the commit case, atomicity is limited by whether all the subxids are in
|
||||||
|
* the same CLOG page as xid. If they all are, then the lock will be grabbed
|
||||||
|
* only once, and the status will be set to committed directly. Otherwise
|
||||||
|
* we must
|
||||||
|
* 1. set sub-committed all subxids that are not on the same page as the
|
||||||
|
* main xid
|
||||||
|
* 2. atomically set committed the main xid and the subxids on the same page
|
||||||
|
* 3. go over the first bunch again and set them committed
|
||||||
|
* Note that as far as concurrent checkers are concerned, main transaction
|
||||||
|
* commit as a whole is still atomic.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* TransactionId t commits and has subxids t1, t2, t3, t4
|
||||||
|
* t is on page p1, t1 is also on p1, t2 and t3 are on p2, t4 is on p3
|
||||||
|
* 1. update pages2-3:
|
||||||
|
* page2: set t2,t3 as sub-committed
|
||||||
|
* page3: set t4 as sub-committed
|
||||||
|
* 2. update page1:
|
||||||
|
* set t1 as sub-committed,
|
||||||
|
* then set t as committed,
|
||||||
|
then set t1 as committed
|
||||||
|
* 3. update pages2-3:
|
||||||
|
* page2: set t2,t3 as committed
|
||||||
|
* page3: set t4 as committed
|
||||||
|
*
|
||||||
* NB: this is a low-level routine and is NOT the preferred entry point
|
* NB: this is a low-level routine and is NOT the preferred entry point
|
||||||
* for most uses; TransactionLogUpdate() in transam.c is the intended caller.
|
* for most uses; functions in transam.c are the intended callers.
|
||||||
|
*
|
||||||
|
* XXX Think about issuing FADVISE_WILLNEED on pages that we will need,
|
||||||
|
* but aren't yet in cache, as well as hinting pages not to fall out of
|
||||||
|
* cache yet.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TransactionIdSetStatus(TransactionId xid, XidStatus status, XLogRecPtr lsn)
|
TransactionIdSetTreeStatus(TransactionId xid, int nsubxids,
|
||||||
|
TransactionId *subxids, XidStatus status, XLogRecPtr lsn)
|
||||||
|
{
|
||||||
|
int pageno = TransactionIdToPage(xid); /* get page of parent */
|
||||||
|
int i;
|
||||||
|
|
||||||
|
Assert(status == TRANSACTION_STATUS_COMMITTED ||
|
||||||
|
status == TRANSACTION_STATUS_ABORTED);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See how many subxids, if any, are on the same page as the parent, if any.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < nsubxids; i++)
|
||||||
|
{
|
||||||
|
if (TransactionIdToPage(subxids[i]) != pageno)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do all items fit on a single page?
|
||||||
|
*/
|
||||||
|
if (i == nsubxids)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Set the parent and all subtransactions in a single call
|
||||||
|
*/
|
||||||
|
TransactionIdSetPageStatus(xid, nsubxids, subxids, status, lsn,
|
||||||
|
pageno);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int nsubxids_on_first_page = i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this is a commit then we care about doing this correctly (i.e.
|
||||||
|
* using the subcommitted intermediate status). By here, we know we're
|
||||||
|
* updating more than one page of clog, so we must mark entries that
|
||||||
|
* are *not* on the first page so that they show as subcommitted before
|
||||||
|
* we then return to update the status to fully committed.
|
||||||
|
*
|
||||||
|
* To avoid touching the first page twice, skip marking subcommitted
|
||||||
|
* for the subxids on that first page.
|
||||||
|
*/
|
||||||
|
if (status == TRANSACTION_STATUS_COMMITTED)
|
||||||
|
set_status_by_pages(nsubxids - nsubxids_on_first_page,
|
||||||
|
subxids + nsubxids_on_first_page,
|
||||||
|
TRANSACTION_STATUS_SUB_COMMITTED, lsn);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now set the parent and subtransactions on same page as the parent,
|
||||||
|
* if any
|
||||||
|
*/
|
||||||
|
pageno = TransactionIdToPage(xid);
|
||||||
|
TransactionIdSetPageStatus(xid, nsubxids_on_first_page, subxids, status,
|
||||||
|
lsn, pageno);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now work through the rest of the subxids one clog page at a time,
|
||||||
|
* starting from the second page onwards, like we did above.
|
||||||
|
*/
|
||||||
|
set_status_by_pages(nsubxids - nsubxids_on_first_page,
|
||||||
|
subxids + nsubxids_on_first_page,
|
||||||
|
status, lsn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper for TransactionIdSetTreeStatus: set the status for a bunch of
|
||||||
|
* transactions, chunking in the separate CLOG pages involved. We never
|
||||||
|
* pass the whole transaction tree to this function, only subtransactions
|
||||||
|
* that are on different pages to the top level transaction id.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
set_status_by_pages(int nsubxids, TransactionId *subxids,
|
||||||
|
XidStatus status, XLogRecPtr lsn)
|
||||||
|
{
|
||||||
|
int pageno = TransactionIdToPage(subxids[0]);
|
||||||
|
int offset = 0;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (i < nsubxids)
|
||||||
|
{
|
||||||
|
int num_on_page = 0;
|
||||||
|
|
||||||
|
while (TransactionIdToPage(subxids[i]) == pageno && i < nsubxids)
|
||||||
|
{
|
||||||
|
num_on_page++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
TransactionIdSetPageStatus(InvalidTransactionId,
|
||||||
|
num_on_page, subxids + offset,
|
||||||
|
status, lsn, pageno);
|
||||||
|
offset = i;
|
||||||
|
pageno = TransactionIdToPage(subxids[offset]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Record the final state of transaction entries in the commit log for
|
||||||
|
* all entries on a single page. Atomic only on this page.
|
||||||
|
*
|
||||||
|
* Otherwise API is same as TransactionIdSetTreeStatus()
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
TransactionIdSetPageStatus(TransactionId xid, int nsubxids,
|
||||||
|
TransactionId *subxids, XidStatus status,
|
||||||
|
XLogRecPtr lsn, int pageno)
|
||||||
{
|
{
|
||||||
int pageno = TransactionIdToPage(xid);
|
|
||||||
int byteno = TransactionIdToByte(xid);
|
|
||||||
int bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
|
|
||||||
int slotno;
|
int slotno;
|
||||||
char *byteptr;
|
int i;
|
||||||
char byteval;
|
|
||||||
|
|
||||||
Assert(status == TRANSACTION_STATUS_COMMITTED ||
|
Assert(status == TRANSACTION_STATUS_COMMITTED ||
|
||||||
status == TRANSACTION_STATUS_ABORTED ||
|
status == TRANSACTION_STATUS_ABORTED ||
|
||||||
status == TRANSACTION_STATUS_SUB_COMMITTED);
|
(status == TRANSACTION_STATUS_SUB_COMMITTED && !TransactionIdIsValid(xid)));
|
||||||
|
|
||||||
LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
|
LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
|
||||||
|
|
||||||
@ -116,9 +266,62 @@ TransactionIdSetStatus(TransactionId xid, XidStatus status, XLogRecPtr lsn)
|
|||||||
* mustn't let it reach disk until we've done the appropriate WAL flush.
|
* mustn't let it reach disk until we've done the appropriate WAL flush.
|
||||||
* But when lsn is invalid, it's OK to scribble on a page while it is
|
* But when lsn is invalid, it's OK to scribble on a page while it is
|
||||||
* write-busy, since we don't care if the update reaches disk sooner than
|
* write-busy, since we don't care if the update reaches disk sooner than
|
||||||
* we think. Hence, pass write_ok = XLogRecPtrIsInvalid(lsn).
|
* we think.
|
||||||
*/
|
*/
|
||||||
slotno = SimpleLruReadPage(ClogCtl, pageno, XLogRecPtrIsInvalid(lsn), xid);
|
slotno = SimpleLruReadPage(ClogCtl, pageno, XLogRecPtrIsInvalid(lsn), xid);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the main transaction id, if any.
|
||||||
|
*
|
||||||
|
* If we update more than one xid on this page while it is being written
|
||||||
|
* out, we might find that some of the bits go to disk and others don't.
|
||||||
|
* If we are updating commits on the page with the top-level xid that could
|
||||||
|
* break atomicity, so we subcommit the subxids first before we mark the
|
||||||
|
* top-level commit.
|
||||||
|
*/
|
||||||
|
if (TransactionIdIsValid(xid))
|
||||||
|
{
|
||||||
|
/* Subtransactions first, if needed ... */
|
||||||
|
if (status == TRANSACTION_STATUS_COMMITTED)
|
||||||
|
{
|
||||||
|
for (i = 0; i < nsubxids; i++)
|
||||||
|
{
|
||||||
|
Assert(ClogCtl->shared->page_number[slotno] == TransactionIdToPage(subxids[i]));
|
||||||
|
TransactionIdSetStatusBit(subxids[i],
|
||||||
|
TRANSACTION_STATUS_SUB_COMMITTED,
|
||||||
|
lsn, slotno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ... then the main transaction */
|
||||||
|
TransactionIdSetStatusBit(xid, status, lsn, slotno);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the subtransactions */
|
||||||
|
for (i = 0; i < nsubxids; i++)
|
||||||
|
{
|
||||||
|
Assert(ClogCtl->shared->page_number[slotno] == TransactionIdToPage(subxids[i]));
|
||||||
|
TransactionIdSetStatusBit(subxids[i], status, lsn, slotno);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClogCtl->shared->page_dirty[slotno] = true;
|
||||||
|
|
||||||
|
LWLockRelease(CLogControlLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets the commit status of a single transaction.
|
||||||
|
*
|
||||||
|
* Must be called with CLogControlLock held
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
TransactionIdSetStatusBit(TransactionId xid, XidStatus status, XLogRecPtr lsn, int slotno)
|
||||||
|
{
|
||||||
|
int byteno = TransactionIdToByte(xid);
|
||||||
|
int bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
|
||||||
|
char *byteptr;
|
||||||
|
char byteval;
|
||||||
|
|
||||||
byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
|
byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
|
||||||
|
|
||||||
/* Current state should be 0, subcommitted or target state */
|
/* Current state should be 0, subcommitted or target state */
|
||||||
@ -132,8 +335,6 @@ TransactionIdSetStatus(TransactionId xid, XidStatus status, XLogRecPtr lsn)
|
|||||||
byteval |= (status << bshift);
|
byteval |= (status << bshift);
|
||||||
*byteptr = byteval;
|
*byteptr = byteval;
|
||||||
|
|
||||||
ClogCtl->shared->page_dirty[slotno] = true;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update the group LSN if the transaction completion LSN is higher.
|
* Update the group LSN if the transaction completion LSN is higher.
|
||||||
*
|
*
|
||||||
@ -149,8 +350,6 @@ TransactionIdSetStatus(TransactionId xid, XidStatus status, XLogRecPtr lsn)
|
|||||||
if (XLByteLT(ClogCtl->shared->group_lsn[lsnindex], lsn))
|
if (XLByteLT(ClogCtl->shared->group_lsn[lsnindex], lsn))
|
||||||
ClogCtl->shared->group_lsn[lsnindex] = lsn;
|
ClogCtl->shared->group_lsn[lsnindex] = lsn;
|
||||||
}
|
}
|
||||||
|
|
||||||
LWLockRelease(CLogControlLock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/transam.c,v 1.76 2008/03/26 18:48:59 alvherre Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/transam.c,v 1.77 2008/10/20 19:18:18 alvherre Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* This file contains the high level access-method interface to the
|
* This file contains the high level access-method interface to the
|
||||||
@ -40,15 +40,12 @@ static const XLogRecPtr InvalidXLogRecPtr = {0, 0};
|
|||||||
|
|
||||||
/* Local functions */
|
/* Local functions */
|
||||||
static XidStatus TransactionLogFetch(TransactionId transactionId);
|
static XidStatus TransactionLogFetch(TransactionId transactionId);
|
||||||
static void TransactionLogUpdate(TransactionId transactionId,
|
|
||||||
XidStatus status, XLogRecPtr lsn);
|
|
||||||
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* Postgres log access method interface
|
* Postgres log access method interface
|
||||||
*
|
*
|
||||||
* TransactionLogFetch
|
* TransactionLogFetch
|
||||||
* TransactionLogUpdate
|
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -100,41 +97,6 @@ TransactionLogFetch(TransactionId transactionId)
|
|||||||
return xidstatus;
|
return xidstatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* TransactionLogUpdate
|
|
||||||
*
|
|
||||||
* Store the new status of a transaction. The commit record LSN must be
|
|
||||||
* passed when recording an async commit; else it should be InvalidXLogRecPtr.
|
|
||||||
*/
|
|
||||||
static inline void
|
|
||||||
TransactionLogUpdate(TransactionId transactionId,
|
|
||||||
XidStatus status, XLogRecPtr lsn)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* update the commit log
|
|
||||||
*/
|
|
||||||
TransactionIdSetStatus(transactionId, status, lsn);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TransactionLogMultiUpdate
|
|
||||||
*
|
|
||||||
* Update multiple transaction identifiers to a given status.
|
|
||||||
* Don't depend on this being atomic; it's not.
|
|
||||||
*/
|
|
||||||
static inline void
|
|
||||||
TransactionLogMultiUpdate(int nxids, TransactionId *xids,
|
|
||||||
XidStatus status, XLogRecPtr lsn)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
Assert(nxids != 0);
|
|
||||||
|
|
||||||
for (i = 0; i < nxids; i++)
|
|
||||||
TransactionIdSetStatus(xids[i], status, lsn);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* Interface functions
|
* Interface functions
|
||||||
*
|
*
|
||||||
@ -144,11 +106,12 @@ TransactionLogMultiUpdate(int nxids, TransactionId *xids,
|
|||||||
* these functions test the transaction status of
|
* these functions test the transaction status of
|
||||||
* a specified transaction id.
|
* a specified transaction id.
|
||||||
*
|
*
|
||||||
* TransactionIdCommit
|
* TransactionIdCommitTree
|
||||||
* TransactionIdAbort
|
* TransactionIdAsyncCommitTree
|
||||||
|
* TransactionIdAbortTree
|
||||||
* ========
|
* ========
|
||||||
* these functions set the transaction status
|
* these functions set the transaction status of the specified
|
||||||
* of the specified xid.
|
* transaction tree.
|
||||||
*
|
*
|
||||||
* See also TransactionIdIsInProgress, which once was in this module
|
* See also TransactionIdIsInProgress, which once was in this module
|
||||||
* but now lives in procarray.c.
|
* but now lives in procarray.c.
|
||||||
@ -287,75 +250,21 @@ TransactionIdIsKnownCompleted(TransactionId transactionId)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TransactionIdCommit
|
|
||||||
* Commits the transaction associated with the identifier.
|
|
||||||
*
|
|
||||||
* Note:
|
|
||||||
* Assumes transaction identifier is valid.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
TransactionIdCommit(TransactionId transactionId)
|
|
||||||
{
|
|
||||||
TransactionLogUpdate(transactionId, TRANSACTION_STATUS_COMMITTED,
|
|
||||||
InvalidXLogRecPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TransactionIdAsyncCommit
|
|
||||||
* Same as above, but for async commits. The commit record LSN is needed.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
TransactionIdAsyncCommit(TransactionId transactionId, XLogRecPtr lsn)
|
|
||||||
{
|
|
||||||
TransactionLogUpdate(transactionId, TRANSACTION_STATUS_COMMITTED, lsn);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TransactionIdAbort
|
|
||||||
* Aborts the transaction associated with the identifier.
|
|
||||||
*
|
|
||||||
* Note:
|
|
||||||
* Assumes transaction identifier is valid.
|
|
||||||
* No async version of this is needed.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
TransactionIdAbort(TransactionId transactionId)
|
|
||||||
{
|
|
||||||
TransactionLogUpdate(transactionId, TRANSACTION_STATUS_ABORTED,
|
|
||||||
InvalidXLogRecPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TransactionIdSubCommit
|
|
||||||
* Marks the subtransaction associated with the identifier as
|
|
||||||
* sub-committed.
|
|
||||||
*
|
|
||||||
* Note:
|
|
||||||
* No async version of this is needed.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
TransactionIdSubCommit(TransactionId transactionId)
|
|
||||||
{
|
|
||||||
TransactionLogUpdate(transactionId, TRANSACTION_STATUS_SUB_COMMITTED,
|
|
||||||
InvalidXLogRecPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TransactionIdCommitTree
|
* TransactionIdCommitTree
|
||||||
* Marks all the given transaction ids as committed.
|
* Marks the given transaction and children as committed
|
||||||
*
|
*
|
||||||
* The caller has to be sure that this is used only to mark subcommitted
|
* "xid" is a toplevel transaction commit, and the xids array contains its
|
||||||
* subtransactions as committed, and only *after* marking the toplevel
|
* committed subtransactions.
|
||||||
* parent as committed. Otherwise there is a race condition against
|
*
|
||||||
* TransactionIdDidCommit.
|
* This commit operation is not guaranteed to be atomic, but if not, subxids
|
||||||
|
* are correctly marked subcommit first.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TransactionIdCommitTree(int nxids, TransactionId *xids)
|
TransactionIdCommitTree(TransactionId xid, int nxids, TransactionId *xids)
|
||||||
{
|
{
|
||||||
if (nxids > 0)
|
return TransactionIdSetTreeStatus(xid, nxids, xids,
|
||||||
TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_COMMITTED,
|
TRANSACTION_STATUS_COMMITTED,
|
||||||
InvalidXLogRecPtr);
|
InvalidXLogRecPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,29 +273,30 @@ TransactionIdCommitTree(int nxids, TransactionId *xids)
|
|||||||
* Same as above, but for async commits. The commit record LSN is needed.
|
* Same as above, but for async commits. The commit record LSN is needed.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TransactionIdAsyncCommitTree(int nxids, TransactionId *xids, XLogRecPtr lsn)
|
TransactionIdAsyncCommitTree(TransactionId xid, int nxids, TransactionId *xids,
|
||||||
|
XLogRecPtr lsn)
|
||||||
{
|
{
|
||||||
if (nxids > 0)
|
return TransactionIdSetTreeStatus(xid, nxids, xids,
|
||||||
TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_COMMITTED,
|
TRANSACTION_STATUS_COMMITTED, lsn);
|
||||||
lsn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TransactionIdAbortTree
|
* TransactionIdAbortTree
|
||||||
* Marks all the given transaction ids as aborted.
|
* Marks the given transaction and children as aborted.
|
||||||
|
*
|
||||||
|
* "xid" is a toplevel transaction commit, and the xids array contains its
|
||||||
|
* committed subtransactions.
|
||||||
*
|
*
|
||||||
* We don't need to worry about the non-atomic behavior, since any onlookers
|
* We don't need to worry about the non-atomic behavior, since any onlookers
|
||||||
* will consider all the xacts as not-yet-committed anyway.
|
* will consider all the xacts as not-yet-committed anyway.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TransactionIdAbortTree(int nxids, TransactionId *xids)
|
TransactionIdAbortTree(TransactionId xid, int nxids, TransactionId *xids)
|
||||||
{
|
{
|
||||||
if (nxids > 0)
|
TransactionIdSetTreeStatus(xid, nxids, xids,
|
||||||
TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_ABORTED,
|
TRANSACTION_STATUS_ABORTED, InvalidXLogRecPtr);
|
||||||
InvalidXLogRecPtr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TransactionIdPrecedes --- is id1 logically < id2?
|
* TransactionIdPrecedes --- is id1 logically < id2?
|
||||||
*/
|
*/
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.45 2008/08/11 11:05:10 heikki Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.46 2008/10/20 19:18:18 alvherre Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Each global transaction is associated with a global transaction
|
* Each global transaction is associated with a global transaction
|
||||||
@ -1745,9 +1745,7 @@ RecordTransactionCommitPrepared(TransactionId xid,
|
|||||||
XLogFlush(recptr);
|
XLogFlush(recptr);
|
||||||
|
|
||||||
/* Mark the transaction committed in pg_clog */
|
/* Mark the transaction committed in pg_clog */
|
||||||
TransactionIdCommit(xid);
|
TransactionIdCommitTree(xid, nchildren, children);
|
||||||
/* to avoid race conditions, the parent must commit first */
|
|
||||||
TransactionIdCommitTree(nchildren, children);
|
|
||||||
|
|
||||||
/* Checkpoint can proceed now */
|
/* Checkpoint can proceed now */
|
||||||
MyProc->inCommit = false;
|
MyProc->inCommit = false;
|
||||||
@ -1822,8 +1820,7 @@ RecordTransactionAbortPrepared(TransactionId xid,
|
|||||||
* Mark the transaction aborted in clog. This is not absolutely necessary
|
* Mark the transaction aborted in clog. This is not absolutely necessary
|
||||||
* but we may as well do it while we are here.
|
* but we may as well do it while we are here.
|
||||||
*/
|
*/
|
||||||
TransactionIdAbort(xid);
|
TransactionIdAbortTree(xid, nchildren, children);
|
||||||
TransactionIdAbortTree(nchildren, children);
|
|
||||||
|
|
||||||
END_CRIT_SECTION();
|
END_CRIT_SECTION();
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.265 2008/08/11 11:05:10 heikki Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.266 2008/10/20 19:18:18 alvherre Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -254,7 +254,6 @@ static void CommitTransaction(void);
|
|||||||
static TransactionId RecordTransactionAbort(bool isSubXact);
|
static TransactionId RecordTransactionAbort(bool isSubXact);
|
||||||
static void StartTransaction(void);
|
static void StartTransaction(void);
|
||||||
|
|
||||||
static void RecordSubTransactionCommit(void);
|
|
||||||
static void StartSubTransaction(void);
|
static void StartSubTransaction(void);
|
||||||
static void CommitSubTransaction(void);
|
static void CommitSubTransaction(void);
|
||||||
static void AbortSubTransaction(void);
|
static void AbortSubTransaction(void);
|
||||||
@ -952,11 +951,7 @@ RecordTransactionCommit(void)
|
|||||||
* Now we may update the CLOG, if we wrote a COMMIT record above
|
* Now we may update the CLOG, if we wrote a COMMIT record above
|
||||||
*/
|
*/
|
||||||
if (markXidCommitted)
|
if (markXidCommitted)
|
||||||
{
|
TransactionIdCommitTree(xid, nchildren, children);
|
||||||
TransactionIdCommit(xid);
|
|
||||||
/* to avoid race conditions, the parent must commit first */
|
|
||||||
TransactionIdCommitTree(nchildren, children);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -974,11 +969,7 @@ RecordTransactionCommit(void)
|
|||||||
* flushed before the CLOG may be updated.
|
* flushed before the CLOG may be updated.
|
||||||
*/
|
*/
|
||||||
if (markXidCommitted)
|
if (markXidCommitted)
|
||||||
{
|
TransactionIdAsyncCommitTree(xid, nchildren, children, XactLastRecEnd);
|
||||||
TransactionIdAsyncCommit(xid, XactLastRecEnd);
|
|
||||||
/* to avoid race conditions, the parent must commit first */
|
|
||||||
TransactionIdAsyncCommitTree(nchildren, children, XactLastRecEnd);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1156,36 +1147,6 @@ AtSubCommit_childXids(void)
|
|||||||
s->maxChildXids = 0;
|
s->maxChildXids = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* RecordSubTransactionCommit
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
RecordSubTransactionCommit(void)
|
|
||||||
{
|
|
||||||
TransactionId xid = GetCurrentTransactionIdIfAny();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We do not log the subcommit in XLOG; it doesn't matter until the
|
|
||||||
* top-level transaction commits.
|
|
||||||
*
|
|
||||||
* We must mark the subtransaction subcommitted in the CLOG if it had a
|
|
||||||
* valid XID assigned. If it did not, nobody else will ever know about
|
|
||||||
* the existence of this subxact. We don't have to deal with deletions
|
|
||||||
* scheduled for on-commit here, since they'll be reassigned to our parent
|
|
||||||
* (who might still abort).
|
|
||||||
*/
|
|
||||||
if (TransactionIdIsValid(xid))
|
|
||||||
{
|
|
||||||
/* XXX does this really need to be a critical section? */
|
|
||||||
START_CRIT_SECTION();
|
|
||||||
|
|
||||||
/* Record subtransaction subcommit */
|
|
||||||
TransactionIdSubCommit(xid);
|
|
||||||
|
|
||||||
END_CRIT_SECTION();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* AbortTransaction stuff
|
* AbortTransaction stuff
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
@ -1288,14 +1249,8 @@ RecordTransactionAbort(bool isSubXact)
|
|||||||
* waiting for already-aborted subtransactions. It is OK to do it without
|
* waiting for already-aborted subtransactions. It is OK to do it without
|
||||||
* having flushed the ABORT record to disk, because in event of a crash
|
* having flushed the ABORT record to disk, because in event of a crash
|
||||||
* we'd be assumed to have aborted anyway.
|
* we'd be assumed to have aborted anyway.
|
||||||
*
|
|
||||||
* The ordering here isn't critical but it seems best to mark the parent
|
|
||||||
* first. This assures an atomic transition of all the subtransactions to
|
|
||||||
* aborted state from the point of view of concurrent
|
|
||||||
* TransactionIdDidAbort calls.
|
|
||||||
*/
|
*/
|
||||||
TransactionIdAbort(xid);
|
TransactionIdAbortTree(xid, nchildren, children);
|
||||||
TransactionIdAbortTree(nchildren, children);
|
|
||||||
|
|
||||||
END_CRIT_SECTION();
|
END_CRIT_SECTION();
|
||||||
|
|
||||||
@ -3791,8 +3746,11 @@ CommitSubTransaction(void)
|
|||||||
/* Must CCI to ensure commands of subtransaction are seen as done */
|
/* Must CCI to ensure commands of subtransaction are seen as done */
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
|
|
||||||
/* Mark subtransaction as subcommitted */
|
/*
|
||||||
RecordSubTransactionCommit();
|
* Prior to 8.4 we marked subcommit in clog at this point. We now only
|
||||||
|
* perform that step, if required, as part of the atomic update of the
|
||||||
|
* whole transaction tree at top level commit or abort.
|
||||||
|
*/
|
||||||
|
|
||||||
/* Post-commit cleanup */
|
/* Post-commit cleanup */
|
||||||
if (TransactionIdIsValid(s->transactionId))
|
if (TransactionIdIsValid(s->transactionId))
|
||||||
@ -4259,11 +4217,9 @@ xact_redo_commit(xl_xact_commit *xlrec, TransactionId xid)
|
|||||||
TransactionId max_xid;
|
TransactionId max_xid;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
TransactionIdCommit(xid);
|
/* Mark the transaction committed in pg_clog */
|
||||||
|
|
||||||
/* Mark committed subtransactions as committed */
|
|
||||||
sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]);
|
sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]);
|
||||||
TransactionIdCommitTree(xlrec->nsubxacts, sub_xids);
|
TransactionIdCommitTree(xid, xlrec->nsubxacts, sub_xids);
|
||||||
|
|
||||||
/* Make sure nextXid is beyond any XID mentioned in the record */
|
/* Make sure nextXid is beyond any XID mentioned in the record */
|
||||||
max_xid = xid;
|
max_xid = xid;
|
||||||
@ -4299,11 +4255,9 @@ xact_redo_abort(xl_xact_abort *xlrec, TransactionId xid)
|
|||||||
TransactionId max_xid;
|
TransactionId max_xid;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
TransactionIdAbort(xid);
|
/* Mark the transaction aborted in pg_clog */
|
||||||
|
|
||||||
/* Mark subtransactions as aborted */
|
|
||||||
sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]);
|
sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]);
|
||||||
TransactionIdAbortTree(xlrec->nsubxacts, sub_xids);
|
TransactionIdAbortTree(xid, xlrec->nsubxacts, sub_xids);
|
||||||
|
|
||||||
/* Make sure nextXid is beyond any XID mentioned in the record */
|
/* Make sure nextXid is beyond any XID mentioned in the record */
|
||||||
max_xid = xid;
|
max_xid = xid;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/access/clog.h,v 1.21 2008/01/01 19:45:56 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/access/clog.h,v 1.22 2008/10/20 19:18:18 alvherre Exp $
|
||||||
*/
|
*/
|
||||||
#ifndef CLOG_H
|
#ifndef CLOG_H
|
||||||
#define CLOG_H
|
#define CLOG_H
|
||||||
@ -32,7 +32,8 @@ typedef int XidStatus;
|
|||||||
#define NUM_CLOG_BUFFERS 8
|
#define NUM_CLOG_BUFFERS 8
|
||||||
|
|
||||||
|
|
||||||
extern void TransactionIdSetStatus(TransactionId xid, XidStatus status, XLogRecPtr lsn);
|
extern void TransactionIdSetTreeStatus(TransactionId xid, int nsubxids,
|
||||||
|
TransactionId *subxids, XidStatus status, XLogRecPtr lsn);
|
||||||
extern XidStatus TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn);
|
extern XidStatus TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn);
|
||||||
|
|
||||||
extern Size CLOGShmemSize(void);
|
extern Size CLOGShmemSize(void);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/access/transam.h,v 1.65 2008/03/11 20:20:35 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/access/transam.h,v 1.66 2008/10/20 19:18:18 alvherre Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -139,13 +139,10 @@ extern VariableCache ShmemVariableCache;
|
|||||||
extern bool TransactionIdDidCommit(TransactionId transactionId);
|
extern bool TransactionIdDidCommit(TransactionId transactionId);
|
||||||
extern bool TransactionIdDidAbort(TransactionId transactionId);
|
extern bool TransactionIdDidAbort(TransactionId transactionId);
|
||||||
extern bool TransactionIdIsKnownCompleted(TransactionId transactionId);
|
extern bool TransactionIdIsKnownCompleted(TransactionId transactionId);
|
||||||
extern void TransactionIdCommit(TransactionId transactionId);
|
|
||||||
extern void TransactionIdAsyncCommit(TransactionId transactionId, XLogRecPtr lsn);
|
|
||||||
extern void TransactionIdAbort(TransactionId transactionId);
|
extern void TransactionIdAbort(TransactionId transactionId);
|
||||||
extern void TransactionIdSubCommit(TransactionId transactionId);
|
extern void TransactionIdCommitTree(TransactionId xid, int nxids, TransactionId *xids);
|
||||||
extern void TransactionIdCommitTree(int nxids, TransactionId *xids);
|
extern void TransactionIdAsyncCommitTree(TransactionId xid, int nxids, TransactionId *xids, XLogRecPtr lsn);
|
||||||
extern void TransactionIdAsyncCommitTree(int nxids, TransactionId *xids, XLogRecPtr lsn);
|
extern void TransactionIdAbortTree(TransactionId xid, int nxids, TransactionId *xids);
|
||||||
extern void TransactionIdAbortTree(int nxids, TransactionId *xids);
|
|
||||||
extern bool TransactionIdPrecedes(TransactionId id1, TransactionId id2);
|
extern bool TransactionIdPrecedes(TransactionId id1, TransactionId id2);
|
||||||
extern bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2);
|
extern bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2);
|
||||||
extern bool TransactionIdFollows(TransactionId id1, TransactionId id2);
|
extern bool TransactionIdFollows(TransactionId id1, TransactionId id2);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user