On Sat, 2009-01-24 at 17:24 +1300, Mark Kirkwood wrote:
> I'm doing some test runs with this now. I notice an old flatfiles
> related bug has reappeared:
Bug fix patch against git repo.
--
Simon Riggs www.2ndQuadrant.com
PostgreSQL Training, Services and Support
*** a/src/backend/access/transam/xact.c
--- b/src/backend/access/transam/xact.c
***************
*** 1381,1387 **** RecordTransactionAbort(bool isSubXact)
* main xacts, the equivalent happens just after this function returns.
*/
if (isSubXact)
! XidCacheRemoveRunningXids(xid, nchildren, children, latestXid);
/* Reset XactLastRecEnd until the next transaction writes something */
if (!isSubXact)
--- 1381,1387 ----
* main xacts, the equivalent happens just after this function returns.
*/
if (isSubXact)
! XidCacheRemoveRunningXids(MyProc, xid, nchildren, children, latestXid);
/* Reset XactLastRecEnd until the next transaction writes something */
if (!isSubXact)
***************
*** 4536,4541 **** RecordKnownAssignedTransactionIds(XLogRecPtr lsn, TransactionId top_xid, Transac
--- 4536,4548 ----
{
int nxids = myproc->subxids.nxids;
+ /*
+ * It's possible for us to overflow the subxid cache and then
+ * for a subtransaction abort to reduce the number of subxids
+ * in the cache below the cache threshold again. If that happens
+ * then it's still OK for us to use the subxid cache again, since
+ * once its in the cache it lives there till abort or commit.
+ */
if (nxids < PGPROC_MAX_CACHED_SUBXIDS)
{
/*
***************
*** 4621,4629 **** RecordKnownAssignedTransactionIds(XLogRecPtr lsn, TransactionId top_xid, Transac
LWLockRelease(ProcArrayLock);
elog(trace_recovery(DEBUG4),
! "record known xact top_xid %u child_xid %u %slatestObservedXid %u",
top_xid, child_xid,
(unobserved ? "unobserved " : " "),
latestObservedXid);
/*
--- 4628,4637 ----
LWLockRelease(ProcArrayLock);
elog(trace_recovery(DEBUG4),
! "record known xact top_xid %u child_xid %u %s%slatestObservedXid %u",
top_xid, child_xid,
(unobserved ? "unobserved " : " "),
+ (mark_subtrans ? "mark subtrans " : " "),
latestObservedXid);
/*
***************
*** 4690,4707 **** xact_redo_commit(xl_xact_commit *xlrec, TransactionId xid, bool preparedXact)
PGPROC *proc;
int i;
- /* Make sure nextXid is beyond any XID mentioned in the record */
- max_xid = xid;
sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]);
! /*
! * Find the highest xid and remove unobserved xids if required.
! */
! for (i = 0; i < xlrec->nsubxacts; i++)
! {
! if (TransactionIdPrecedes(max_xid, sub_xids[i]))
! max_xid = sub_xids[i];
! }
/* Mark the transaction committed in pg_clog */
TransactionIdCommitTree(xid, xlrec->nsubxacts, sub_xids);
--- 4698,4706 ----
PGPROC *proc;
int i;
sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]);
! max_xid = TransactionIdLatest(xid, xlrec->nsubxacts, sub_xids);
/* Mark the transaction committed in pg_clog */
TransactionIdCommitTree(xid, xlrec->nsubxacts, sub_xids);
***************
*** 4720,4726 **** xact_redo_commit(xl_xact_commit *xlrec, TransactionId xid, bool preparedXact)
*/
if (IsRunningXactDataValid() && !preparedXact)
{
! ProcArrayRemove(proc, InvalidTransactionId, xlrec->nsubxacts, sub_xids);
FreeRecoveryProcess(proc);
}
--- 4719,4725 ----
*/
if (IsRunningXactDataValid() && !preparedXact)
{
! ProcArrayRemove(proc, max_xid, xlrec->nsubxacts, sub_xids);
FreeRecoveryProcess(proc);
}
***************
*** 4790,4821 **** xact_redo_commit(xl_xact_commit *xlrec, TransactionId xid, bool preparedXact)
/*
* Be careful with the order of execution, as with xact_redo_commit().
* The two functions are similar but differ in key places.
*/
static void
! xact_redo_abort(xl_xact_abort *xlrec, TransactionId xid, bool preparedXact)
{
PGPROC *proc = NULL;
TransactionId *sub_xids;
TransactionId max_xid;
int i;
- /* Make sure nextXid is beyond any XID mentioned in the record */
- max_xid = xid;
sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]);
!
! /*
! * Find the highest xid and remove unobserved xids if required.
! */
! for (i = 0; i < xlrec->nsubxacts; i++)
! {
! if (TransactionIdPrecedes(max_xid, sub_xids[i]))
! max_xid = sub_xids[i];
! }
/* Mark the transaction aborted in pg_clog */
TransactionIdAbortTree(xid, xlrec->nsubxacts, sub_xids);
! if (InArchiveRecovery && (proc = BackendXidGetProc(xid)) != NULL)
{
/*
* We must mark clog before we update the ProcArray. Only update
--- 4789,4815 ----
/*
* Be careful with the order of execution, as with xact_redo_commit().
* The two functions are similar but differ in key places.
+ *
+ * Note also that an abort can be for a subtransaction and its children,
+ * not just for a top level abort. That means we have to consider
+ * topxid != xid, whereas in commit we would find topxid == xid always
+ * because subtransaction commit is never WAL logged.
*/
static void
! xact_redo_abort(xl_xact_abort *xlrec, TransactionId xid, TransactionId topxid, bool preparedXact)
{
PGPROC *proc = NULL;
TransactionId *sub_xids;
TransactionId max_xid;
int i;
sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]);
! max_xid = TransactionIdLatest(xid, xlrec->nsubxacts, sub_xids);
/* Mark the transaction aborted in pg_clog */
TransactionIdAbortTree(xid, xlrec->nsubxacts, sub_xids);
! if (InArchiveRecovery && (proc = BackendXidGetProc(topxid)) != NULL)
{
/*
* We must mark clog before we update the ProcArray. Only update
***************
*** 4827,4840 **** xact_redo_abort(xl_xact_abort *xlrec, TransactionId xid, bool preparedXact)
* happened, but there are cases where they might sneak through.
* Leave these for the periodic cleanup by XACT_RUNNING_XACT records.
*/
! if (IsRunningXactDataValid() &&
! TransactionIdIsValid(proc->xid) && !preparedXact)
{
! ProcArrayRemove(proc, InvalidTransactionId, xlrec->nsubxacts, sub_xids);
! FreeRecoveryProcess(proc);
}
/*
* Release locks, if any. There are no invalidations to send.
*/
RelationReleaseRecoveryLockTree(xid, xlrec->nsubxacts, sub_xids);
--- 4821,4846 ----
* happened, but there are cases where they might sneak through.
* Leave these for the periodic cleanup by XACT_RUNNING_XACT records.
*/
! if (IsRunningXactDataValid() && !preparedXact)
{
! /*
! * Do we have a top-level transaction abort, or not?
! */
! if (topxid == xid)
! {
! ProcArrayRemove(proc, max_xid, xlrec->nsubxacts, sub_xids);
! FreeRecoveryProcess(proc);
! }
! else
! XidCacheRemoveRunningXids(proc, xid, xlrec->nsubxacts, sub_xids, max_xid);
}
/*
+ * There are no flat files that need updating, nor invalidation
+ * messages to send or undo.
+ */
+
+ /*
* Release locks, if any. There are no invalidations to send.
*/
RelationReleaseRecoveryLockTree(xid, xlrec->nsubxacts, sub_xids);
***************
*** 4937,4943 **** xact_redo(XLogRecPtr lsn, XLogRecord *record)
{
xl_xact_abort *xlrec = (xl_xact_abort *) XLogRecGetData(record);
! xact_redo_abort(xlrec, record->xl_xid, false);
}
else if (info == XLOG_XACT_PREPARE)
{
--- 4943,4949 ----
{
xl_xact_abort *xlrec = (xl_xact_abort *) XLogRecGetData(record);
! xact_redo_abort(xlrec, record->xl_xid, record->xl_topxid, false);
}
else if (info == XLOG_XACT_PREPARE)
{
***************
*** 4956,4962 **** xact_redo(XLogRecPtr lsn, XLogRecord *record)
{
xl_xact_abort_prepared *xlrec = (xl_xact_abort_prepared *) XLogRecGetData(record);
! xact_redo_abort(&xlrec->arec, xlrec->xid, true);
RemoveTwoPhaseFile(xlrec->xid, false);
}
else
--- 4962,4968 ----
{
xl_xact_abort_prepared *xlrec = (xl_xact_abort_prepared *) XLogRecGetData(record);
! xact_redo_abort(&xlrec->arec, xlrec->xid, record->xl_topxid, true);
RemoveTwoPhaseFile(xlrec->xid, false);
}
else
*** a/src/backend/storage/ipc/procarray.c
--- b/src/backend/storage/ipc/procarray.c
***************
*** 2061,2068 **** CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
#define XidCacheRemove(i) \
do { \
! MyProc->subxids.xids[i] = MyProc->subxids.xids[MyProc->subxids.nxids - 1]; \
! MyProc->subxids.nxids--; \
} while (0)
/*
--- 2061,2068 ----
#define XidCacheRemove(i) \
do { \
! proc->subxids.xids[i] = proc->subxids.xids[proc->subxids.nxids - 1]; \
! proc->subxids.nxids--; \
} while (0)
/*
***************
*** 2074,2080 **** CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
* latestXid must be the latest XID among the group.
*/
void
! XidCacheRemoveRunningXids(TransactionId xid,
int nxids, const TransactionId *xids,
TransactionId latestXid)
{
--- 2074,2080 ----
* latestXid must be the latest XID among the group.
*/
void
! XidCacheRemoveRunningXids(PGPROC *proc, TransactionId xid,
int nxids, const TransactionId *xids,
TransactionId latestXid)
{
***************
*** 2101,2109 **** XidCacheRemoveRunningXids(TransactionId xid,
{
TransactionId anxid = xids[i];
! for (j = MyProc->subxids.nxids - 1; j >= 0; j--)
{
! if (TransactionIdEquals(MyProc->subxids.xids[j], anxid))
{
XidCacheRemove(j);
break;
--- 2101,2109 ----
{
TransactionId anxid = xids[i];
! for (j = proc->subxids.nxids - 1; j >= 0; j--)
{
! if (TransactionIdEquals(proc->subxids.xids[j], anxid))
{
XidCacheRemove(j);
break;
***************
*** 2117,2137 **** XidCacheRemoveRunningXids(TransactionId xid,
* error during AbortSubTransaction. So instead of Assert, emit a
* debug warning.
*/
! if (j < 0 && !MyProc->subxids.overflowed)
! elog(WARNING, "did not find subXID %u in MyProc", anxid);
}
! for (j = MyProc->subxids.nxids - 1; j >= 0; j--)
{
! if (TransactionIdEquals(MyProc->subxids.xids[j], xid))
{
XidCacheRemove(j);
break;
}
}
/* Ordinarily we should have found it, unless the cache has overflowed */
! if (j < 0 && !MyProc->subxids.overflowed)
! elog(WARNING, "did not find subXID %u in MyProc", xid);
/* Also advance global latestCompletedXid while holding the lock */
if (TransactionIdPrecedes(ShmemVariableCache->latestCompletedXid,
--- 2117,2137 ----
* error during AbortSubTransaction. So instead of Assert, emit a
* debug warning.
*/
! if (j < 0 && !proc->subxids.overflowed)
! elog(WARNING, "did not find subXID %u in proc", anxid);
}
! for (j = proc->subxids.nxids - 1; j >= 0; j--)
{
! if (TransactionIdEquals(proc->subxids.xids[j], xid))
{
XidCacheRemove(j);
break;
}
}
/* Ordinarily we should have found it, unless the cache has overflowed */
! if (j < 0 && !proc->subxids.overflowed)
! elog(WARNING, "did not find subXID %u in proc", xid);
/* Also advance global latestCompletedXid while holding the lock */
if (TransactionIdPrecedes(ShmemVariableCache->latestCompletedXid,
*** a/src/backend/utils/cache/inval.c
--- b/src/backend/utils/cache/inval.c
***************
*** 1635,1641 **** ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
* We only keep track of AccessExclusiveLocks, which are only ever held by
* one transaction on one relation, and don't worry about lock queuing.
*
! * We keep a single dynamically expandible locks list in local memory.
* List elements use type xl_rel_lock, since the WAL record type exactly
* matches the information that we need to keep track of.
*
--- 1635,1644 ----
* We only keep track of AccessExclusiveLocks, which are only ever held by
* one transaction on one relation, and don't worry about lock queuing.
*
! * We keep a single dynamically expandible list of locks in local memory,
! * RelationLockList, so we can keep track of the various entried made by
! * the Startup process's virtual xid in the shared lock table.
! *
* List elements use type xl_rel_lock, since the WAL record type exactly
* matches the information that we need to keep track of.
*
*** a/src/include/storage/procarray.h
--- b/src/include/storage/procarray.h
***************
*** 63,69 **** extern int CountUserBackends(Oid roleid);
extern bool CountOtherDBBackends(Oid databaseId,
int *nbackends, int *nprepared);
! extern void XidCacheRemoveRunningXids(TransactionId xid,
int nxids, const TransactionId *xids,
TransactionId latestXid);
--- 63,69 ----
extern bool CountOtherDBBackends(Oid databaseId,
int *nbackends, int *nprepared);
! extern void XidCacheRemoveRunningXids(PGPROC *proc, TransactionId xid,
int nxids, const TransactionId *xids,
TransactionId latestXid);
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers