@@ -542,6 +542,18 @@ typedef union WALInsertLockPadded
542
542
char pad [PG_CACHE_LINE_SIZE ];
543
543
} WALInsertLockPadded ;
544
544
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
+
545
557
/*
546
558
* State of an exclusive backup, necessary to control concurrent activities
547
559
* across sessions when working on exclusive backups.
@@ -7031,7 +7043,25 @@ StartupXLOG(void)
7031
7043
EndRecPtr = ControlFile -> checkPointCopy .redo ;
7032
7044
7033
7045
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;
7035
7065
7036
7066
/* Initialize expectedTLEs, like ReadRecord() does */
7037
7067
expectedTLEs = readTimeLineHistory (checkPoint .ThisTimeLineID );
@@ -7201,6 +7231,24 @@ StartupXLOG(void)
7201
7231
ereport (PANIC ,
7202
7232
(errmsg ("invalid next transaction ID" )));
7203
7233
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
+
7204
7252
/* initialize shared memory variables from the checkpoint record */
7205
7253
ShmemVariableCache -> nextXid = checkPoint .nextXid ;
7206
7254
ShmemVariableCache -> nextOid = checkPoint .nextOid ;
0 commit comments