Skip to content

Commit be91d91

Browse files
knizhnikKonstantin Knizhnik
andauthored
Set wasShutdown=true during hot-standby replica startup only when primary is not alive (#364)
* Set wasShutdown=true during hot-standby replica startup only when primary is not alive * Report fatal error if hot standaby replica is started with oldestAcriveXid=0 Postgres part of neondatabase/neon#6705 --------- Co-authored-by: Konstantin Knizhnik <knizhnik@neon.tech>
1 parent 0baccce commit be91d91

File tree

2 files changed

+50
-1
lines changed

2 files changed

+50
-1
lines changed

src/backend/access/transam/xlog.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5187,6 +5187,24 @@ StartupXLOG(void)
51875187
&haveBackupLabel, &haveTblspcMap);
51885188
checkPoint = ControlFile->checkPointCopy;
51895189

5190+
if (ZenithRecoveryRequested)
5191+
{
5192+
if (wasShutdown)
5193+
checkPoint.oldestActiveXid = InvalidTransactionId;
5194+
else if (!TransactionIdIsValid(checkPoint.oldestActiveXid))
5195+
{
5196+
/*
5197+
* It should not actually happen: PS oldestActiveXid
5198+
* from running xacts WAL records and include it in checkpoint
5199+
* sent in basebackup.
5200+
* FirstNormalTransactionId is conservative estimation of oldest active XACT, unless
5201+
* current XID is greater than 1^31. So it is also not 100% safe solution but better than assertion failure.
5202+
*/
5203+
elog(FATAL, "oldestActiveXid=%d", checkPoint.oldestActiveXid);
5204+
checkPoint.oldestActiveXid = FirstNormalTransactionId;
5205+
}
5206+
}
5207+
51905208
/* initialize shared memory variables from the checkpoint record */
51915209
ShmemVariableCache->nextXid = checkPoint.nextXid;
51925210
ShmemVariableCache->nextOid = checkPoint.nextOid;

src/backend/access/transam/xlogrecovery.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,18 @@ XLogWaitForReplayOf(XLogRecPtr redoEndRecPtr)
546546
ConditionVariableCancelSleep();
547547
}
548548

549+
550+
/*
551+
* NEON: check if primary node is running.
552+
* Correspondent GUC is received from control plane
553+
*/
554+
static bool
555+
IsPrimaryAlive()
556+
{
557+
const char* val = GetConfigOption("neon.primary_is_running", true, false);
558+
return val != NULL && strcmp(val, "on") == 0;
559+
}
560+
549561
/*
550562
* Prepare the system for WAL recovery, if needed.
551563
*
@@ -802,7 +814,26 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
802814
//EndRecPtr = ControlFile->checkPointCopy.redo;
803815

804816
memcpy(&checkPoint, &ControlFile->checkPointCopy, sizeof(CheckPoint));
805-
wasShutdown = true;
817+
// When primary Neon compute node is started, we pretend that it started after a clean shutdown and
818+
// no recovery is needed. We don't need to do WAL replay, the page server does that on a page-by-page basis.
819+
// When a read-only replica is started, PostgreSQL normally waits for a shutdown checkpoint or running-xacts
820+
// record before enabling hot standby, to establish which transactions are still running in the primary,
821+
// and might still commit later. But if we know that the primary is not running - because the control plane
822+
// says so - we can skip that. That avoids having to wait indefinitely if the primary is not running. This is
823+
// particularly important for Neon because we don't start recovery from a checkpoint record, so there's
824+
// no guarantee on when we'll see the next checkpoint or running-xacts record, if ever. so if we know the primary is
825+
// not currently running, also set wasShutdown to 'true'.
826+
if (StandbyModeRequested &&
827+
PrimaryConnInfo != NULL && *PrimaryConnInfo != '\0')
828+
{
829+
if (!IsPrimaryAlive())
830+
wasShutdown = true;
831+
else
832+
wasShutdown = false;
833+
}
834+
else
835+
wasShutdown = true;
836+
806837

807838
/* Initialize expectedTLEs, like ReadRecord() does */
808839
expectedTLEs = readTimeLineHistory(checkPoint.ThisTimeLineID);

0 commit comments

Comments
 (0)