From 71504c8ad74d3e29c1586205a90065ad1e41b28d Mon Sep 17 00:00:00 2001 From: Bharath Rupireddy Date: Sun, 13 Sep 2026 22:53:04 +0000 Subject: [PATCH v9 1/5] Add statistics for WAL reads from WAL buffers in pg_stat_io. Commit a051e71e28a started tracking IO on WAL in pg_stat_io, and reports a read from WALRead(), where a read from a file happens. Previously, a read from the WAL buffers, done by WALReadFromBuffers() added by commit 91f2cae7a4 and used by physical replication walsenders, was not reported at all, because such a read never reaches WALRead(). As a result, a walsender that keeps up with the insertion point reads WAL continuously and still reports no read activity, and reads staying at zero do not tell whether no WAL was read or all of it came from the WAL buffers. This commit reports a read from the WAL buffers as a hit on object wal, so that it can be distinguished from a read from a file without adding a new column to pg_stat_io. IOOP_HIT is now tracked for IOOBJECT_WAL in IOCONTEXT_NORMAL, for the backend types that read WAL. With this change, hits, which was NULL for the WAL rows of pg_stat_io, is now reported for those backend types. This commit also adjusts a TAP test that waits for a physical walsender to report WAL read activity, because such a read is now reported as a hit and not as a read. Note that hits reports how many reads came from the WAL buffers, like reads does for reads from a file, but not how many bytes, as there is no hit_bytes column in pg_stat_io matching read_bytes. Adding one is left as future work. Author: Bharath Rupireddy Discussion: https://www.postgresql.org/message-id/CALj2ACVfF2Uj9NoFy-5m98HNtjHpuD17EDE9twVeJng-jTAe7A%40mail.gmail.com --- doc/src/sgml/monitoring.sgml | 9 ++++++++- src/backend/access/transam/xlog.c | 16 ++++++++++++++-- src/backend/utils/activity/pgstat_io.c | 14 ++++++++------ src/test/recovery/t/001_stream_rep.pl | 5 +++-- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 49bf6b51c49..41463584a12 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -3174,7 +3174,9 @@ description | Waiting for a newly initialized WAL file to reach durable storage reads bigint - Number of read operations. + Number of read operations. When object is + wal, reads that come from the WAL buffers are + counted in hits instead. @@ -3317,6 +3319,11 @@ description | Waiting for a newly initialized WAL file to reach durable storage The number of times a desired block was found in a shared buffer. + When object is wal, the + number of read operations that found the requested WAL in the WAL + buffers, fully or in part; a read that found only part of it is + counted in reads as well, for the rest + of it, which comes from a file. diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index d6f4fb2a99a..26f67dbfcfc 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -1826,6 +1826,7 @@ WALReadFromBuffers(char *dstbuf, XLogRecPtr startptr, Size count, XLogRecPtr recptr = startptr; XLogRecPtr inserted; Size nbytes = count; + Size nread; if (RecoveryInProgress() || tli != GetWALInsertionTimeLine()) return 0; @@ -1918,9 +1919,20 @@ WALReadFromBuffers(char *dstbuf, XLogRecPtr startptr, Size count, nbytes -= npagebytes; } - Assert(pdst - dstbuf <= count); + nread = pdst - dstbuf; - return pdst - dstbuf; + Assert(nread <= count); + + /* + * Report the read as a hit on the WAL buffers, so that it can be + * distinguished from a read from a file. A read that finds only part of + * the requested WAL here is also reported by the file read that the + * caller then does, for the rest of it. + */ + if (nread > 0) + pgstat_count_io_op(IOOBJECT_WAL, IOCONTEXT_NORMAL, IOOP_HIT, 1, 0); + + return nread; } /* diff --git a/src/backend/utils/activity/pgstat_io.c b/src/backend/utils/activity/pgstat_io.c index 8ec1aad5078..b70227431bd 100644 --- a/src/backend/utils/activity/pgstat_io.c +++ b/src/backend/utils/activity/pgstat_io.c @@ -515,9 +515,8 @@ pgstat_tracks_io_op(BackendType bktype, IOObject io_object, (io_op == IOOP_READ || io_op == IOOP_EVICT || io_op == IOOP_HIT)) return false; - if (bktype == B_CHECKPOINTER && - ((io_object != IOOBJECT_WAL && io_op == IOOP_READ) || - (io_op == IOOP_EVICT || io_op == IOOP_HIT))) + if (bktype == B_CHECKPOINTER && io_object != IOOBJECT_WAL && + (io_op == IOOP_READ || io_op == IOOP_EVICT || io_op == IOOP_HIT)) return false; if ((bktype == B_BG_WRITER || bktype == B_CHECKPOINTER) && @@ -525,9 +524,11 @@ pgstat_tracks_io_op(BackendType bktype, IOObject io_object, return false; /* - * Some BackendTypes do not perform reads with IOOBJECT_WAL. + * Some BackendTypes do not read WAL, either from a file or from the WAL + * buffers. */ - if (io_object == IOOBJECT_WAL && io_op == IOOP_READ && + if (io_object == IOOBJECT_WAL && + (io_op == IOOP_READ || io_op == IOOP_HIT) && (bktype == B_WAL_RECEIVER || bktype == B_BG_WRITER || bktype == B_AUTOVAC_LAUNCHER || bktype == B_AUTOVAC_WORKER || bktype == B_DATACHECKSUMSWORKER_LAUNCHER || @@ -567,7 +568,8 @@ pgstat_tracks_io_op(BackendType bktype, IOObject io_object, return false; if (io_object == IOOBJECT_WAL && io_context == IOCONTEXT_NORMAL && - !(io_op == IOOP_WRITE || io_op == IOOP_READ || io_op == IOOP_FSYNC)) + !(io_op == IOOP_WRITE || io_op == IOOP_READ || io_op == IOOP_FSYNC || + io_op == IOOP_HIT)) return false; /* diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl index a4fa4b96c61..3d6a2f152c4 100644 --- a/src/test/recovery/t/001_stream_rep.pl +++ b/src/test/recovery/t/001_stream_rep.pl @@ -343,10 +343,11 @@ note "switching to physical replication slot"; # Wait for the physical WAL sender to update its IO statistics. This is # done before the next restart, which would force a flush of its stats, and -# far enough from the reset done above to not impact the run time. +# far enough from the reset done above to not impact the run time. A WAL sender +# reads WAL either from a file or from the WAL buffers, so count both. $node_primary->poll_query_until( 'postgres', - qq[SELECT sum(reads) > 0 + qq[SELECT sum(reads) + sum(hits) > 0 FROM pg_catalog.pg_stat_io WHERE backend_type = 'walsender' AND object = 'wal'] -- 2.47.3