Skip to content

Commit a41c881

Browse files
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
1 parent 27e9e86 commit a41c881

File tree

3 files changed

+51
-6
lines changed

3 files changed

+51
-6
lines changed

src/backend/access/transam/xlog.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6449,6 +6449,9 @@ StartupXLOG(void)
64496449
oldestActiveXID = checkPoint.oldestActiveXid;
64506450
Assert(TransactionIdIsValid(oldestActiveXID));
64516451

6452+
/* Tell procarray about the range of xids it has to deal with */
6453+
ProcArrayInitRecovery(ShmemVariableCache->nextXid);
6454+
64526455
/*
64536456
* Startup commit log and subtrans only. Other SLRUs are not
64546457
* maintained during recovery and need not be started yet.

src/backend/storage/ipc/procarray.c

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,28 @@ ProcArrayClearTransaction(PGPROC *proc)
435435
proc->subxids.overflowed = false;
436436
}
437437

438+
/*
439+
* ProcArrayInitRecovery -- initialize recovery xid mgmt environment
440+
*
441+
* Remember up to where the startup process initialized the CLOG and subtrans
442+
* so we can ensure its initialized gaplessly up to the point where necessary
443+
* while in recovery.
444+
*/
445+
void
446+
ProcArrayInitRecovery(TransactionId initializedUptoXID)
447+
{
448+
Assert(standbyState == STANDBY_INITIALIZED);
449+
Assert(TransactionIdIsNormal(initializedUptoXID));
450+
451+
/*
452+
* we set latestObservedXid to the xid SUBTRANS has been initialized upto
453+
* so we can extend it from that point onwards when we reach a consistent
454+
* state in ProcArrayApplyRecoveryInfo().
455+
*/
456+
latestObservedXid = initializedUptoXID;
457+
TransactionIdRetreat(latestObservedXid);
458+
}
459+
438460
/*
439461
* ProcArrayApplyRecoveryInfo -- apply recovery info about xids
440462
*
@@ -523,7 +545,10 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running)
523545
Assert(standbyState == STANDBY_INITIALIZED);
524546

525547
/*
526-
* OK, we need to initialise from the RunningTransactionsData record
548+
* OK, we need to initialise from the RunningTransactionsData record.
549+
*
550+
* NB: this can be reached at least twice, so make sure new code can deal
551+
* with that.
527552
*/
528553

529554
/*
@@ -595,20 +620,32 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running)
595620
pfree(xids);
596621

597622
/*
623+
* latestObservedXid is set to the the point where SUBTRANS was started up
624+
* to, initialize subtrans from thereon, up to nextXid - 1.
625+
*/
626+
Assert(TransactionIdIsNormal(latestObservedXid));
627+
while (TransactionIdPrecedes(latestObservedXid, running->nextXid))
628+
{
629+
ExtendCLOG(latestObservedXid);
630+
ExtendSUBTRANS(latestObservedXid);
631+
632+
TransactionIdAdvance(latestObservedXid);
633+
}
634+
635+
/* ----------
598636
* Now we've got the running xids we need to set the global values that
599637
* are used to track snapshots as they evolve further.
600638
*
601-
* - latestCompletedXid which will be the xmax for snapshots -
602-
* lastOverflowedXid which shows whether snapshots overflow - nextXid
639+
* - latestCompletedXid which will be the xmax for snapshots
640+
* - lastOverflowedXid which shows whether snapshots overflow
641+
* - nextXid
603642
*
604643
* If the snapshot overflowed, then we still initialise with what we know,
605644
* but the recovery snapshot isn't fully valid yet because we know there
606645
* are some subxids missing. We don't know the specific subxids that are
607646
* missing, so conservatively assume the last one is latestObservedXid.
647+
* ----------
608648
*/
609-
latestObservedXid = running->nextXid;
610-
TransactionIdRetreat(latestObservedXid);
611-
612649
if (running->subxid_overflow)
613650
{
614651
standbyState = STANDBY_SNAPSHOT_PENDING;
@@ -667,6 +704,10 @@ ProcArrayApplyXidAssignment(TransactionId topxid,
667704

668705
Assert(standbyState >= STANDBY_INITIALIZED);
669706

707+
/* can't do anything useful unless we have more state setup */
708+
if (standbyState == STANDBY_INITIALIZED)
709+
return;
710+
670711
max_xid = TransactionIdLatest(topxid, nsubxids, subxids);
671712

672713
/*

src/include/storage/procarray.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ extern void ProcArrayRemove(PGPROC *proc, TransactionId latestXid);
2828
extern void ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid);
2929
extern void ProcArrayClearTransaction(PGPROC *proc);
3030

31+
extern void ProcArrayInitRecovery(TransactionId initializedUptoXID);
3132
extern void ProcArrayApplyRecoveryInfo(RunningTransactions running);
3233
extern void ProcArrayApplyXidAssignment(TransactionId topxid,
3334
int nsubxids, TransactionId *subxids);

0 commit comments

Comments
 (0)