On Tue, 2008-09-16 at 10:11 -0400, Alvaro Herrera wrote: > >> Right now we lock and unlock the clog for each committed subtransaction > >> at commit time, which is wasteful. A better scheme: > >> pre-scan the list of xids to derive list of pages > >> if we have just a single page to update > >> { > >> update all entries on page in one action > >> } > >> else > >> { > >> loop thru xids marking them all as subcommitted > >> mark top level transaction committed > >> loop thus xids again marking them all as committed > >> } > > > Hmm, I don't see anything immediately wrong with that. > > Neither do I. > > I wonder if the improved clog API required to mark multiple transactions > as committed at once would be also useful to TransactionIdCommitTree > which is used in regular transaction commit.
I enclose a patch to transform what we have now into what I think is possible. If we agree this is possible, then I will do further work to optimise transam.c (using clog.c changes also). So this is an "intermediate" or precursor patch for discussion only. backend/access/transam/transam.c | 78 ++++++++++++-----------------! backend/access/transam/twophase.c | 4 ! backend/access/transam/xact.c | 50 -----------------!!!!!!! include/access/transam.h | 7 !!! 4 files changed, 32 insertions(+), 78 deletions(-), 29 modifications(!) -- Simon Riggs www.2ndQuadrant.com PostgreSQL Training, Services and Support
Index: src/backend/access/transam/transam.c =================================================================== RCS file: /home/sriggs/pg/REPOSITORY/pgsql/src/backend/access/transam/transam.c,v retrieving revision 1.76 diff -c -r1.76 transam.c *** src/backend/access/transam/transam.c 26 Mar 2008 18:48:59 -0000 1.76 --- src/backend/access/transam/transam.c 16 Sep 2008 16:55:30 -0000 *************** *** 287,317 **** 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. --- 287,292 ---- *************** *** 328,359 **** } /* - * 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 * Marks all the given transaction ids as committed. * * The caller has to be sure that this is used only to mark subcommitted * subtransactions as committed, and only *after* marking the toplevel * parent as committed. Otherwise there is a race condition against ! * TransactionIdDidCommit. */ void ! TransactionIdCommitTree(int nxids, TransactionId *xids) { if (nxids > 0) TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_COMMITTED, InvalidXLogRecPtr); --- 303,335 ---- } /* * TransactionIdCommitTree * Marks all the given transaction ids as committed. * * The caller has to be sure that this is used only to mark subcommitted * subtransactions as committed, and only *after* marking the toplevel * parent as committed. Otherwise there is a race condition against ! * TransactionIdDidCommit since we do not apply changes atomically, yet. */ void ! TransactionIdCommitTree(TransactionId commitxid, int nxids, TransactionId *xids) { + /* + * Mark top level transaction id as committed first, to avoid + * race conditions with TransactionIdDidCommit + */ + TransactionLogUpdate(commitxid, TRANSACTION_STATUS_COMMITTED, + InvalidXLogRecPtr); + + /* + * If there is more than one subcommit, then we need to mark them + * subcommitted first to ensure there is no race condition where + * we might see a subtransaction as still in progress when it is + * now committed. + */ + if (nxids > 1) + TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_SUB_COMMITTED, + InvalidXLogRecPtr); if (nxids > 0) TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_COMMITTED, InvalidXLogRecPtr); *************** *** 364,371 **** * Same as above, but for async commits. The commit record LSN is needed. */ void ! TransactionIdAsyncCommitTree(int nxids, TransactionId *xids, XLogRecPtr lsn) { if (nxids > 0) TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_COMMITTED, lsn); --- 340,363 ---- * Same as above, but for async commits. The commit record LSN is needed. */ void ! TransactionIdAsyncCommitTree(TransactionId commitxid, int nxids, TransactionId *xids, XLogRecPtr lsn) { + /* + * Mark top level transaction id as committed first, to avoid + * race conditions with TransactionIdDidCommit + */ + TransactionLogUpdate(commitxid, TRANSACTION_STATUS_COMMITTED, + lsn); + + /* + * If there is more than one subcommit, then we need to mark them + * subcommitted first to ensure there is no race condition where + * we might see a subtransaction as still in progress when it is + * now committed. + */ + if (nxids > 1) + TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_SUB_COMMITTED, + lsn); if (nxids > 0) TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_COMMITTED, lsn); Index: src/backend/access/transam/twophase.c =================================================================== RCS file: /home/sriggs/pg/REPOSITORY/pgsql/src/backend/access/transam/twophase.c,v retrieving revision 1.45 diff -c -r1.45 twophase.c *** src/backend/access/transam/twophase.c 11 Aug 2008 11:05:10 -0000 1.45 --- src/backend/access/transam/twophase.c 16 Sep 2008 16:47:21 -0000 *************** *** 1745,1753 **** XLogFlush(recptr); /* Mark the transaction committed in pg_clog */ ! TransactionIdCommit(xid); ! /* to avoid race conditions, the parent must commit first */ ! TransactionIdCommitTree(nchildren, children); /* Checkpoint can proceed now */ MyProc->inCommit = false; --- 1745,1751 ---- XLogFlush(recptr); /* Mark the transaction committed in pg_clog */ ! TransactionIdCommitTree(xid, nchildren, children); /* Checkpoint can proceed now */ MyProc->inCommit = false; Index: src/backend/access/transam/xact.c =================================================================== RCS file: /home/sriggs/pg/REPOSITORY/pgsql/src/backend/access/transam/xact.c,v retrieving revision 1.265 diff -c -r1.265 xact.c *** src/backend/access/transam/xact.c 11 Aug 2008 11:05:10 -0000 1.265 --- src/backend/access/transam/xact.c 16 Sep 2008 16:40:17 -0000 *************** *** 254,260 **** static TransactionId RecordTransactionAbort(bool isSubXact); static void StartTransaction(void); - static void RecordSubTransactionCommit(void); static void StartSubTransaction(void); static void CommitSubTransaction(void); static void AbortSubTransaction(void); --- 254,259 ---- *************** *** 952,962 **** * Now we may update the CLOG, if we wrote a COMMIT record above */ if (markXidCommitted) ! { ! TransactionIdCommit(xid); ! /* to avoid race conditions, the parent must commit first */ ! TransactionIdCommitTree(nchildren, children); ! } } else { --- 951,957 ---- * Now we may update the CLOG, if we wrote a COMMIT record above */ if (markXidCommitted) ! TransactionIdCommitTree(xid, nchildren, children); } else { *************** *** 974,984 **** * flushed before the CLOG may be updated. */ if (markXidCommitted) ! { ! TransactionIdAsyncCommit(xid, XactLastRecEnd); ! /* to avoid race conditions, the parent must commit first */ ! TransactionIdAsyncCommitTree(nchildren, children, XactLastRecEnd); ! } } /* --- 969,975 ---- * flushed before the CLOG may be updated. */ if (markXidCommitted) ! TransactionIdAsyncCommitTree(xid, nchildren, children, XactLastRecEnd); } /* *************** *** 1156,1191 **** 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 * ---------------------------------------------------------------- --- 1147,1152 ---- *************** *** 3791,3799 **** /* Must CCI to ensure commands of subtransaction are seen as done */ CommandCounterIncrement(); - /* Mark subtransaction as subcommitted */ - RecordSubTransactionCommit(); - /* Post-commit cleanup */ if (TransactionIdIsValid(s->transactionId)) AtSubCommit_childXids(); --- 3752,3757 ---- *************** *** 4259,4269 **** TransactionId max_xid; int i; - TransactionIdCommit(xid); - /* Mark committed subtransactions as committed */ sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]); ! TransactionIdCommitTree(xlrec->nsubxacts, sub_xids); /* Make sure nextXid is beyond any XID mentioned in the record */ max_xid = xid; --- 4217,4225 ---- TransactionId max_xid; int i; /* Mark committed subtransactions as committed */ sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]); ! TransactionIdCommitTree(xid, xlrec->nsubxacts, sub_xids); /* Make sure nextXid is beyond any XID mentioned in the record */ max_xid = xid; Index: src/include/access/transam.h =================================================================== RCS file: /home/sriggs/pg/REPOSITORY/pgsql/src/include/access/transam.h,v retrieving revision 1.65 diff -c -r1.65 transam.h *** src/include/access/transam.h 11 Mar 2008 20:20:35 -0000 1.65 --- src/include/access/transam.h 16 Sep 2008 17:05:12 -0000 *************** *** 139,150 **** extern bool TransactionIdDidCommit(TransactionId transactionId); extern bool TransactionIdDidAbort(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 TransactionIdSubCommit(TransactionId transactionId); ! extern void TransactionIdCommitTree(int nxids, TransactionId *xids); ! extern void TransactionIdAsyncCommitTree(int nxids, TransactionId *xids, XLogRecPtr lsn); extern void TransactionIdAbortTree(int nxids, TransactionId *xids); extern bool TransactionIdPrecedes(TransactionId id1, TransactionId id2); extern bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2); --- 139,147 ---- extern bool TransactionIdDidCommit(TransactionId transactionId); extern bool TransactionIdDidAbort(TransactionId transactionId); extern bool TransactionIdIsKnownCompleted(TransactionId transactionId); extern void TransactionIdAbort(TransactionId transactionId); ! extern void TransactionIdCommitTree(TransactionId commitxid, int nxids, TransactionId *xids); ! extern void TransactionIdAsyncCommitTree(TransactionId commitxid, int nxids, TransactionId *xids, XLogRecPtr lsn); extern void TransactionIdAbortTree(int nxids, TransactionId *xids); extern bool TransactionIdPrecedes(TransactionId id1, TransactionId id2); extern bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2);
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers