Skip to content

Commit 46fed80

Browse files
committed
Set xl_prev correctly if replica is promoted without replaying any records
Every WAL record in Postgres has a pointer to the start of the previous record (xl_prev). Postgres keeps track of the last record that was written in variable in shared memory, so which is then used on the next record to fill that field. When Postgres starts up after a clean shutdown, the variable is initialized to point ot the checkpoint record. If the system was not shut down cleanly, Postgres performs WAL recovery, the variable is initialized to point to the last replayed WAL record. In Neon however, we can start up from any record, without performing WAL recovery. When we do that, there is no last replayed WAL record to initialize it from, and there's not real checkpoint record either - we fake the variables related to starting checkpoint. The "neon signal file" that is included in the basebackup must include an explicit LSN to use as the xl_prev value in such cases ("PREV LSN") field, and we use that. However, that did not work correctly in versions 15 and above, when a replica was promoted immediately after startup, without replaying any records.As a result, the promoted standby wrote an end-of-WAL record with an incorrect xl_prev field, which trips over any read replicas, that are following across the switchover. It also trips over logical replication across that point. Add a test and fix it.
1 parent 41fb35f commit 46fed80

File tree

1 file changed

+16
-1
lines changed

1 file changed

+16
-1
lines changed

src/backend/access/transam/xlogrecovery.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1807,7 +1807,22 @@ PerformWalRecovery(void)
18071807
* checkpoint record itself, if it's a shutdown checkpoint).
18081808
*/
18091809
SpinLockAcquire(&XLogRecoveryCtl->info_lck);
1810-
if (RedoStartLSN < CheckPointLoc)
1810+
if (NeonRecoveryRequested)
1811+
{
1812+
/*
1813+
* In Neon recovery mode, we can start from any record, not only at a
1814+
* checkpoint. The Neon signal file includes an explicit "PREV LSN"
1815+
* field, which is the LSN of the previous record, before the point at
1816+
* which we start up. Initialize lastReplayedRecPtr from that, as if
1817+
* we had just replayed that record.
1818+
*/
1819+
Assert(xlogreader->ReadRecPtr == InvalidXLogRecPtr);
1820+
Assert(xlogreader->EndRecPtr == RedoStartLSN);
1821+
XLogRecoveryCtl->lastReplayedEndRecPtr = RedoStartLSN;
1822+
XLogRecoveryCtl->lastReplayedReadRecPtr = neonLastRec;
1823+
XLogRecoveryCtl->lastReplayedTLI = CheckPointTLI;
1824+
}
1825+
else if (RedoStartLSN < CheckPointLoc)
18111826
{
18121827
XLogRecoveryCtl->lastReplayedReadRecPtr = InvalidXLogRecPtr;
18131828
XLogRecoveryCtl->lastReplayedEndRecPtr = RedoStartLSN;

0 commit comments

Comments
 (0)