On Tue, 2009-01-27 at 12:53 -0500, Gabi Julien wrote: > I have merged the last hot standby patch (v9g) to 8.4 devel and I am pleased > with the experience. This is promising stuff. Thanks, > Perhaps it is a bit too soon to > ask questions here but here it is: Thanks very much for the bug report. > 1. Speed of recovery > > With a archive_timeout of 60 seconds, it can take about 4 minutes before I see > the reflected changes in the replica. This is normal since, in addition to > the WAL log shipping, it takes more time to do the recovery itself. Still, is > there any way besides the archive_timeout config option to speed up the > recovery of WAL logs on the hot standby? There was a reported bug whose apparent symptoms were delay of WAL files. The bug was not in fact anything to do with that at all, it was just delayed *visibility*. So I doubt very much that you have a performance problem. The bug fix patch is attached, verified to solve the problem. -- 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-general mailing list (pgsql-general@xxxxxxxxxxxxxx) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-general