Ensure no xid gaps during Hot Standby startup

In some cases with higher numbers of subtransactions
it was possible for us to incorrectly initialize
subtrans leading to complaints of missing pages.

Bug report by Sergey Konoplev
Analysis and fix by Andres Freund
This commit is contained in:
Simon Riggs 2013-06-23 11:09:24 +01:00
parent 0b5a5c8e05
commit 7b2c9d6829
3 changed files with 51 additions and 6 deletions

View File

@ -5391,6 +5391,9 @@ StartupXLOG(void)
oldestActiveXID = checkPoint.oldestActiveXid; oldestActiveXID = checkPoint.oldestActiveXid;
Assert(TransactionIdIsValid(oldestActiveXID)); Assert(TransactionIdIsValid(oldestActiveXID));
/* Tell procarray about the range of xids it has to deal with */
ProcArrayInitRecovery(ShmemVariableCache->nextXid);
/* /*
* Startup commit log and subtrans only. Other SLRUs are not * Startup commit log and subtrans only. Other SLRUs are not
* maintained during recovery and need not be started yet. * maintained during recovery and need not be started yet.

View File

@ -469,6 +469,28 @@ ProcArrayClearTransaction(PGPROC *proc)
pgxact->overflowed = false; pgxact->overflowed = false;
} }
/*
* ProcArrayInitRecovery -- initialize recovery xid mgmt environment
*
* Remember up to where the startup process initialized the CLOG and subtrans
* so we can ensure its initialized gaplessly up to the point where necessary
* while in recovery.
*/
void
ProcArrayInitRecovery(TransactionId initializedUptoXID)
{
Assert(standbyState == STANDBY_INITIALIZED);
Assert(TransactionIdIsNormal(initializedUptoXID));
/*
* we set latestObservedXid to the xid SUBTRANS has been initialized upto
* so we can extend it from that point onwards when we reach a consistent
* state in ProcArrayApplyRecoveryInfo().
*/
latestObservedXid = initializedUptoXID;
TransactionIdRetreat(latestObservedXid);
}
/* /*
* ProcArrayApplyRecoveryInfo -- apply recovery info about xids * ProcArrayApplyRecoveryInfo -- apply recovery info about xids
* *
@ -564,7 +586,10 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running)
Assert(standbyState == STANDBY_INITIALIZED); Assert(standbyState == STANDBY_INITIALIZED);
/* /*
* OK, we need to initialise from the RunningTransactionsData record * OK, we need to initialise from the RunningTransactionsData record.
*
* NB: this can be reached at least twice, so make sure new code can deal
* with that.
*/ */
/* /*
@ -636,20 +661,32 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running)
pfree(xids); pfree(xids);
/* /*
* latestObservedXid is set to the the point where SUBTRANS was started up
* to, initialize subtrans from thereon, up to nextXid - 1.
*/
Assert(TransactionIdIsNormal(latestObservedXid));
while (TransactionIdPrecedes(latestObservedXid, running->nextXid))
{
ExtendCLOG(latestObservedXid);
ExtendSUBTRANS(latestObservedXid);
TransactionIdAdvance(latestObservedXid);
}
/* ----------
* Now we've got the running xids we need to set the global values that * Now we've got the running xids we need to set the global values that
* are used to track snapshots as they evolve further. * are used to track snapshots as they evolve further.
* *
* - latestCompletedXid which will be the xmax for snapshots - * - latestCompletedXid which will be the xmax for snapshots
* lastOverflowedXid which shows whether snapshots overflow - nextXid * - lastOverflowedXid which shows whether snapshots overflow
* - nextXid
* *
* If the snapshot overflowed, then we still initialise with what we know, * If the snapshot overflowed, then we still initialise with what we know,
* but the recovery snapshot isn't fully valid yet because we know there * but the recovery snapshot isn't fully valid yet because we know there
* are some subxids missing. We don't know the specific subxids that are * are some subxids missing. We don't know the specific subxids that are
* missing, so conservatively assume the last one is latestObservedXid. * missing, so conservatively assume the last one is latestObservedXid.
* ----------
*/ */
latestObservedXid = running->nextXid;
TransactionIdRetreat(latestObservedXid);
if (running->subxid_overflow) if (running->subxid_overflow)
{ {
standbyState = STANDBY_SNAPSHOT_PENDING; standbyState = STANDBY_SNAPSHOT_PENDING;
@ -719,6 +756,10 @@ ProcArrayApplyXidAssignment(TransactionId topxid,
Assert(standbyState >= STANDBY_INITIALIZED); Assert(standbyState >= STANDBY_INITIALIZED);
/* can't do anything useful unless we have more state setup */
if (standbyState == STANDBY_INITIALIZED)
return;
max_xid = TransactionIdLatest(topxid, nsubxids, subxids); max_xid = TransactionIdLatest(topxid, nsubxids, subxids);
/* /*

View File

@ -26,6 +26,7 @@ extern void ProcArrayRemove(PGPROC *proc, TransactionId latestXid);
extern void ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid); extern void ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid);
extern void ProcArrayClearTransaction(PGPROC *proc); extern void ProcArrayClearTransaction(PGPROC *proc);
extern void ProcArrayInitRecovery(TransactionId initializedUptoXID);
extern void ProcArrayApplyRecoveryInfo(RunningTransactions running); extern void ProcArrayApplyRecoveryInfo(RunningTransactions running);
extern void ProcArrayApplyXidAssignment(TransactionId topxid, extern void ProcArrayApplyXidAssignment(TransactionId topxid,
int nsubxids, TransactionId *subxids); int nsubxids, TransactionId *subxids);