From 5cfdb02ef6d0697e5670356345fc89af78d1eeb9 Mon Sep 17 00:00:00 2001 From: Anthonin Bonnefoy Date: Thu, 12 Mar 2026 14:29:21 +0100 Subject: Fix flushing record ending at page boundary In 6eedb2a5fd, a call to XLogFlush(GetXLogInsertRecPtr()) has been added to allow walsender to flush the latest WAL record. However, if the last record is at the end of a page, GetXLogInsertRecPtr() will return the start position for the next record, which will be located in the next page, after the page header. XLogInsert will complain with a 'xlog flush request 0/03604018 is not satisfied --- flushed only to 0/03604000' error, as the flush request tries to write WAL that hasn't been reserved yet. This patch fixes the issue by introducing and using a GetXLogInsertEndRecPtr() which stops at the page boundary, instead of the beginning of the next page. --- src/backend/access/transam/xlog.c | 18 ++++++++++++++++++ src/backend/replication/logical/syncutils.c | 2 +- src/backend/replication/walsender.c | 2 +- src/include/access/xlog.h | 1 + 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index b9b678f3722..9fd90636ee1 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -9595,6 +9595,24 @@ GetXLogInsertRecPtr(void) return XLogBytePosToRecPtr(current_bytepos); } +/* + * Like GetXLogInsertRecPtr, but if the position is at a page boundary, returns + * a pointer to the beginning of the page (ie. before page header), not to where + * the first xlog record on that page would go to. + */ +XLogRecPtr +GetXLogInsertEndRecPtr(void) +{ + XLogCtlInsert *Insert = &XLogCtl->Insert; + uint64 current_bytepos; + + SpinLockAcquire(&Insert->insertpos_lck); + current_bytepos = Insert->CurrBytePos; + SpinLockRelease(&Insert->insertpos_lck); + + return XLogBytePosToEndRecPtr(current_bytepos); +} + /* * Get latest WAL write pointer */ diff --git a/src/backend/replication/logical/syncutils.c b/src/backend/replication/logical/syncutils.c index ef61ca0437d..8c5da44d42e 100644 --- a/src/backend/replication/logical/syncutils.c +++ b/src/backend/replication/logical/syncutils.c @@ -62,7 +62,7 @@ FinishSyncWorker(void) } /* And flush all writes. */ - XLogFlush(GetXLogWriteRecPtr()); + XLogFlush(GetXLogInsertEndRecPtr()); if (am_sequencesync_worker()) { diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index 79fc192b171..dd46de7bcd6 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -1887,7 +1887,7 @@ WalSndWaitForWal(XLogRecPtr loc) * written, because walwriter has shut down already. */ if (got_STOPPING && !RecoveryInProgress()) - XLogFlush(GetXLogInsertRecPtr()); + XLogFlush(GetXLogInsertEndRecPtr()); /* * To avoid the scenario where standbys need to catch up to a newer diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index fdfb572467b..958f39edda4 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -238,6 +238,7 @@ extern bool RecoveryInProgress(void); extern RecoveryState GetRecoveryState(void); extern bool XLogInsertAllowed(void); extern XLogRecPtr GetXLogInsertRecPtr(void); +extern XLogRecPtr GetXLogInsertEndRecPtr(void); extern XLogRecPtr GetXLogWriteRecPtr(void); extern uint64 GetSystemIdentifier(void); -- 2.53.0