From 55bf797f9a206291fb892b48dce4f94f126a4ef4 Mon Sep 17 00:00:00 2001 From: alterego655 <824662526@qq.com> Date: Sun, 18 Oct 2026 19:21:59 +0800 Subject: [PATCH v12 1/2] Replace polling with LSN waits in read_local_xlog_page_guts() Use WaitForLSN() instead of sleeping for 1 ms between checks when the requested WAL is not yet available. This avoids periodic wakeups and lets readers respond to WAL progress without waiting for the next polling interval. Recheck the timeline and read limit after waiting, as before. Local promotion releases the replay wait so the next iteration can use the flush position. An upstream timeline switch needs no separate wakeup: before the requested segment can be read as historical WAL, replay must pass that segment, satisfying the LSN wait. Requests in the switch segment or later still require replay to reach the requested position. In one sampled run, CPU cycles fell by approximately 39%, instructions by 14%, cache misses by 28%, and context switches by 23%, with similar elapsed time. Preserve the no-wait behavior. WaitForLSN() handles interrupts, so the separate CHECK_FOR_INTERRUPTS() before the wait is unnecessary. --- src/backend/access/transam/xlogutils.c | 26 ++++++++++++++++++-------- src/backend/replication/walsender.c | 4 ---- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c index ff71e14015d..0fa76cd8846 100644 --- a/src/backend/access/transam/xlogutils.c +++ b/src/backend/access/transam/xlogutils.c @@ -23,6 +23,7 @@ #include "access/xlogrecovery.h" #include "access/xlog_internal.h" #include "access/xlogutils.h" +#include "access/xlogwait.h" #include "miscadmin.h" #include "storage/fd.h" #include "storage/smgr.h" @@ -907,12 +908,7 @@ read_local_xlog_page_guts(XLogReaderState *state, XLogRecPtr targetPagePtr, loc = targetPagePtr + reqLen; /* - * Loop waiting for xlog to be available if necessary - * - * TODO: The walsender has its own version of this function, which uses a - * condition variable to wake up whenever WAL is flushed. We could use the - * same infrastructure here, instead of the check/sleep/repeat style of - * loop. + * Loop waiting for xlog to be available if necessary. */ while (1) { @@ -985,8 +981,22 @@ read_local_xlog_page_guts(XLogReaderState *state, XLogRecPtr targetPagePtr, break; } - CHECK_FOR_INTERRUPTS(); - pg_usleep(1000L); + /* + * Wait for the flush or replay position to reach loc, then + * recheck the timeline and read limit above. Ignore the result: + * success still requires those checks, and if recovery has ended, + * the next iteration uses the flush position instead. + * + * An upstream promotion can change our timeline without ending + * recovery. No separate timeline-change wakeup is needed: before + * the requested segment can be read as historical WAL, replay must + * pass that segment, satisfying this wait. Requests in the segment + * containing the switch or later still require replay to reach loc. + */ + (void) WaitForLSN(RecoveryInProgress() ? + WAIT_LSN_TYPE_STANDBY_REPLAY : + WAIT_LSN_TYPE_PRIMARY_FLUSH, + loc, -1); } else { diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index 10bc3e0cae4..49534949a96 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -1084,10 +1084,6 @@ StartReplication(StartReplicationCmd *cmd) /* * XLogReaderRoutine->page_read callback for logical decoding contexts, as a * walsender process. - * - * Inside the walsender we can do better than read_local_xlog_page, - * which has to do a plain sleep/busy loop, because the walsender's latch gets - * set every time WAL is flushed. */ static int logical_read_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen, -- 2.51.0