From d33150f09585fa0ca20ab3919428a7e211a28521 Mon Sep 17 00:00:00 2001
From: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Date: Mon, 26 Oct 2026 13:50:58 +0000
Subject: [PATCH v4 1/8] Update buffer strategy descriptions in the buffer
 README

The buffer README still describes the bulk-read strategy as using a fixed
256kB ring.

Commit 15f0cb26b530 changed this but forgot to update the README: the requested
ring size now starts at 256kB and grows to accommodate the configured I/O
concurrency, subject to the backend pin limit and the general cap of one eighth
of shared_buffers. The actual ring can therefore be smaller than 256kB.

The README also contains an outdated list of operations that use the bulk-write
strategy. Remove it rather than replacing it with another list that could
become stale as callers change.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Sami Imseih <samimseih.pg@gmail.com>
Discussion: https://postgr.es/m/auCYYbDyrQEwIsQq@bdtpg
---
 src/backend/storage/buffer/README | 39 +++++++++++++++++--------------
 1 file changed, 22 insertions(+), 17 deletions(-)
 100.0% src/backend/storage/buffer/

diff --git a/src/backend/storage/buffer/README b/src/backend/storage/buffer/README
index b332e002ba1..8f42835935a 100644
--- a/src/backend/storage/buffer/README
+++ b/src/backend/storage/buffer/README
@@ -216,19 +216,26 @@ whole scan.  This also implies that much of the write traffic caused by such
 a statement will be done by the backend itself and not pushed off onto other
 processes.
 
-For sequential scans, a 256KB ring is used. That's small enough to fit in L2
-cache, which makes transferring pages from OS cache to shared buffer cache
-efficient.  Even less would often be enough, but the ring must be big enough
-to accommodate all pages in the scan that are pinned concurrently.  256KB
-should also be enough to leave a small cache trail for other backends to
-join in a synchronized seq scan.  If a ring buffer is dirtied and its LSN
-updated, we would normally have to write and flush WAL before we could
-re-use the buffer; in this case we instead discard the buffer from the ring
-and (later) choose a replacement using the normal clock-sweep algorithm.
-Hence this strategy works best for scans that are read-only (or at worst
-update hint bits).  In a scan that modifies every page in the scan, like a
-bulk UPDATE or DELETE, the buffers in the ring will always be dirtied and
-the ring strategy effectively degrades to the normal strategy.
+For bulk reads, the requested ring size is at least 256KB. The 256KB target is
+small enough to fit in L2 cache, which makes transferring pages from OS cache
+to shared buffer cache efficient.  Even less would often be enough, but the
+target must be big enough to accommodate all pages in the scan that are
+pinned concurrently.  The 256KB target should also be enough to leave a small
+cache trail for other backends to join in a synchronized seq scan.
+
+The requested ring size is enlarged to accommodate io_combine_limit *
+effective_io_concurrency buffers while I/O is in progress, but no further
+than the backend's pin limit allows.  Like all strategy rings, the actual size
+is capped at 1/8th of shared_buffers and can therefore be less than 256KB.
+
+If a ring buffer is dirtied and its LSN updated, we would normally have to
+write and flush WAL before we could re-use the buffer; in this case we instead
+discard the buffer from the ring and (later) choose a replacement using the
+normal clock-sweep algorithm.  Hence this strategy works best for scans that
+are read-only (or at worst update hint bits).  In a scan that modifies every
+page in the scan, like a bulk UPDATE or DELETE, the buffers in the ring will
+always be dirtied and the ring strategy effectively degrades to the normal
+strategy.
 
 VACUUM uses a ring like sequential scans, however, the size of this ring is
 controlled by the vacuum_buffer_usage_limit GUC.  Dirty pages are not removed
@@ -237,10 +244,8 @@ buffers.  Before introducing the buffer ring strategy in 8.3, VACUUM's buffers
 were sent to the freelist, which was effectively a buffer ring of 1 buffer,
 resulting in excessive WAL flushing.
 
-Bulk writes work similarly to VACUUM.  Currently this applies only to
-COPY IN and CREATE TABLE AS SELECT.  (Might it be interesting to make
-seqscan UPDATE and DELETE use the bulkwrite strategy?)  For bulk writes
-we use a ring size of 16MB (but not more than 1/8th of shared_buffers).
+Bulk writes work similarly to VACUUM.  For bulk writes we use a ring size
+of 16MB (but not more than 1/8th of shared_buffers).
 Smaller sizes have been shown to result in the COPY blocking too often
 for WAL flushes.  While it's okay for a background vacuum to be slowed by
 doing its own WAL flushing, we'd prefer that COPY not be subject to that,
-- 
2.34.1

