On 3/5/26 21:25, Andres Freund wrote:
> Hi,
> 
> On 2026-03-05 10:52:03 -0800, Noah Misch wrote:
>> On Thu, Mar 05, 2026 at 12:10:11PM -0500, Andres Freund wrote:
>>> Tomas encountered a crash with the index prefetching patchset. One of the
>>> patches included therein is a generalization of the gistGetFakeLSN()
>>> mechanism, which is then used by other indexes as well.  That triggered an
>>> occasional, hard to locally reproduce, ERROR or PANIC in CI, about
>>>
>>>   ERROR: xlog flush request 0/01BD2018 is not satisfied --- flushed only to 
>>> 0/01BD2000
>>
>>> To be safe, this code would need to use a version of GetXLogInsertRecPtr()
>>> that does use XLogBytePosToEndRecPtr() instead of XLogBytePosToRecPtr().
>>
>> I agree.  Thanks for diagnosing it.  Feel free to move forward with that
>> strategy, or let me know if you'd like me to do it.
> 
> I'd appreciate if you could do it.
> 

Here's a fix for master (and backpatching). It introduces a new function
GetXLogInsertEndRecPtr() and then uses that in gistGetFakeLSN(). I still
need to test this a bit more, but it did fix the issue in our dev branch
(where we saw regular failures). So I'm 99% sure it's fine.

After writing the fix I had the idea to grep for GetXLogInsertRecPtr
calls that might have similar issue (being passed to XLogFlush), and
sure enough walsender does this:

    XLogFlush(GetXLogInsertRecPtr());

Which AFAICS has the same issue, right? Funnily enough, this is a very
new call, from 2026/03/06. Before 6eedb2a5fd88 walsender might flush not
far enough, now it may be flushing too far ;-) AFAIK it should call the
same GetXLogInsertEndRecPtr() once we have it.


regards

-- 
Tomas Vondra
From 7298812c6badd55a8852e31ced84d60b5bd49e94 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <[email protected]>
Date: Fri, 13 Mar 2026 11:28:04 +0100
Subject: [PATCH] Use GetXLogInsertEndRecPtr in gistGetFakeLSN

The function used GetXLogInsertRecPtr() to generate the fake LSN. Most
of the time this is the same as what XLogInsert() would return, and so
it works fine with the XLogFlush() call. But if the last record ends at
a page boundary, GetXLogInsertRecPtr() returns LSN pointing after the
page header. In such case XLogFlush() fails with errors like this:

  ERROR: xlog flush request 0/01BD2018 is not satisfied --- flushed only to 0/01BD2000

Such failures are very hard to trigger, particularly outside aggressive
test scenarios.

Fixed by introducing GetXLogInsertEndRecPtr(), returning the correct LSN
without skipping the header. This is the same as GetXLogInsertRecPtr(),
except that it calls XLogBytePosToEndRecPtr().

This is a long-standing bug in gistGetFakeLSN(), probably introduced by
c6b92041d38. Initial investigation by me, root cause identified by
Andres Freund.

Reported-by: Peter Geoghegan <[email protected]>
Reviewed-by: Andres Freund <[email protected]>
Reviewed-by: Noah Misch <[email protected]>
Discussion: https://postgr.es/m/vf4hbwrotvhbgcnknrqmfbqlu75oyjkmausvy66ic7x7vuhafx@e4rvwavtjswo
---
 src/backend/access/gist/gistutil.c |  2 +-
 src/backend/access/transam/xlog.c  | 16 ++++++++++++++++
 src/include/access/xlog.h          |  1 +
 3 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 27972fad2b0..e7e4b273e19 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -1034,7 +1034,7 @@ gistGetFakeLSN(Relation rel)
 		 * last call.
 		 */
 		static XLogRecPtr lastlsn = InvalidXLogRecPtr;
-		XLogRecPtr	currlsn = GetXLogInsertRecPtr();
+		XLogRecPtr	currlsn = GetXLogInsertEndRecPtr();
 
 		/* Shouldn't be called for WAL-logging relations */
 		Assert(!RelationNeedsWAL(rel));
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index b9b678f3722..f760291e10c 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -9595,6 +9595,22 @@ GetXLogInsertRecPtr(void)
 	return XLogBytePosToRecPtr(current_bytepos);
 }
 
+/*
+ * Get latest WAL record end pointer
+ */
+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/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

Reply via email to