v3-0002-Fix-memory-ordering-in-WAIT-FOR-LSN-wakeup-mechan.patch

application/octet-stream

Filename: v3-0002-Fix-memory-ordering-in-WAIT-FOR-LSN-wakeup-mechan.patch
Type: application/octet-stream
Part: 0
Message: Re: Implement waiting for wal lsn replay: reloaded

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v3-0002
Subject: Fix memory ordering in WAIT FOR LSN wakeup mechanism
File+
src/backend/access/transam/xlogwait.c 20 3
From 31f4549c1f271563eb9191478e6766c0848bde07 Mon Sep 17 00:00:00 2001
From: Alexander Korotkov <akorotkov@postgresql.org>
Date: Thu, 9 Apr 2026 18:08:06 +0300
Subject: [PATCH v3 2/5] Fix memory ordering in WAIT FOR LSN wakeup mechanism

WAIT FOR LSN uses a Dekker-style handshake: the waker stores an LSN position
then reads minWaitedLSN; the waiter stores its target into minWaitedLSN then
reads the position.  Without a barrier between each side's store and load,
the CPU may satisfy the load before the store becomes globally visible,
causing the waker to miss a just-registered waiter and the waiter to miss
a just-advanced position.  The result is a missed wakeup: the waiter sleeps
indefinitely until the next unrelated event.

Fix by adding pg_memory_barrier() in WaitLSNWakeup() before
reading minWaitedLSN (waker side) and explain that GetCurrentLSNForWaitType()
now has the memory barrier before reading the current position (waiter side).

Reported-by: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/zqbppucpmkeqecfy4s5kscnru4tbk6khp3ozqz6ad2zijz354k%40w4bdf4z3wqoz
Author: Xuneng Zhou <xunengzhou@gmail.com>
---
 src/backend/access/transam/xlogwait.c | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)

diff --git a/src/backend/access/transam/xlogwait.c b/src/backend/access/transam/xlogwait.c
index 2e31c0d67d7..5be84bd4566 100644
--- a/src/backend/access/transam/xlogwait.c
+++ b/src/backend/access/transam/xlogwait.c
@@ -92,13 +92,19 @@ StaticAssertDecl(lengthof(WaitLSNWaitEvents) == WAIT_LSN_TYPE_COUNT,
 				 "WaitLSNWaitEvents must match WaitLSNType enum");
 
 /*
- * Get the current LSN for the specified wait type.
+ * Get the current LSN for the specified wait type.  Provide memory
+ * barrier semantics before getting the value.
  */
 XLogRecPtr
 GetCurrentLSNForWaitType(WaitLSNType lsnType)
 {
 	Assert(lsnType >= 0 && lsnType < WAIT_LSN_TYPE_COUNT);
 
+	/*
+	 * All of the cases below provides memory barrier semantics:
+	 * GetWalRcvWriteRecPtr() and GetFlushRecPtr() have explicit barriers,
+	 * while GetXLogReplayRecPtr() and GetWalRcvFlushRecPtr() use spinlocks.
+	 */
 	switch (lsnType)
 	{
 		case WAIT_LSN_TYPE_STANDBY_REPLAY:
@@ -323,6 +329,18 @@ WaitLSNWakeup(WaitLSNType lsnType, XLogRecPtr currentLSN)
 
 	Assert(i >= 0 && i < WAIT_LSN_TYPE_COUNT);
 
+	/*
+	 * Ensure the waker's prior position store (writtenUpto, flushedUpto,
+	 * lastReplayedEndRecPtr, etc.) is globally visible before we read
+	 * minWaitedLSN.  Without this barrier, the CPU could load minWaitedLSN
+	 * before draining the position store, leaving the position invisible to a
+	 * concurrently-registering waiter.
+	 *
+	 * This is the waker side of a Dekker-style handshake; pairs with
+	 * pg_memory_barrier() in GetCurrentLSNForWaitType() on the waiter side.
+	 */
+	pg_memory_barrier();
+
 	/*
 	 * Fast path check.  Skip if currentLSN is InvalidXLogRecPtr, which means
 	 * "wake all waiters" (e.g., during promotion when recovery ends).
@@ -450,8 +468,7 @@ WaitForLSN(WaitLSNType lsnType, XLogRecPtr targetLSN, int64 timeout)
 					errmsg("terminating connection due to unexpected postmaster exit"),
 					errcontext("while waiting for LSN"));
 
-		if (rc & WL_LATCH_SET)
-			ResetLatch(MyLatch);
+		ResetLatch(MyLatch);
 	}
 
 	/*
-- 
2.39.5 (Apple Git-154)