Skip to content

Commit b4d9719

Browse files
knizhnikKonstantin Knizhnik
authored andcommitted
Set wasShutdown=true during hot-standby replica startup only when primary is not alive (#363)
* 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 9170ffd commit b4d9719

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
@@ -5336,6 +5336,24 @@ StartupXLOG(void)
53365336
&haveBackupLabel, &haveTblspcMap);
53375337
checkPoint = ControlFile->checkPointCopy;
53385338

5339+
if (ZenithRecoveryRequested)
5340+
{
5341+
if (wasShutdown)
5342+
checkPoint.oldestActiveXid = InvalidTransactionId;
5343+
else if (!TransactionIdIsValid(checkPoint.oldestActiveXid))
5344+
{
5345+
/*
5346+
* It should not actually happen: PS oldestActiveXid
5347+
* from running xacts WAL records and include it in checkpoint
5348+
* sent in basebackup.
5349+
* FirstNormalTransactionId is conservative estimation of oldest active XACT, unless
5350+
* current XID is greater than 1^31. So it is also not 100% safe solution but better than assertion failure.
5351+
*/
5352+
elog(FATAL, "oldestActiveXid=%d", checkPoint.oldestActiveXid);
5353+
checkPoint.oldestActiveXid = FirstNormalTransactionId;
5354+
}
5355+
}
5356+
53395357
/* initialize shared memory variables from the checkpoint record */
53405358
ShmemVariableCache->nextXid = checkPoint.nextXid;
53415359
ShmemVariableCache->nextOid = checkPoint.nextOid;

src/backend/access/transam/xlogrecovery.c

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

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

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

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

0 commit comments

Comments
 (0)