Skip to content

Commit b9336bc

Browse files
knizhnikKonstantin Knizhnikhlinnaka
authored
Set wasShutdown=true during hot-standby replica startup only when primary is not alive (#365)
* 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 <[email protected]> Co-authored-by: Heikki Linnakangas <[email protected]>
1 parent 1710119 commit b9336bc

File tree

1 file changed

+49
-1
lines changed
  • src/backend/access/transam

1 file changed

+49
-1
lines changed

src/backend/access/transam/xlog.c

+49-1
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,18 @@ typedef union WALInsertLockPadded
542542
char pad[PG_CACHE_LINE_SIZE];
543543
} WALInsertLockPadded;
544544

545+
546+
/*
547+
* NEON: check if primary node is running.
548+
* Correspondent GUC is received from control plane
549+
*/
550+
static bool
551+
IsPrimaryAlive()
552+
{
553+
const char* val = GetConfigOption("neon.primary_is_running", true, false);
554+
return val != NULL && strcmp(val, "on") == 0;
555+
}
556+
545557
/*
546558
* State of an exclusive backup, necessary to control concurrent activities
547559
* across sessions when working on exclusive backups.
@@ -7031,7 +7043,25 @@ StartupXLOG(void)
70317043
EndRecPtr = ControlFile->checkPointCopy.redo;
70327044

70337045
memcpy(&checkPoint, &ControlFile->checkPointCopy, sizeof(CheckPoint));
7034-
wasShutdown = true;
7046+
// When primary Neon compute node is started, we pretend that it started after a clean shutdown and
7047+
// no recovery is needed. We don't need to do WAL replay, the page server does that on a page-by-page basis.
7048+
// When a read-only replica is started, PostgreSQL normally waits for a shutdown checkpoint or running-xacts
7049+
// record before enabling hot standby, to establish which transactions are still running in the primary,
7050+
// and might still commit later. But if we know that the primary is not running - because the control plane
7051+
// says so - we can skip that. That avoids having to wait indefinitely if the primary is not running. This is
7052+
// particularly important for Neon because we don't start recovery from a checkpoint record, so there's
7053+
// no guarantee on when we'll see the next checkpoint or running-xacts record, if ever. so if we know the primary is
7054+
// not currently running, also set wasShutdown to 'true'.
7055+
if (StandbyModeRequested &&
7056+
PrimaryConnInfo != NULL && *PrimaryConnInfo != '\0')
7057+
{
7058+
if (!IsPrimaryAlive())
7059+
wasShutdown = true;
7060+
else
7061+
wasShutdown = false;
7062+
}
7063+
else
7064+
wasShutdown = true;
70357065

70367066
/* Initialize expectedTLEs, like ReadRecord() does */
70377067
expectedTLEs = readTimeLineHistory(checkPoint.ThisTimeLineID);
@@ -7201,6 +7231,24 @@ StartupXLOG(void)
72017231
ereport(PANIC,
72027232
(errmsg("invalid next transaction ID")));
72037233

7234+
if (ZenithRecoveryRequested)
7235+
{
7236+
if (wasShutdown)
7237+
checkPoint.oldestActiveXid = InvalidTransactionId;
7238+
else if (!TransactionIdIsValid(checkPoint.oldestActiveXid))
7239+
{
7240+
/*
7241+
* It should not actually happen: PS oldestActiveXid
7242+
* from running xacts WAL records and include it in checkpoint
7243+
* sent in basebackup.
7244+
* FirstNormalTransactionId is conservative estimation of oldest active XACT, unless
7245+
* current XID is greater than 1^31. So it is also not 100% safe solution but better than assertion failure.
7246+
*/
7247+
elog(FATAL, "oldestActiveXid=%d", checkPoint.oldestActiveXid);
7248+
checkPoint.oldestActiveXid = FirstNormalTransactionId;
7249+
}
7250+
}
7251+
72047252
/* initialize shared memory variables from the checkpoint record */
72057253
ShmemVariableCache->nextXid = checkPoint.nextXid;
72067254
ShmemVariableCache->nextOid = checkPoint.nextOid;

0 commit comments

Comments
 (0)