From: Greg Burd <greg@burd.me>
To: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Cc: Tomas Vondra <tomas@vondra.me>
Cc: Nathan Bossart <nathandbossart@gmail.com>
Cc: Andres Freund <andres@anarazel.de>
Subject: Re: [PATCH] Batched clock sweep to reduce cross-socket atomic contention
Date: Tue, 14 Jul 2026 13:53:06 -0400
Message-ID: <96dc1db7-58a2-41ba-ae2e-bea86fa2fb3a@app.fastmail.com> (raw)
In-Reply-To: <9cfdf3f2-b799-485a-8b5a-5159c00ff2df@app.fastmail.com>
References: <79629577-3ad8-4b1c-a469-ebc2cb4c5104@app.fastmail.com>
<nwri4oxfusruo7yog2rewbeiegs4rhclkjukn54mcumiilak4b@6chlhxypm5sq>
<618241c3-a1e5-4a16-be12-17a361822ee0@app.fastmail.com>
<5bcaa70b-89c7-44b6-b197-638d10997cae@app.fastmail.com>
<76c33898-3baa-4f7b-8b72-57dd390fcffe@app.fastmail.com>
<9cfdf3f2-b799-485a-8b5a-5159c00ff2df@app.fastmail.com>
Hi,
v6 is attached, rebased on master (d7cd5d6).
0001 Batch the clock sweep to reduce nextVictimBuffer atomic contention
0002 Replace the usage_count clock sweep with a cooling-stage evictor
0003 Remove BufferAccessStrategy; scan resistance is now intrinsic
Two kinds of change since v5: three correctness fixes, and one bgwriter
tweak (folded into 0002) that deals with a regression I found while chasing
down whether removing StrategyRejectBuffer would hurt. Most of this mail is
that second thing, with numbers, because it is the part I most want picked
apart.
The three fixes first, briefly:
stats.out was a real bug. Once the rings are gone in 0003, pg_stat_io's
"reuses" column is NULL for every row, so the reset test's sum(reuses) came
back NULL, the \gset made an empty variable, and the next line died with
"syntax error at or near ':'". I dropped the now-meaningless reuses term
from the reset-test sums and regenerated the expected output.
force_cool now honours the reference bit. When the sweep has found no COOL
victim in a full pass and starts demoting HOT buffers, it applies the same
single second-chance ref-bit rule the bgwriter uses instead of demoting
unconditionally, so a buffer touched mid-pass isn't demoted a tick later.
And completePasses now counts an intra-batch wrap: with 0001's batched claim
a batch can straddle the NBuffers wrap, and that wasn't being reflected, so
the bgwriter's pass accounting could drift by up to a batch.
Let's talk regression...
The buffer cache ring strategy didn't only bound cache pollution, it also
deferred writeback. A backend running a big COPY, bulk UPDATE or VACUUM
reused a small ring and, via StrategyRejectBuffer, pushed dirty victims back
for the bgwriter and checkpointer to write instead of writing (and
WAL-flushing) them inline on its own allocation path. Pull the rings (as in
0003) and, done naively, that deferral goes too.
I wrote a small benchmark to see how much that costs. It's on an AWS
m6id.metal (Sapphire Rapids, 128 vCPU, 512 GB), with the data and WAL on the
instance-local NVMe (mdraid-0), not EBS. Both builds are meson
debugoptimized, cassert off, libnuma on; stock is master at d15a6bc2e16,
patched is the series on the same base.
Server pinned with "numactl --cpunodebind=0 --interleave=all",
non-default GUCs, same for both:
shared_buffers = 8GB # small enough that a bulk op spills
huge_pages = try
maintenance_work_mem = 1GB
max_wal_size = 64GB
min_wal_size = 8GB
checkpoint_timeout = 30min
checkpoint_completion_target = 0.9
wal_level = replica
fsync = on
synchronous_commit = on
track_io_timing = on
The table is ~4GB (20M rows, 180-byte payload, fillfactor 90), half of
shared_buffers, so a whole-table dirtying op has to evict dirty victims.
Each of the three statements runs on its own fresh cluster, three times,
median reported; IO stats are reset right before each timed statement so
the write count is per-statement, not cumulative:
CREATE TABLE bulk (id int, pad text) WITH (fillfactor=90);
INSERT INTO bulk SELECT g, repeat('x',180) FROM generate_series(1,2e7) g;
VACUUM (FREEZE) bulk; CHECKPOINT;
-- (1) UPDATE, dirties every page
UPDATE bulk SET id = id + 1;
-- (2) bulk INSERT into an empty table
CREATE TABLE ins (id int, pad text);
INSERT INTO ins SELECT g, repeat('y',180) FROM generate_series(1,2e7) g;
-- (3) VACUUM after dirtying half the pages
UPDATE bulk SET pad = repeat('z',180) WHERE id % 2 = 0; CHECKPOINT;
VACUUM bulk;
One caveat up front: (2) is INSERT...SELECT, not the COPY command. It hits
the same relation-extend + WAL-logged-write path BAS_BULKWRITE served, which
is what I wanted to stress, but if that distinction matters to anyone's
conclusion I'll re-run with COPY FROM.
For each statement I record time_s (wall seconds), bwrites (buffers the
backend wrote itself -- sum(writes) from pg_stat_io where object='relation'
after a forced flush, i.e. the writes the rings used to hand off), and
wal_gb (LSN delta, just a check the two builds did the same work). Full
series, Option B included:
workload metric stock patched impact of patches
------------ --------- ---------- ---------- ------------------
bulk UPDATE time_s 26.0 27.3 5% slower
bwrites 90754 158165 74% more
wal_gb 5.83 5.90 +1%
bulk INSERT time_s 14.2 15.6 10% slower
bwrites 0 0 none; extend path
wal_gb 5.00 5.11 +2%
VACUUM time_s 11.4 9.3 19% faster
bwrites 996255 390945 61% fewer
wal_gb 6.80 6.77 -1%
Per-run time_s so you can see the spread (stock / patched):
UPDATE 29.0 25.9 26.0 / 29.4 27.2 27.3
INSERT 15.1 14.2 13.4 / 15.5 15.6 15.7
VACUUM 11.5 11.4 11.4 / 9.2 9.3 9.4
The 5% and 10% are only a couple of run-to-run wobbles apart, but the
direction is steady across runs; the VACUUM win and the bwrites jump are
well clear of the noise.
That +74% bwrites on UPDATE is the crux: the backend is doing the writeback
the rings used to defer. Before I added the bgwriter tweak it was worse --
8% slower, 77% more writes -- and VACUUM was only 8% faster rather than 19%.
Two things I tried. First, having the sweep prefer a clean COOL victim and
skip dirty ones (bounded, so it can't turn into a full-pool rescan). That
made UPDATE *slower still*, 14% over stock, because under bulk dirtying the
clean victims simply aren't there and the skipping is wasted work. I threw
that out.
What stuck instead was letting the bgwriter keep up. Its per-cycle
clean-write cap is bgwriter_lru_maxpages, default 100; under bulk dirtying
the pool fills with dirty COOL buffers faster than 100/cycle can clean, the
bgwriter stalls at the cap, and the backend is left flushing victims itself.
So when the bgwriter's own predicted demand for the next cycle already
exceeds the cap, I let the cap follow demand:
write_limit = bgwriter_lru_maxpages;
if (upcoming_alloc_est > write_limit)
write_limit = upcoming_alloc_est;
It's still bounded -- by demand and by lapping the strategy point -- so it
can't run away, and anything whose demand sits below the cap
(i.e. everything that isn't bulk-dirtying) is untouched. A small change in
BgBufferSync, folded into 0002 in v6.
That halves the UPDATE slowdown and flips VACUUM into a clear win, but it
doesn't erase the residual, so I ran one more comparison to see where the
residual actually lives: patches 0001+0002 (rings still in place, 0003 not
applied). Same instance type, same three runs:
workload metric stock 0001+0002 impact
------------ --------- ---------- ---------- ------------------
bulk UPDATE time_s 25.8 25.4 1% faster
bwrites 90797 122416 35% more
bulk INSERT time_s 14.0 15.2 8% slower
bwrites 0 ~0 extend path
VACUUM time_s 11.5 9.7 15% faster
bwrites 986865 500190 49% fewer
So with the rings still doing their deferral, the new evictor on its own is
a touch faster on UPDATE and already a solid win on VACUUM. The 5% UPDATE
slowdown in the full series is therefore the price of removing the rings
(0003) specifically -- not the evictor -- and now I can say that against a
rings-kept baseline rather than just asserting it. The bgwriter tweak moves
those writes off the critical path (why the time recovers to 5%) but doesn't
reduce their number; some of that is just the cost of not having a private
ring to write-behind from.
Bulk INSERT is the more interesting one, because it's 8% slower even with
the rings kept -- so it's not about ring removal at all, it's something in
0001+0002 on the extend/write path (the pages are extended, not evicted:
bwrites is ~0, and it's WAL-bound at +2%). 0003 adds only the last two
points. I haven't fully root-caused it; my guess is lost write coalescing
on the extend path that BULKWRITE gave for free. It's small and contained,
and since it shows up without 0003 I expect it's fixable without bringing
the strategy machinery back. I'd rather understand it than wave at it, so
I'll follow up on that specifically.
The harness is just three psql scripts and the pg_stat_io / pg_stat_wal
deltas above; happy to post them if anyone wants to reproduce.
One loose end: 0003 drops the vacuum_buffer_usage_limit GUC, which only ever
sized the BAS_VACUUM ring. With no ring to size it does nothing, so I
removed it.
So the questions I'd most like opinions on:
Is a 5%-slower bulk UPDATE (the cost of retiring the rings) and an 8-10%
slower bulk INSERT (an extend-path effect independent of 0003) an acceptable
price for making scan resistance an algorithm property and dropping the
rings? Or would you rather the rings stayed and 0002 rode alongside them --
which the isolation run shows is a real option, since 0001+0002 with the
rings kept is neutral-to-better on the eviction-bound workloads? My hope
was to offer both a better/simpler algorithm for clock-sweep as well as
remove a lot of code.
Separately: is admitting demand-loaded pages COOL (probationary, promoted on
the second touch) an acceptable basis for scan resistance in core at all?
Where is my in-cache testing flattering the sweep, and what would you want
measured instead? And is reinterpreting the usage_count field as
{HOT/COOL, ref} bits, plus collapsing pg_stat_io's contexts, something you'd
accept, or is there a representation you'd want to see first?
This idea started with the question of, "I wonder if we really need a 5
count on each buffer in the clock-sweep eviction path or if that is simply
overhead and carries no signal at all for eviction or information useful to
the bgwriter?" FWIW, I feel that the COOL/HOT clock is simplier and that
tests show it is better than the 0..5 clock. I'm not thrilled by *any*
regression, especially not one that induces more WAL traffic, so I'm open to
thoughts on this.
That's all for now.
-greg
Attachments:
[text/x-patch] v6-0001-Batch-the-clock-sweep-to-reduce-nextVictimBuffer-.patch (9.8K, ../96dc1db7-58a2-41ba-ae2e-bea86fa2fb3a@app.fastmail.com/2-v6-0001-Batch-the-clock-sweep-to-reduce-nextVictimBuffer-.patch)
download | inline diff:
From 0d8492a6b59fd0acfc2ebc75ebfbd8e8842ee3ac Mon Sep 17 00:00:00 2001
From: Greg Burd <greg@burd.me>
Date: Mon, 6 Jul 2026 17:25:00 -0400
Subject: [PATCH v6 1/3] Batch the clock sweep to reduce nextVictimBuffer
atomic contention
StrategyGetBuffer() advances the shared clock hand, nextVictimBuffer, with a
pg_atomic_fetch_add_u32(..., 1) on every tick. On a multi-socket system the
cache line holding that counter has to travel over the interconnect on each
operation, pushing a sweep tick from ~20ns (same-socket, line warm in L1/L2)
into the ~100-200ns range. Under eviction pressure with hundreds of backends
in StrategyGetBuffer() concurrently, that single cache line becomes the
dominant cost of the sweep, visible as elevated bus-cycles and cache-misses in
a perf profile.
Have each backend claim a run of consecutive buffer IDs from the shared hand
with a single fetch-add and then iterate through them privately. The sweep
still advances through the pool in order, each buffer is still visited exactly
once per complete pass, and the meaning of the clock state is unchanged; only
the temporal ordering of visits within a pass changes, which the algorithm
does not depend on. The contended atomic now fires roughly once per batch
rather than once per buffer.
The batch is one cache line's worth of clock-hand values --
PG_CACHE_LINE_SIZE / sizeof(uint32) -- capped at NBuffers so a claim can never
wrap the pool more than once. Batching only helps when the counter's cache
line actually bounces between sockets, so it is enabled only on multi-node
NUMA hardware (pg_numa_get_max_node() >= 1); on a single socket, or where
libnuma is unavailable, the batch size stays 1 and the code path is
byte-identical to the stock clock sweep.
Wraparound handling is adjusted: with batching, several backends can each see
a fetch-add return a value past NBuffers within the same pass. Any such
backend takes buffer_strategy_lock, re-reads the counter, and if it is still
out of range wraps it with a single CAS and increments completePasses.
StrategySyncStart() continues to see a consistent (nextVictimBuffer,
completePasses) pair.
This is the batched-clock-sweep idea from Jim Mlodgenski's pgsql-hackers
thread, adapted to derive the batch size from the platform cache-line size.
Co-authored-by: Jim Mlodgenski <mlodj@amazon.com>
---
src/backend/storage/buffer/freelist.c | 152 ++++++++++++++++++--------
1 file changed, 109 insertions(+), 43 deletions(-)
diff --git a/src/backend/storage/buffer/freelist.c b/src/backend/storage/buffer/freelist.cindex fdb5bad7910..46104b3341d 100644--- a/src/backend/storage/buffer/freelist.c+++ b/src/backend/storage/buffer/freelist.c@@ -22,6 +22,7 @@
#include "storage/proc.h"
#include "storage/shmem.h"
#include "storage/subsystems.h"
+#include "port/pg_numa.h"
#define INT_ACCESS_ONCE(var) ((int)(*((volatile int *)&(var))))
@@ -100,68 +101,110 @@ static BufferDesc *GetBufferFromRing(BufferAccessStrategy strategy,
static void AddBufferToRing(BufferAccessStrategy strategy,
BufferDesc *buf);
+/*+ * Per-backend state for the batched clock sweep. Each backend claims a run+ * of consecutive clock-hand values with a single atomic fetch-add and then+ * iterates through them privately, so the contended nextVictimBuffer cache+ * line is touched roughly 1/batch as often. MyBatchPos is the next hand+ * value to hand out; MyBatchEnd is one past the end of the claimed run. Both+ * are absolute (monotonically increasing) hand values; the buffer id is the+ * value modulo NBuffers.+ */+static uint32 MyBatchPos = 0;+static uint32 MyBatchEnd = 0;++/*+ * Number of clock-hand values a backend claims per atomic fetch-add,+ * computed once at startup (see StrategyCtlShmemInit). When batching is+ * enabled it is one cache line's worth of hand advance, so concurrent+ * backends sweep non-overlapping, cache-line-sized runs of the pool; the+ * global sweep order is preserved (each buffer is still visited exactly once+ * per pass). Batching is enabled only on multi-node NUMA hardware; otherwise+ * this stays 1 and the sweep is byte-identical to the stock clock.+ */+static uint32 ClockSweepBatchSize = 1;+
/*
* ClockSweepTick - Helper routine for StrategyGetBuffer()
*
- * Move the clock hand one buffer ahead of its current position and return the- * id of the buffer now under the hand.+ * Return the next buffer to consider for eviction. Backends claim batches of+ * consecutive buffer IDs from the shared clock hand, then iterate through+ * them locally without further atomic operations. This preserves the global+ * sweep order while reducing contention on the shared counter.
*/
static inline uint32
ClockSweepTick(void)
{
uint32 victim;
- /*- * Atomically move hand ahead one buffer - if there's several processes- * doing this, this can lead to buffers being returned slightly out of- * apparent order.- */- victim =- pg_atomic_fetch_add_u32(&StrategyControl->nextVictimBuffer, 1);-- if (victim >= NBuffers)+ if (MyBatchPos >= MyBatchEnd)
{
- uint32 originalVictim = victim;-- /* always wrap what we look up in BufferDescriptors */- victim = victim % NBuffers;-
/*
- * If we're the one that just caused a wraparound, force- * completePasses to be incremented while holding the spinlock. We- * need the spinlock so StrategySyncStart() can return a consistent- * value consisting of nextVictimBuffer and completePasses.+ * Claim a fresh batch from the shared clock hand. This is the only+ * atomic operation per batch, reducing contention by the batch size.
*/
- if (victim == 0)- {- uint32 expected;- uint32 wrapped;- bool success = false;+ uint32 start;+ uint32 batch_size = ClockSweepBatchSize;- expected = originalVictim + 1;+ start = pg_atomic_fetch_add_u32(&StrategyControl->nextVictimBuffer,+ batch_size);- while (!success)- {- /*- * Acquire the spinlock while increasing completePasses. That- * allows other readers to read nextVictimBuffer and- * completePasses in a consistent manner which is required for- * StrategySyncStart(). In theory delaying the increment- * could lead to an overflow of nextVictimBuffers, but that's- * highly unlikely and wouldn't be particularly harmful.- */- SpinLockAcquire(&StrategyControl->buffer_strategy_lock);+ if (start >= (uint32) NBuffers)+ {+ start = start % NBuffers;- wrapped = expected % NBuffers;+ /*+ * The counter has grown past NBuffers; try to wrap it back. We+ * must hold the spinlock so StrategySyncStart() can read+ * nextVictimBuffer and completePasses consistently.+ *+ * With batching, multiple backends may each land a fetch-add+ * that returns a value past NBuffers in the same pass. After+ * acquiring the spinlock we re-read the counter: if another+ * backend already wrapped it below NBuffers we are done.+ */+ SpinLockAcquire(&StrategyControl->buffer_strategy_lock);+ {+ uint32 current;+ uint32 wrapped;- success = pg_atomic_compare_exchange_u32(&StrategyControl->nextVictimBuffer,- &expected, wrapped);- if (success)- StrategyControl->completePasses++;- SpinLockRelease(&StrategyControl->buffer_strategy_lock);+ current = pg_atomic_read_u32(&StrategyControl->nextVictimBuffer);+ if (current >= (uint32) NBuffers)+ {+ wrapped = current % NBuffers;+ if (pg_atomic_compare_exchange_u32(&StrategyControl->nextVictimBuffer,+ ¤t, wrapped))+ StrategyControl->completePasses++;+ }
}
+ SpinLockRelease(&StrategyControl->buffer_strategy_lock);+ }+ else if (start + batch_size > (uint32) NBuffers)+ {+ /*+ * The fetch-add returned a value below NBuffers, but this batch+ * spans the wrap point (start .. NBuffers-1 then 0 .. k are+ * iterated locally via "% NBuffers"). That crossing is a+ * complete pass too, but the wrap-and-increment path above only+ * fires when the fetch-add return is itself >= NBuffers, so+ * without this it would go uncounted and completePasses would+ * drift low (it feeds bgwriter pacing / StrategySyncStart, not+ * correctness). The batch is capped at NBuffers, so a batch+ * spans the wrap at most once; count it under the spinlock for a+ * consistent (nextVictimBuffer, completePasses) pair.+ */+ SpinLockAcquire(&StrategyControl->buffer_strategy_lock);+ StrategyControl->completePasses++;+ SpinLockRelease(&StrategyControl->buffer_strategy_lock);
}
++ MyBatchPos = start;+ MyBatchEnd = start + batch_size;
}
++ victim = MyBatchPos % NBuffers;+ MyBatchPos++;+
return victim;
}
@@ -408,6 +451,29 @@ StrategyCtlShmemInit(void *arg)
/* No pending notification */
StrategyControl->bgwprocno = -1;
++ /*+ * Decide whether to batch the clock sweep.+ *+ * Batching claims a run of consecutive buffer IDs per atomic fetch-add so+ * concurrent backends touch the shared nextVictimBuffer cache line ~1/batch+ * as often -- a win only when that line actually bounces across sockets,+ * i.e. on multi-node NUMA hardware. On a single socket the atomic is+ * already node-local and batching would only make backends skip ahead for+ * no benefit, so we fall back to batch size 1 there (byte-identical to the+ * stock one-buffer-at-a-time clock sweep).+ *+ * Batch (> 1) only when libnuma reports more than one node+ * (pg_numa_get_max_node() >= 1); pg_numa_init() returns -1 when NUMA is+ * unavailable. (Benchmarking showed the win holds with or without huge+ * pages, so huge-page availability is intentionally not part of the gate.)+ */+ if (pg_numa_init() != -1 &&+ pg_numa_get_max_node() >= 1)+ ClockSweepBatchSize = Min(PG_CACHE_LINE_SIZE / (uint32) sizeof(uint32),+ (uint32) NBuffers);+ else+ ClockSweepBatchSize = 1;
}
--
2.51.2
[text/x-patch] v6-0002-Replace-the-usage_count-clock-sweep-with-a-coolin.patch (30.4K, ../96dc1db7-58a2-41ba-ae2e-bea86fa2fb3a@app.fastmail.com/3-v6-0002-Replace-the-usage_count-clock-sweep-with-a-coolin.patch)
download | inline diff:
From 826edb64e97e3ba2d1e2d1aedd5e76dec98fff2d Mon Sep 17 00:00:00 2001
From: Greg Burd <greg@burd.me>
Date: Mon, 6 Jul 2026 19:22:45 -0400
Subject: [PATCH v6 2/3] Replace the usage_count clock sweep with a
cooling-stage evictor
Replace the 0..5 usage_count buffer-replacement policy with a cooling-stage
clock (the LeanStore / 2Q-A1 model): a buffer is simply HOT (recently used)
or COOL (an eviction candidate), with "pinned" being the existing refcount.
There is no per-buffer access counter.
- A demand-loaded page is admitted COOL (probationary), not HOT. A second
access via PinBuffer promotes it COOL -> HOT (the rescue). So a page
touched once -- a sequential scan -- fills and drains the COOL stage and
is evicted from it without ever displacing the HOT working set. Scan
resistance is intrinsic to the replacement algorithm, which is what lets a
later commit remove the BufferAccessStrategy ring buffers entirely.
- The foreground sweep in StrategyGetBuffer() reclaims an already-COOL,
unpinned buffer, pinning it with a CAS so a racing PinBuffer always wins.
Promotion (COOL -> HOT) and demotion (HOT -> COOL) are single-bit
transitions; only the eviction claim is a CAS.
- The background writer maintains the supply of COOL victims. As its LRU
scan runs ahead of the clock hand it demotes HOT buffers to COOL, so the
foreground finds a victim in a single pass rather than having to cool
buffers itself. The demotion is demand-driven -- bounded by the predicted
allocation for the next cycle -- so it stages just enough COOL buffers
without cooling the whole pool, and it is done under the buffer header
lock the scan already holds. A single second-chance reference bit
(set by PinBuffer, cleared on the bgwriter's first pass over a HOT buffer)
spares a recently-accessed buffer one cooling pass, keeping the genuinely
hot set out of the COOL stage under scan pressure. BgBufferSync also
tracks the reusable-buffer density it directly observes on a shorter
smoothing window, so a burst of probationary/scan COOL pages is followed
promptly rather than averaged away. Under a bulk-dirtying workload the
per-cycle clean-write cap is raised from bgwriter_lru_maxpages to predicted
demand so the bgwriter keeps supplying clean victims, rather than the
foreground sweep having to flush dirty victims inline; normal workloads,
where demand is below the cap, are unaffected.
The 4-bit usage_count field is reinterpreted in place: bit 0 is the HOT/COOL
state (BUF_COOLSTATE_ONE), bit 1 the reference bit (BUF_REFBIT). The 64-bit
buffer-state layout -- refcount, flag and lock offsets and their StaticAsserts
-- is unchanged; only the meaning of the field and the instructions that touch
it change. A StaticAssert requires the field to be at least 2 bits wide so a
future width change cannot push the reference bit into the flag bits.
BM_MAX_USAGE_COUNT becomes BUF_COOLSTATE_HOT (1), so the pin fast path
saturates at HOT. Local (temp-table) buffers get the same two-state
treatment; being single-backend they need no background cooler.
Because the reference bit shares the field, the full 4-bit value can be 0..3;
readers that mean "the cooling state" must use BUF_STATE_GET_COOLSTATE(), which
masks to bit 0 and returns only 0 (COOL) or 1 (HOT), never the raw field.
contrib/pg_buffercache is updated accordingly: its usagecount column and the
pg_buffercache_summary average report the cooling state (0 = COOL, 1 = HOT),
and pg_buffercache_usage_counts() buckets on it. Using the raw 4-bit getter
there would index its BM_MAX_USAGE_COUNT+1 = 2-element arrays with values up
to 3 and overrun the stack; masking to the cooling bit keeps the index in
range. The reference bit is deliberately not exposed as usagecount.
Depends on the batched clock sweep from the previous commit.
---
contrib/pg_buffercache/pg_buffercache_pages.c | 6 +-
src/backend/storage/buffer/bufmgr.c | 124 +++++++++++----
src/backend/storage/buffer/freelist.c | 141 ++++++++++++------
src/backend/storage/buffer/localbuf.c | 13 +-
src/include/storage/buf_internals.h | 63 +++++++-
5 files changed, 260 insertions(+), 87 deletions(-)
diff --git a/contrib/pg_buffercache/pg_buffercache_pages.c b/contrib/pg_buffercache/pg_buffercache_pages.cindex 510455998aa..b94e6f6fda8 100644--- a/contrib/pg_buffercache/pg_buffercache_pages.c+++ b/contrib/pg_buffercache/pg_buffercache_pages.c@@ -161,7 +161,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
reldatabase = bufHdr->tag.dbOid;
forknum = BufTagGetForkNum(&bufHdr->tag);
blocknum = bufHdr->tag.blockNum;
- usagecount = BUF_STATE_GET_USAGECOUNT(buf_state);+ usagecount = BUF_STATE_GET_COOLSTATE(buf_state);
pinning_backends = BUF_STATE_GET_REFCOUNT(buf_state);
if (buf_state & BM_DIRTY)
@@ -605,7 +605,7 @@ pg_buffercache_summary(PG_FUNCTION_ARGS)
if (buf_state & BM_VALID)
{
buffers_used++;
- usagecount_total += BUF_STATE_GET_USAGECOUNT(buf_state);+ usagecount_total += BUF_STATE_GET_COOLSTATE(buf_state);
if (buf_state & BM_DIRTY)
buffers_dirty++;
@@ -655,7 +655,7 @@ pg_buffercache_usage_counts(PG_FUNCTION_ARGS)
CHECK_FOR_INTERRUPTS();
- usage_count = BUF_STATE_GET_USAGECOUNT(buf_state);+ usage_count = BUF_STATE_GET_COOLSTATE(buf_state);
usage_counts[usage_count]++;
if (buf_state & BM_DIRTY)
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.cindex 3908529872a..1f5a00849e6 100644--- a/src/backend/storage/buffer/bufmgr.c+++ b/src/backend/storage/buffer/bufmgr.c@@ -83,6 +83,7 @@
/* Bits in SyncOneBuffer's return value */
#define BUF_WRITTEN 0x01
#define BUF_REUSABLE 0x02
+#define BUF_COOLED 0x04
#define RELS_BSEARCH_THRESHOLD 20
@@ -634,7 +635,7 @@ static void PinBuffer_Locked(BufferDesc *buf);
static void UnpinBuffer(BufferDesc *buf);
static void UnpinBufferNoOwner(BufferDesc *buf);
static void BufferSync(int flags);
-static int SyncOneBuffer(int buf_id, bool skip_recently_used,+static int SyncOneBuffer(int buf_id, bool skip_recently_used, bool cool_if_hot,
WritebackContext *wb_context);
static void WaitIO(BufferDesc *buf);
static void AbortBufferIO(Buffer buffer);
@@ -2333,7 +2334,10 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
* checkpoints, except for their "init" forks, which need to be treated
* just like permanent relations.
*/
- set_bits |= BM_TAG_VALID | BUF_USAGECOUNT_ONE;+ set_bits |= BM_TAG_VALID;+ /* Admit the newly loaded page COOL (probation); a second access via+ * PinBuffer promotes it to HOT. This is what makes a one-touch scan+ * self-evicting -- see the cooling-state notes in buf_internals.h. */
if (relpersistence == RELPERSISTENCE_PERMANENT || forkNum == INIT_FORKNUM)
set_bits |= BM_PERMANENT;
@@ -3002,7 +3006,9 @@ ExtendBufferedRelShared(BufferManagerRelation bmr,
victim_buf_hdr->tag = tag;
- set_bits |= BM_TAG_VALID | BUF_USAGECOUNT_ONE;+ set_bits |= BM_TAG_VALID;+ /* Admit COOL (probation); see the comment at the other admission+ * site and the cooling-state notes in buf_internals.h. */
if (bmr.relpersistence == RELPERSISTENCE_PERMANENT || fork == INIT_FORKNUM)
set_bits |= BM_PERMANENT;
@@ -3332,21 +3338,17 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy,
/* increase refcount */
buf_state += BUF_REFCOUNT_ONE;
- if (strategy == NULL)- {- /* Default case: increase usagecount unless already max. */- if (BUF_STATE_GET_USAGECOUNT(buf_state) < BM_MAX_USAGE_COUNT)- buf_state += BUF_USAGECOUNT_ONE;- }- else- {- /*- * Ring buffers shouldn't evict others from pool. Thus we- * don't make usagecount more than 1.- */- if (BUF_STATE_GET_USAGECOUNT(buf_state) == 0)- buf_state += BUF_USAGECOUNT_ONE;- }+ /*+ * Accessing a resident buffer promotes it to HOT (the 2Q rescue):+ * a page loaded COOL on probation becomes part of the hot working+ * set on its second touch. BM_MAX_USAGE_COUNT is+ * BUF_COOLSTATE_HOT (1), so this saturates at HOT and never+ * overflows the field. We also set the second-chance ref bit so+ * the bgwriter's next cooling pass spares this recently-used buffer.+ */+ if (BUF_STATE_GET_COOLSTATE(buf_state) < BUF_COOLSTATE_HOT)+ buf_state += BUF_COOLSTATE_ONE;+ buf_state |= BUF_REFBIT;
if (pg_atomic_compare_exchange_u64(&buf->state, &old_buf_state,
buf_state))
@@ -3785,7 +3787,7 @@ BufferSync(int flags)
*/
if (pg_atomic_read_u64(&bufHdr->state) & BM_CHECKPOINT_NEEDED)
{
- if (SyncOneBuffer(buf_id, false, &wb_context) & BUF_WRITTEN)+ if (SyncOneBuffer(buf_id, false, false, &wb_context) & BUF_WRITTEN)
{
TRACE_POSTGRESQL_BUFFER_SYNC_WRITTEN(buf_id);
PendingCheckpointerStats.buffers_written++;
@@ -3876,6 +3878,19 @@ BgBufferSync(WritebackContext *wb_context)
float smoothing_samples = 16;
float scan_whole_pool_milliseconds = 120000.0;
+ /*+ * The cleaner scan directly observes the reusable (COOL, unpinned) buffer+ * density over the region it walks, which -- with the cooling-stage+ * evictor -- is exactly the sweep's victim predicate. That observation is+ * ground truth for the buffers about to be reused, whereas the strategy+ * scan's positional proxy (strategy_delta/recent_alloc) blurs a pool whose+ * COOL population is spatially clustered (a scan burst leaves whole regions+ * COOL, hot OLTP regions not). So we let the cleaner's own sample adapt on+ * a shorter window than the strategy proxy, tracking a burst of+ * probationary/scan COOL pages within a cycle or two instead of lagging it.+ */+ float cleaner_smoothing_samples = 4;+
/* Used to compute how far we scan ahead */
long strategy_delta;
int bufs_to_lap;
@@ -3889,6 +3904,7 @@ BgBufferSync(WritebackContext *wb_context)
int num_to_scan;
int num_written;
int reusable_buffers;
+ int write_limit;
/* Variables for final smoothed_density update */
long new_strategy_delta;
@@ -4063,8 +4079,22 @@ BgBufferSync(WritebackContext *wb_context)
* Now write out dirty reusable buffers, working forward from the
* next_to_clean point, until we have lapped the strategy scan, or cleaned
* enough buffers to match our estimate of the next cycle's allocation
- * requirements, or hit the bgwriter_lru_maxpages limit.- */+ * requirements, or hit the write limit.+ *+ * The per-cycle write cap is normally bgwriter_lru_maxpages. But under a+ * bulk-dirtying workload (COPY, bulk UPDATE, VACUUM) the pool fills with+ * dirty COOL buffers faster than that fixed cap can clean, so the+ * foreground clock sweep is forced to flush dirty victims inline -- the+ * very cost the cooling-stage evictor is meant to keep off the critical+ * path. When predicted demand (upcoming_alloc_est) exceeds the fixed cap,+ * raise the limit to meet demand so the bgwriter stays ahead and supplies+ * clean victims. This stays bounded (by demand and by lapping the+ * strategy point), so it cannot run away, and normal workloads are+ * unaffected because there upcoming_alloc_est <= bgwriter_lru_maxpages.+ */+ write_limit = bgwriter_lru_maxpages;+ if (upcoming_alloc_est > write_limit)+ write_limit = upcoming_alloc_est;
num_to_scan = bufs_to_lap;
num_written = 0;
@@ -4073,7 +4103,7 @@ BgBufferSync(WritebackContext *wb_context)
/* Execute the LRU scan */
while (num_to_scan > 0 && reusable_buffers < upcoming_alloc_est)
{
- int sync_state = SyncOneBuffer(next_to_clean, true,+ int sync_state = SyncOneBuffer(next_to_clean, true, true,
wb_context);
if (++next_to_clean >= NBuffers)
@@ -4086,7 +4116,7 @@ BgBufferSync(WritebackContext *wb_context)
if (sync_state & BUF_WRITTEN)
{
reusable_buffers++;
- if (++num_written >= bgwriter_lru_maxpages)+ if (++num_written >= write_limit)
{
PendingBgWriterStats.maxwritten_clean++;
break;
@@ -4121,7 +4151,7 @@ BgBufferSync(WritebackContext *wb_context)
{
scans_per_alloc = (float) new_strategy_delta / (float) new_recent_alloc;
smoothed_density += (scans_per_alloc - smoothed_density) /
- smoothing_samples;+ cleaner_smoothing_samples;
#ifdef BGW_DEBUG
elog(DEBUG2, "bgwriter: cleaner density alloc=%u scan=%ld density=%.2f new smoothed=%.2f",
@@ -4140,16 +4170,26 @@ BgBufferSync(WritebackContext *wb_context)
* If skip_recently_used is true, we don't write currently-pinned buffers, nor
* buffers marked recently used, as these are not replacement candidates.
*
+ * If cool_if_hot is true (the bgwriter's LRU scan), an unpinned HOT buffer is+ * demoted HOT -> COOL as we pass it, pre-staging eviction candidates so the+ * foreground clock sweep finds a COOL victim in a single pass instead of+ * having to cool buffers itself (force_cool). The demotion is done under the+ * buffer header lock we already hold, so it needs no CAS and cannot race a+ * concurrent demotion. A concurrent PinBuffer promotes it back to HOT, which+ * is the intended 2Q behavior (a re-accessed buffer is rescued).+ *
* Returns a bitmask containing the following flag bits:
* BUF_WRITTEN: we wrote the buffer.
* BUF_REUSABLE: buffer is available for replacement, ie, it has
- * pin count 0 and usage count 0.+ * pin count 0 and is COOL (an eviction candidate).+ * BUF_COOLED: we demoted this buffer HOT -> COOL this call.
*
* (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean
* after locking it, but we don't care all that much.)
*/
static int
-SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context)+SyncOneBuffer(int buf_id, bool skip_recently_used, bool cool_if_hot,+ WritebackContext *wb_context)
{
BufferDesc *bufHdr = GetBufferDescriptor(buf_id);
int result = 0;
@@ -4171,8 +4211,38 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context)
*/
buf_state = LockBufHdr(bufHdr);
+ /*+ * Pre-cool with a second chance: if asked, act on an unpinned HOT buffer.+ * If its ref bit is set (accessed since our last pass), clear the ref bit+ * and leave it HOT -- a recently-used buffer earns one reprieve, keeping+ * the hot working set out of the COOL stage under scan pressure. Only a+ * HOT buffer whose ref bit is already clear is demoted HOT -> COOL,+ * pre-staging it as an eviction candidate for the foreground sweep. We+ * hold the header lock, so each transition is a plain masked store applied+ * atomically by UnlockBufHdrExt; we re-lock to continue the dirty-write+ * inspection below.+ */+ if (cool_if_hot &&+ BUF_STATE_GET_REFCOUNT(buf_state) == 0 &&+ BUF_STATE_GET_COOLSTATE(buf_state) != BUF_COOLSTATE_COOL)+ {+ if (BUF_STATE_GET_REFBIT(buf_state))+ {+ /* second chance: consume the ref bit, stay HOT */+ UnlockBufHdrExt(bufHdr, buf_state, 0, BUF_REFBIT, 0);+ buf_state = LockBufHdr(bufHdr);+ }+ else+ {+ /* not re-accessed since last pass: demote to COOL */+ UnlockBufHdrExt(bufHdr, buf_state, 0, BUF_USAGECOUNT_MASK, 0);+ buf_state = LockBufHdr(bufHdr);+ result |= BUF_COOLED;+ }+ }+
if (BUF_STATE_GET_REFCOUNT(buf_state) == 0 &&
- BUF_STATE_GET_USAGECOUNT(buf_state) == 0)+ BUF_STATE_GET_COOLSTATE(buf_state) == BUF_COOLSTATE_COOL)
{
result |= BUF_REUSABLE;
}
diff --git a/src/backend/storage/buffer/freelist.c b/src/backend/storage/buffer/freelist.cindex 46104b3341d..e8cf13f3362 100644--- a/src/backend/storage/buffer/freelist.c+++ b/src/backend/storage/buffer/freelist.c@@ -49,6 +49,16 @@ typedef struct
uint32 completePasses; /* Complete cycles of the clock-sweep */
pg_atomic_uint32 numBufferAllocs; /* Buffers allocated since last reset */
+ /*+ * Number of clock-hand values a backend claims per atomic fetch-add.+ * Computed once at startup (see StrategyCtlShmemInit). Kept in shared+ * memory rather than a backend-local static so that EXEC_BACKEND children+ * (which do not inherit the postmaster's statics) see the same value; a+ * backend-local copy would silently reset to 1 there, disabling batching+ * on Windows.+ */+ uint32 batchSize;+
/*
* Bgworker process to be notified upon activity or -1 if none. See
* StrategyNotifyBgWriter.
@@ -113,17 +123,6 @@ static void AddBufferToRing(BufferAccessStrategy strategy,
static uint32 MyBatchPos = 0;
static uint32 MyBatchEnd = 0;
-/*- * Number of clock-hand values a backend claims per atomic fetch-add,- * computed once at startup (see StrategyCtlShmemInit). When batching is- * enabled it is one cache line's worth of hand advance, so concurrent- * backends sweep non-overlapping, cache-line-sized runs of the pool; the- * global sweep order is preserved (each buffer is still visited exactly once- * per pass). Batching is enabled only on multi-node NUMA hardware; otherwise- * this stays 1 and the sweep is byte-identical to the stock clock.- */-static uint32 ClockSweepBatchSize = 1;-
/*
* ClockSweepTick - Helper routine for StrategyGetBuffer()
*
@@ -144,7 +143,7 @@ ClockSweepTick(void)
* atomic operation per batch, reducing contention by the batch size.
*/
uint32 start;
- uint32 batch_size = ClockSweepBatchSize;+ uint32 batch_size = StrategyControl->batchSize;
start = pg_atomic_fetch_add_u32(&StrategyControl->nextVictimBuffer,
batch_size);
@@ -229,6 +228,7 @@ StrategyGetBuffer(BufferAccessStrategy strategy, uint64 *buf_state, bool *from_r
BufferDesc *buf;
int bgwprocno;
int trycounter;
+ bool force_cool;
*from_ring = false;
@@ -279,12 +279,32 @@ StrategyGetBuffer(BufferAccessStrategy strategy, uint64 *buf_state, bool *from_r
*/
pg_atomic_fetch_add_u32(&StrategyControl->numBufferAllocs, 1);
- /* Use the "clock sweep" algorithm to find a free buffer */+ /*+ * Use the cooling-stage clock sweep to find a victim.+ *+ * A buffer is HOT (recently used) or COOL (an eviction candidate). We+ * prefer to reclaim an already-COOL buffer and demote a HOT buffer to+ * COOL only once a full sweep has found no COOL victim (force_cool) -- so+ * an abundant supply of COOL/probationary pages (e.g. a scan) is drained+ * before the hot working set is cooled. Newly loaded pages are admitted+ * COOL (see BufferAlloc), so scan resistance falls out of the algorithm.+ *+ * trycounter bounds the search. Any tick that does not produce a victim+ * and does not make progress -- a pinned buffer, or a HOT buffer skipped+ * on a prefer-COOL pass -- decrements it. Cooling a HOT buffer (under+ * force_cool) is progress and resets it. When a full pass (NBuffers)+ * makes no progress we escalate to force_cool so the next pass cools HOT+ * buffers into victims; if a force_cool pass ALSO makes no progress every+ * buffer is pinned and we fail, matching the stock "no unpinned buffers+ * available" contract.+ */
trycounter = NBuffers;
+ force_cool = false;
for (;;)
{
uint64 old_buf_state;
uint64 local_buf_state;
+ bool no_progress = false;
buf = GetBufferDescriptor(ClockSweepTick());
@@ -297,25 +317,10 @@ StrategyGetBuffer(BufferAccessStrategy strategy, uint64 *buf_state, bool *from_r
{
local_buf_state = old_buf_state;
- /*- * If the buffer is pinned or has a nonzero usage_count, we cannot- * use it; decrement the usage_count (unless pinned) and keep- * scanning.- */-+ /* If the buffer is pinned we cannot use it; keep scanning. */
if (BUF_STATE_GET_REFCOUNT(local_buf_state) != 0)
{
- if (--trycounter == 0)- {- /*- * We've scanned all the buffers without making any state- * changes, so all the buffers are pinned (or were when we- * looked at them). We could hope that someone will free- * one eventually, but it's probably better to fail than- * to risk getting stuck in an infinite loop.- */- elog(ERROR, "no unpinned buffers available");- }+ no_progress = true;
break;
}
@@ -326,20 +331,56 @@ StrategyGetBuffer(BufferAccessStrategy strategy, uint64 *buf_state, bool *from_r
continue;
}
- if (BUF_STATE_GET_USAGECOUNT(local_buf_state) != 0)+ if (BUF_STATE_GET_COOLSTATE(local_buf_state) != BUF_COOLSTATE_COOL)
{
- local_buf_state -= BUF_USAGECOUNT_ONE;+ /*+ * HOT buffer. Prefer a COOL victim: on a normal pass just+ * advance the hand (no progress). Under force_cool apply the+ * same second-chance rule the bgwriter's pre-cooling uses: a+ * HOT buffer whose ref bit is set (recently accessed) has the+ * ref bit cleared and is left HOT this pass; only a HOT buffer+ * whose ref bit is already clear is demoted HOT -> COOL. This+ * keeps a just-touched buffer from being cooled the instant the+ * bgwriter falls behind and the foreground has to cool for+ * itself. Either transition is progress toward a victim.+ */+ if (!force_cool)+ {+ no_progress = true;+ break; /* advance the hand, look for COOL */+ }++ if (BUF_STATE_GET_REFBIT(local_buf_state))+ local_buf_state &= ~BUF_REFBIT; /* second chance: clear ref, stay HOT */+ else+ local_buf_state &= ~BUF_USAGECOUNT_MASK; /* HOT -> COOL, clear ref bit */
if (pg_atomic_compare_exchange_u64(&buf->state, &old_buf_state,
local_buf_state))
{
+ /*+ * Making a cooling-state transition is progress toward a+ * victim, so reset the counter. Stay in force_cool: we made+ * a transition but have not yet produced a reclaimable COOL+ * victim (a ref-clear leaves the buffer HOT; a demote leaves+ * it COOL for a later tick to claim), and dropping out here+ * would waste a full no-progress pass re-escalating. An+ * all-HOT, all-recently-referenced pool thus takes up to ~3+ * full passes to yield a victim (discover no COOL, clear ref+ * bits, cool + reclaim) -- still within the stock clock's+ * worst case (up to BM_MAX_USAGE_COUNT+1 passes), and in+ * practice the bgwriter pre-cooling keeps a COOL victim+ * available so force_cool rarely fires at all. force_cool+ * ends naturally once a COOL victim is found and returned+ * below.+ */
trycounter = NBuffers;
break;
}
}
else
{
- /* pin the buffer if the CAS succeeds */+ /* COOL and unpinned: claim it. Pin if the CAS succeeds. */
local_buf_state += BUF_REFCOUNT_ONE;
if (pg_atomic_compare_exchange_u64(&buf->state, &old_buf_state,
@@ -356,6 +397,21 @@ StrategyGetBuffer(BufferAccessStrategy strategy, uint64 *buf_state, bool *from_r
}
}
}
++ /*+ * A tick that made no progress toward a victim counts down trycounter.+ * A full unproductive pass escalates to force_cool (cool HOT buffers+ * into victims); a second unproductive full pass means everything is+ * pinned, so fail rather than spin forever. (A failed CAS above is+ * neither progress nor a full miss: we simply retry the same buffer.)+ */+ if (no_progress && --trycounter == 0)+ {+ if (force_cool)+ elog(ERROR, "no unpinned buffers available");+ force_cool = true;+ trycounter = NBuffers;+ }
}
}
@@ -470,10 +526,10 @@ StrategyCtlShmemInit(void *arg)
*/
if (pg_numa_init() != -1 &&
pg_numa_get_max_node() >= 1)
- ClockSweepBatchSize = Min(PG_CACHE_LINE_SIZE / (uint32) sizeof(uint32),- (uint32) NBuffers);+ StrategyControl->batchSize = Min(PG_CACHE_LINE_SIZE / (uint32) sizeof(uint32),+ (uint32) NBuffers);
else
- ClockSweepBatchSize = 1;+ StrategyControl->batchSize = 1;
}
@@ -721,14 +777,13 @@ GetBufferFromRing(BufferAccessStrategy strategy, uint64 *buf_state)
/*
* If the buffer is pinned we cannot use it under any circumstances.
*
- * If usage_count is 0 or 1 then the buffer is fair game (we expect 1,- * since our own previous usage of the ring element would have left it- * there, but it might've been decremented by clock-sweep since then).- * A higher usage_count indicates someone else has touched the buffer,- * so we shouldn't re-use it.+ * With the cooling-state replacement the field holds only COOL or HOT,+ * so the stock "usage_count > 1 means another backend touched it"+ * heuristic no longer applies: a ring element is reusable whenever it+ * is unpinned. (The whole ring mechanism is removed in a later patch;+ * scan resistance is now intrinsic to the sweep.)
*/
- if (BUF_STATE_GET_REFCOUNT(local_buf_state) != 0- || BUF_STATE_GET_USAGECOUNT(local_buf_state) > 1)+ if (BUF_STATE_GET_REFCOUNT(local_buf_state) != 0)
break;
/* See equivalent code in PinBuffer() */
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.cindex 4870c8e13d0..21c08091c49 100644--- a/src/backend/storage/buffer/localbuf.c+++ b/src/backend/storage/buffer/localbuf.c@@ -167,7 +167,7 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
buf_state = pg_atomic_read_u64(&bufHdr->state);
buf_state &= ~(BUF_FLAG_MASK | BUF_USAGECOUNT_MASK);
- buf_state |= BM_TAG_VALID | BUF_USAGECOUNT_ONE;+ buf_state |= BM_TAG_VALID; /* admit COOL (probation) */
pg_atomic_unlocked_write_u64(&bufHdr->state, buf_state);
*foundPtr = false;
@@ -248,9 +248,10 @@ GetLocalVictimBuffer(void)
{
uint64 buf_state = pg_atomic_read_u64(&bufHdr->state);
- if (BUF_STATE_GET_USAGECOUNT(buf_state) > 0)+ if (BUF_STATE_GET_COOLSTATE(buf_state) != BUF_COOLSTATE_COOL)
{
- buf_state -= BUF_USAGECOUNT_ONE;+ /* HOT: give it a second chance, cool it and keep scanning. */+ buf_state -= BUF_COOLSTATE_ONE;
pg_atomic_unlocked_write_u64(&bufHdr->state, buf_state);
trycounter = NLocBuffer;
}
@@ -454,7 +455,7 @@ ExtendBufferedRelLocal(BufferManagerRelation bmr,
victim_buf_hdr->tag = tag;
- buf_state |= BM_TAG_VALID | BUF_USAGECOUNT_ONE;+ buf_state |= BM_TAG_VALID; /* admit COOL (probation) */
pg_atomic_unlocked_write_u64(&victim_buf_hdr->state, buf_state);
@@ -839,9 +840,9 @@ PinLocalBuffer(BufferDesc *buf_hdr, bool adjust_usagecount)
NLocalPinnedBuffers++;
buf_state += BUF_REFCOUNT_ONE;
if (adjust_usagecount &&
- BUF_STATE_GET_USAGECOUNT(buf_state) < BM_MAX_USAGE_COUNT)+ BUF_STATE_GET_COOLSTATE(buf_state) < BUF_COOLSTATE_HOT)
{
- buf_state += BUF_USAGECOUNT_ONE;+ buf_state += BUF_COOLSTATE_ONE;
}
pg_atomic_unlocked_write_u64(&buf_hdr->state, buf_state);
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.hindex e4ff5619b79..131df126cda 100644--- a/src/include/storage/buf_internals.h+++ b/src/include/storage/buf_internals.h@@ -67,6 +67,50 @@ StaticAssertDecl(BUF_REFCOUNT_BITS + BUF_USAGECOUNT_BITS + BUF_FLAG_BITS + BUF_L
#define BUF_USAGECOUNT_ONE \
(UINT64CONST(1) << BUF_REFCOUNT_BITS)
+/*+ * Cooling state (LeanStore / 2Q-A1 cooling-stage clock sweep).+ *+ * The field historically used for the 0..5 usage_count now holds a single+ * cooling-state bit: HOT (recently accessed, not an eviction candidate) or+ * COOL (an eviction candidate). We reuse BUF_USAGECOUNT_ONE as the unit so+ * the buffer-state bit geography -- refcount, flag, and lock offsets, and the+ * 64-bit StaticAsserts -- is unchanged; only the meaning of the field and the+ * instructions that touch it change.+ *+ * A demand-loaded page is admitted COOL (probation); a second access promotes+ * it to HOT (the rescue). The sweep prefers COOL victims and demotes HOT to+ * COOL only when a full pass finds no COOL victim. So a one-touch scan fills+ * and drains the COOL stage without displacing the HOT working set -- scan+ * resistance intrinsic to the replacement algorithm.+ */+#define BUF_COOLSTATE_COOL 0+#define BUF_COOLSTATE_HOT 1+#define BUF_COOLSTATE_ONE BUF_USAGECOUNT_ONE++/*+ * Second-chance reference bit, bit 1 of the (former usagecount) field, one+ * position above the cooling-state bit. PinBuffer sets it on every access.+ * The bgwriter's pre-cooling gives a HOT buffer a second chance: the first+ * time it passes a HOT buffer whose ref bit is set, it clears the ref bit and+ * leaves the buffer HOT; only a HOT buffer whose ref bit is already clear (not+ * re-accessed since the previous bgwriter pass) is demoted to COOL. This+ * keeps genuinely-hot pages out of the COOL stage (protecting the working set+ * from being cooled under scan pressure) while leaving the foreground sweep a+ * single-pass search over the pre-staged COOL buffers. A separate bit (not a+ * count) so it stays a plain masked store under the header lock.+ */+#define BUF_REFBIT (UINT64CONST(2) << BUF_REFCOUNT_BITS)+#define BUF_STATE_GET_REFBIT(state) (((state) & BUF_REFBIT) != 0)++/*+ * The cooling state occupies bit 0 and the reference bit occupies bit 1 of+ * the (former usagecount) field, so the field must be at least 2 bits wide.+ * Assert it here so a future change to BUF_USAGECOUNT_BITS cannot silently+ * push BUF_REFBIT up into the flag bits.+ */+StaticAssertDecl(BUF_USAGECOUNT_BITS >= 2,+ "cooling state + reference bit need at least 2 bits in the usagecount field");+
/* flags related definitions */
#define BUF_FLAG_SHIFT \
(BUF_REFCOUNT_BITS + BUF_USAGECOUNT_BITS)
@@ -92,6 +136,11 @@ StaticAssertDecl(BUF_REFCOUNT_BITS + BUF_USAGECOUNT_BITS + BUF_FLAG_BITS + BUF_L
#define BUF_STATE_GET_USAGECOUNT(state) \
((uint32)(((state) & BUF_USAGECOUNT_MASK) >> BUF_USAGECOUNT_SHIFT))
+/* Cooling state (HOT/COOL) from buffer state -- bit 0 of the field only, so+ * the second-chance ref bit (bit 1) does not perturb the HOT/COOL test. */+#define BUF_STATE_GET_COOLSTATE(state) \+ ((uint32) (((state) >> BUF_USAGECOUNT_SHIFT) & 1))+
/*
* Flags for buffer descriptors
*
@@ -134,17 +183,15 @@ StaticAssertDecl(MAX_BACKENDS_BITS <= (BUF_LOCK_BITS - 2),
/*
- * The maximum allowed value of usage_count represents a tradeoff between- * accuracy and speed of the clock-sweep buffer management algorithm. A- * large value (comparable to NBuffers) would approximate LRU semantics.- * But it can take as many as BM_MAX_USAGE_COUNT+1 complete cycles of the- * clock-sweep hand to find a free buffer, so in practice we don't want the- * value to be very large.+ * The cooling state is a single bit (HOT/COOL); the maximum value stored in+ * the field is therefore BUF_COOLSTATE_HOT. Retained under the historical+ * name BM_MAX_USAGE_COUNT so the pin fast path ("promote unless already at+ * max") reads naturally.
*/
-#define BM_MAX_USAGE_COUNT 5+#define BM_MAX_USAGE_COUNT BUF_COOLSTATE_HOT
StaticAssertDecl(BM_MAX_USAGE_COUNT < (UINT64CONST(1) << BUF_USAGECOUNT_BITS),
- "BM_MAX_USAGE_COUNT doesn't fit in BUF_USAGECOUNT_BITS bits");+ "cooling state doesn't fit in BUF_USAGECOUNT_BITS bits");
/*
* Buffer tag identifies which disk block the buffer contains.
--
2.51.2
[text/x-patch] v6-0003-Remove-BufferAccessStrategy-scan-resistance-is-no.patch (180.2K, ../96dc1db7-58a2-41ba-ae2e-bea86fa2fb3a@app.fastmail.com/4-v6-0003-Remove-BufferAccessStrategy-scan-resistance-is-no.patch)
download | inline diff:
From 1f9e7eda9438c168a51a928f7d169fae9f84e96f Mon Sep 17 00:00:00 2001
From: Greg Burd <greg@burd.me>
Date: Tue, 7 Jul 2026 08:45:39 -0400
Subject: [PATCH v6 3/3] Remove BufferAccessStrategy; scan resistance is now
intrinsic
The cooling-stage evictor admits demand-loaded pages COOL and promotes them to
HOT only on a second access, so a one-touch sequential scan fills and drains
the COOL stage without displacing the hot working set. Scan resistance is
therefore a property of the replacement algorithm itself, and the
BufferAccessStrategy ring buffers that previously provided it are dead weight.
Remove them end to end.
Deleted:
- the BufferAccessStrategy type and the BufferAccessStrategyType enum
(BAS_NORMAL/BULKREAD/BULKWRITE/VACUUM);
- the ring machinery in freelist.c (GetAccessStrategy[WithSize],
GetAccessStrategyBufferCount, GetAccessStrategyPinLimit,
FreeAccessStrategy, GetBufferFromRing, AddBufferToRing,
StrategyRejectBuffer, IOContextForStrategy);
- the strategy parameter from ReadBufferExtended, ReadBufferWithoutRelcache,
the ExtendBufferedRel* family, StrategyGetBuffer, read_stream_begin_*,
and every scan/vacuum/analyze/index-AM caller;
- the strategy fields on HeapScanDescData, IndexScanDescData,
BulkInsertStateData, ReadBuffersOperation, and ReadStream;
- _hash_getbuf_with_strategy (identical to _hash_getbuf without a strategy).
pg_stat_io's per-strategy IO contexts collapse: IOCONTEXT_BULKREAD,
IOCONTEXT_BULKWRITE and IOCONTEXT_VACUUM are removed, leaving IOCONTEXT_INIT
and IOCONTEXT_NORMAL. IOOP_REUSE only ever occurred while recycling a ring
buffer, so it is no longer tracked; GetVictimBuffer counts IOOP_EVICT only.
The vacuum_buffer_usage_limit GUC and the VACUUM/ANALYZE (BUFFER_USAGE_LIMIT
...) option are removed, along with the VacuumBufferUsageLimit global, the
ring-size plumbing through VacuumParams and parallel vacuum, and vacuumdb's
--buffer-usage-limit client option. read_stream's per-backend pin budget is
now enforced solely by GetPinLimit()/GetLocalPinLimit(), which already applied
and is unchanged for the (formerly universal) no-strategy case.
Documentation and the stats/amcheck regression tests are updated to drop the
removed contexts and options.
---
contrib/amcheck/expected/check_heap.out | 16 +-
contrib/amcheck/sql/check_heap.sql | 16 +-
contrib/amcheck/verify_gin.c | 18 +-
contrib/amcheck/verify_heapam.c | 3 -
contrib/amcheck/verify_nbtree.c | 11 +-
contrib/bloom/blscan.c | 8 -
contrib/bloom/blutils.c | 4 +-
contrib/bloom/blvacuum.c | 2 -
contrib/pageinspect/rawpage.c | 2 +-
contrib/pg_prewarm/autoprewarm.c | 1 -
contrib/pg_prewarm/pg_prewarm.c | 1 -
contrib/pg_visibility/pg_visibility.c | 7 +-
contrib/pgstattuple/pgstatapprox.c | 3 -
contrib/pgstattuple/pgstatindex.c | 9 +-
contrib/pgstattuple/pgstattuple.c | 37 +-
doc/src/sgml/config.sgml | 30 --
doc/src/sgml/monitoring.sgml | 38 +-
doc/src/sgml/ref/analyze.sgml | 21 -
doc/src/sgml/ref/vacuum.sgml | 25 -
src/backend/access/brin/brin.c | 11 +-
src/backend/access/brin/brin_revmap.c | 2 +-
src/backend/access/gin/gininsert.c | 4 +-
src/backend/access/gin/ginutil.c | 2 +-
src/backend/access/gin/ginvacuum.c | 17 +-
src/backend/access/gist/gist.c | 2 +-
src/backend/access/gist/gistutil.c | 2 +-
src/backend/access/gist/gistvacuum.c | 10 +-
src/backend/access/hash/hash.c | 13 +-
src/backend/access/hash/hashovfl.c | 55 +--
src/backend/access/hash/hashpage.c | 40 +-
src/backend/access/heap/heapam.c | 31 +-
src/backend/access/heap/heapam_handler.c | 4 +-
src/backend/access/heap/hio.c | 13 +-
src/backend/access/heap/vacuumlazy.c | 22 +-
src/backend/access/heap/visibilitymap.c | 4 +-
src/backend/access/nbtree/nbtpage.c | 2 +-
src/backend/access/nbtree/nbtree.c | 4 +-
src/backend/access/spgist/spgutils.c | 2 +-
src/backend/access/spgist/spgvacuum.c | 4 +-
src/backend/access/transam/xloginsert.c | 2 +-
src/backend/access/transam/xlogutils.c | 3 +-
src/backend/catalog/index.c | 1 -
src/backend/commands/analyze.c | 7 +-
src/backend/commands/dbcommands.c | 6 +-
src/backend/commands/repack.c | 2 +-
src/backend/commands/sequence.c | 2 +-
src/backend/commands/vacuum.c | 118 +----
src/backend/commands/vacuumparallel.c | 21 +-
src/backend/postmaster/autovacuum.c | 29 +-
src/backend/postmaster/datachecksum_state.c | 33 +-
src/backend/storage/aio/read_stream.c | 17 +-
src/backend/storage/buffer/README | 44 --
src/backend/storage/buffer/bufmgr.c | 185 +++-----
src/backend/storage/buffer/freelist.c | 434 +-----------------
src/backend/storage/freespace/freespace.c | 4 +-
src/backend/storage/smgr/md.c | 11 +-
src/backend/utils/activity/pgstat_io.c | 45 +-
src/backend/utils/init/globals.c | 1 -
src/backend/utils/misc/guc_parameters.dat | 10 -
src/backend/utils/misc/postgresql.conf.sample | 3 -
src/bin/scripts/vacuumdb.c | 13 -
src/bin/scripts/vacuuming.c | 21 -
src/bin/scripts/vacuuming.h | 1 -
src/include/access/genam.h | 1 -
src/include/access/hash.h | 9 +-
src/include/access/heapam.h | 4 +-
src/include/access/hio.h | 1 -
src/include/access/tableam.h | 8 +-
src/include/commands/vacuum.h | 8 +-
src/include/miscadmin.h | 9 -
src/include/pgstat.h | 5 +-
src/include/storage/buf.h | 7 -
src/include/storage/buf_internals.h | 6 +-
src/include/storage/bufmgr.h | 32 +-
src/include/storage/read_stream.h | 5 +-
src/include/utils/guc_hooks.h | 2 -
src/test/modules/test_aio/test_aio.c | 7 +-
src/test/regress/expected/stats.out | 110 +----
src/test/regress/expected/vacuum.out | 19 -
src/test/regress/sql/stats.sql | 56 +--
src/test/regress/sql/vacuum.sql | 15 -
src/tools/pgindent/typedefs.list | 2 -
82 files changed, 261 insertions(+), 1554 deletions(-)
diff --git a/contrib/amcheck/expected/check_heap.out b/contrib/amcheck/expected/check_heap.outindex 979e5e84e72..569b0202f1c 100644--- a/contrib/amcheck/expected/check_heap.out+++ b/contrib/amcheck/expected/check_heap.out@@ -67,11 +67,11 @@ INSERT INTO heaptest (a, b)
(SELECT gs, repeat('x', gs)
FROM generate_series(1,50) gs);
-- pg_stat_io test:
--- verify_heapam always uses a BAS_BULKREAD BufferAccessStrategy, whereas a--- sequential scan does so only if the table is large enough when compared to--- shared buffers (see initscan()). CREATE DATABASE ... also unconditionally--- uses a BAS_BULKREAD strategy, but we have chosen to use a tablespace and--- verify_heapam to provide coverage instead of adding another expensive+-- verify_heapam reads the heap through the buffer manager; with the+-- cooling-stage clock sweep there are no per-strategy IO contexts, so the+-- reads are counted in the 'normal' context. CREATE DATABASE ... likewise+-- reads through the normal context, but we have chosen to use a tablespace+-- and verify_heapam to provide coverage instead of adding another expensive
-- operation to the main regression test suite.
--
-- Create an alternative tablespace and move the heaptest table to it, causing
@@ -83,7 +83,7 @@ INSERT INTO heaptest (a, b)
SET allow_in_place_tablespaces = true;
CREATE TABLESPACE regress_test_stats_tblspc LOCATION '';
SELECT sum(reads) AS stats_bulkreads_before
- FROM pg_stat_io WHERE context = 'bulkread' \gset+ FROM pg_stat_io WHERE context = 'normal' AND object = 'relation' \gset
BEGIN;
ALTER TABLE heaptest SET TABLESPACE regress_test_stats_tblspc;
-- Check that valid options are not rejected nor corruption reported
@@ -111,7 +111,7 @@ SELECT * FROM verify_heapam(relation := 'heaptest', startblock := 0, endblock :=
COMMIT;
-- verify_heapam should have read in the page written out by
-- ALTER TABLE ... SET TABLESPACE ...
--- causing an additional bulkread, which should be reflected in pg_stat_io.+-- causing additional reads, which should be reflected in pg_stat_io.
SELECT pg_stat_force_next_flush();
pg_stat_force_next_flush
--------------------------
@@ -119,7 +119,7 @@ SELECT pg_stat_force_next_flush();
(1 row)
SELECT sum(reads) AS stats_bulkreads_after
- FROM pg_stat_io WHERE context = 'bulkread' \gset+ FROM pg_stat_io WHERE context = 'normal' AND object = 'relation' \gset
SELECT :stats_bulkreads_after > :stats_bulkreads_before;
?column?
----------
diff --git a/contrib/amcheck/sql/check_heap.sql b/contrib/amcheck/sql/check_heap.sqlindex 1745bae634e..61f710dbeda 100644--- a/contrib/amcheck/sql/check_heap.sql+++ b/contrib/amcheck/sql/check_heap.sql@@ -27,11 +27,11 @@ INSERT INTO heaptest (a, b)
FROM generate_series(1,50) gs);
-- pg_stat_io test:
--- verify_heapam always uses a BAS_BULKREAD BufferAccessStrategy, whereas a--- sequential scan does so only if the table is large enough when compared to--- shared buffers (see initscan()). CREATE DATABASE ... also unconditionally--- uses a BAS_BULKREAD strategy, but we have chosen to use a tablespace and--- verify_heapam to provide coverage instead of adding another expensive+-- verify_heapam reads the heap through the buffer manager; with the+-- cooling-stage clock sweep there are no per-strategy IO contexts, so the+-- reads are counted in the 'normal' context. CREATE DATABASE ... likewise+-- reads through the normal context, but we have chosen to use a tablespace+-- and verify_heapam to provide coverage instead of adding another expensive
-- operation to the main regression test suite.
--
-- Create an alternative tablespace and move the heaptest table to it, causing
@@ -43,7 +43,7 @@ INSERT INTO heaptest (a, b)
SET allow_in_place_tablespaces = true;
CREATE TABLESPACE regress_test_stats_tblspc LOCATION '';
SELECT sum(reads) AS stats_bulkreads_before
- FROM pg_stat_io WHERE context = 'bulkread' \gset+ FROM pg_stat_io WHERE context = 'normal' AND object = 'relation' \gset
BEGIN;
ALTER TABLE heaptest SET TABLESPACE regress_test_stats_tblspc;
-- Check that valid options are not rejected nor corruption reported
@@ -56,10 +56,10 @@ COMMIT;
-- verify_heapam should have read in the page written out by
-- ALTER TABLE ... SET TABLESPACE ...
--- causing an additional bulkread, which should be reflected in pg_stat_io.+-- causing additional reads, which should be reflected in pg_stat_io.
SELECT pg_stat_force_next_flush();
SELECT sum(reads) AS stats_bulkreads_after
- FROM pg_stat_io WHERE context = 'bulkread' \gset+ FROM pg_stat_io WHERE context = 'normal' AND object = 'relation' \gset
SELECT :stats_bulkreads_after > :stats_bulkreads_before;
CREATE ROLE regress_heaptest_role;
diff --git a/contrib/amcheck/verify_gin.c b/contrib/amcheck/verify_gin.cindex fa06689ed5b..ef2e64475c6 100644--- a/contrib/amcheck/verify_gin.c+++ b/contrib/amcheck/verify_gin.c@@ -63,8 +63,7 @@ static void gin_check_parent_keys_consistency(Relation rel,
static void check_index_page(Relation rel, Buffer buffer, BlockNumber blockNo);
static IndexTuple gin_refind_parent(Relation rel,
BlockNumber parentblkno,
- BlockNumber childblkno,- BufferAccessStrategy strategy);+ BlockNumber childblkno);
static ItemId PageGetItemIdCareful(Relation rel, BlockNumber block, Page page,
OffsetNumber offset);
@@ -133,7 +132,6 @@ ginReadTupleWithoutState(IndexTuple itup, int *nitems)
static void
gin_check_posting_tree_parent_keys_consistency(Relation rel, BlockNumber posting_tree_root)
{
- BufferAccessStrategy strategy = GetAccessStrategy(BAS_BULKREAD);
GinPostingTreeScanItem *stack;
MemoryContext mctx;
MemoryContext oldcontext;
@@ -171,8 +169,7 @@ gin_check_posting_tree_parent_keys_consistency(Relation rel, BlockNumber posting
CHECK_FOR_INTERRUPTS();
- buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno,- RBM_NORMAL, strategy);+ buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno, RBM_NORMAL);
LockBuffer(buffer, GIN_SHARE);
page = BufferGetPage(buffer);
@@ -391,7 +388,6 @@ gin_check_parent_keys_consistency(Relation rel,
void *callback_state,
bool readonly)
{
- BufferAccessStrategy strategy = GetAccessStrategy(BAS_BULKREAD);
GinScanItem *stack;
MemoryContext mctx;
MemoryContext oldcontext;
@@ -430,8 +426,7 @@ gin_check_parent_keys_consistency(Relation rel,
CHECK_FOR_INTERRUPTS();
- buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno,- RBM_NORMAL, strategy);+ buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno, RBM_NORMAL);
LockBuffer(buffer, GIN_SHARE);
page = BufferGetPage(buffer);
maxoff = PageGetMaxOffsetNumber(page);
@@ -567,7 +562,7 @@ gin_check_parent_keys_consistency(Relation rel,
*/
pfree(stack->parenttup);
stack->parenttup = gin_refind_parent(rel, stack->parentblk,
- stack->blkno, strategy);+ stack->blkno);
/* We found it - make a final check before failing */
if (!stack->parenttup)
@@ -719,7 +714,7 @@ check_index_page(Relation rel, Buffer buffer, BlockNumber blockNo)
*/
static IndexTuple
gin_refind_parent(Relation rel, BlockNumber parentblkno,
- BlockNumber childblkno, BufferAccessStrategy strategy)+ BlockNumber childblkno)
{
Buffer parentbuf;
Page parentpage;
@@ -727,8 +722,7 @@ gin_refind_parent(Relation rel, BlockNumber parentblkno,
parent_maxoff;
IndexTuple result = NULL;
- parentbuf = ReadBufferExtended(rel, MAIN_FORKNUM, parentblkno, RBM_NORMAL,- strategy);+ parentbuf = ReadBufferExtended(rel, MAIN_FORKNUM, parentblkno, RBM_NORMAL);
LockBuffer(parentbuf, GIN_SHARE);
parentpage = BufferGetPage(parentbuf);
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.cindex 20ff58aa782..e4336eb5fc2 100644--- a/contrib/amcheck/verify_heapam.c+++ b/contrib/amcheck/verify_heapam.c@@ -126,7 +126,6 @@ typedef struct HeapCheckContext
* recent block in the buffer yielded by the read stream API.
*/
BlockNumber blkno;
- BufferAccessStrategy bstrategy;
Buffer buffer;
Page page;
@@ -374,7 +373,6 @@ verify_heapam(PG_FUNCTION_ARGS)
PG_RETURN_NULL();
}
- ctx.bstrategy = GetAccessStrategy(BAS_BULKREAD);
ctx.buffer = InvalidBuffer;
ctx.page = NULL;
@@ -472,7 +470,6 @@ verify_heapam(PG_FUNCTION_ARGS)
}
stream = read_stream_begin_relation(stream_flags,
- ctx.bstrategy,
ctx.rel,
MAIN_FORKNUM,
stream_cb,
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.cindex 3ef2d66f826..18cd8928fdd 100644--- a/contrib/amcheck/verify_nbtree.c+++ b/contrib/amcheck/verify_nbtree.c@@ -88,8 +88,6 @@ typedef struct BtreeCheckState
bool checkunique;
/* Per-page context */
MemoryContext targetcontext;
- /* Buffer access strategy */- BufferAccessStrategy checkstrategy;
/*
* Info for uniqueness checking. Fill this field and the one below once
@@ -490,7 +488,6 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace,
state->targetcontext = AllocSetContextCreate(CurrentMemoryContext,
"amcheck context",
ALLOCSET_DEFAULT_SIZES);
- state->checkstrategy = GetAccessStrategy(BAS_BULKREAD);
/* Get true root block from meta-page */
metapage = palloc_btree_page(state, BTREE_METAPAGE);
@@ -1115,7 +1112,7 @@ bt_recheck_sibling_links(BtreeCheckState *state,
/* Couple locks in the usual order for nbtree: Left to right */
lbuf = ReadBufferExtended(state->rel, MAIN_FORKNUM, leftcurrent,
- RBM_NORMAL, state->checkstrategy);+ RBM_NORMAL);
LockBuffer(lbuf, BT_READ);
_bt_checkpage(state->rel, lbuf);
page = BufferGetPage(lbuf);
@@ -1138,8 +1135,7 @@ bt_recheck_sibling_links(BtreeCheckState *state,
if (newtargetblock != leftcurrent)
{
newtargetbuf = ReadBufferExtended(state->rel, MAIN_FORKNUM,
- newtargetblock, RBM_NORMAL,- state->checkstrategy);+ newtargetblock, RBM_NORMAL);
LockBuffer(newtargetbuf, BT_READ);
_bt_checkpage(state->rel, newtargetbuf);
page = BufferGetPage(newtargetbuf);
@@ -3300,8 +3296,7 @@ palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum)
* We copy the page into local storage to avoid holding pin on the buffer
* longer than we must.
*/
- buffer = ReadBufferExtended(state->rel, MAIN_FORKNUM, blocknum, RBM_NORMAL,- state->checkstrategy);+ buffer = ReadBufferExtended(state->rel, MAIN_FORKNUM, blocknum, RBM_NORMAL);
LockBuffer(buffer, BT_READ);
/*
diff --git a/contrib/bloom/blscan.c b/contrib/bloom/blscan.cindex 1a0e42021ec..ed62d5c04e6 100644--- a/contrib/bloom/blscan.c+++ b/contrib/bloom/blscan.c@@ -80,7 +80,6 @@ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
BlockNumber blkno,
npages;
int i;
- BufferAccessStrategy bas;
BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
BlockRangeReadStreamPrivate p;
ReadStream *stream;
@@ -113,11 +112,6 @@ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
}
}
- /*- * We're going to read the whole index. This is why we use appropriate- * buffer access strategy.- */- bas = GetAccessStrategy(BAS_BULKREAD);
npages = RelationGetNumberOfBlocks(scan->indexRelation);
pgstat_count_index_scan(scan->indexRelation);
if (scan->instrument)
@@ -133,7 +127,6 @@ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
*/
stream = read_stream_begin_relation(READ_STREAM_FULL |
READ_STREAM_USE_BATCHING,
- bas,
scan->indexRelation,
MAIN_FORKNUM,
block_range_read_stream_cb,
@@ -184,7 +177,6 @@ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
Assert(read_stream_next_buffer(stream, NULL) == InvalidBuffer);
read_stream_end(stream);
- FreeAccessStrategy(bas);
return ntids;
}
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.cindex 5111cdc6dd6..6c1ec15c59c 100644--- a/contrib/bloom/blutils.c+++ b/contrib/bloom/blutils.c@@ -392,7 +392,7 @@ BloomNewBuffer(Relation index)
}
/* Must extend the file */
- buffer = ExtendBufferedRel(BMR_REL(index), MAIN_FORKNUM, NULL,+ buffer = ExtendBufferedRel(BMR_REL(index), MAIN_FORKNUM,
EB_LOCK_FIRST);
return buffer;
@@ -460,7 +460,7 @@ BloomInitMetapage(Relation index, ForkNumber forknum)
* block number 0 (BLOOM_METAPAGE_BLKNO). No need to hold the extension
* lock because there cannot be concurrent inserters yet.
*/
- metaBuffer = ReadBufferExtended(index, forknum, P_NEW, RBM_NORMAL, NULL);+ metaBuffer = ReadBufferExtended(index, forknum, P_NEW, RBM_NORMAL);
LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
Assert(BufferGetBlockNumber(metaBuffer) == BLOOM_METAPAGE_BLKNO);
diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.cindex 6beb1c20ebb..08d7705e365 100644--- a/contrib/bloom/blvacuum.c+++ b/contrib/bloom/blvacuum.c@@ -66,7 +66,6 @@ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
READ_STREAM_FULL |
READ_STREAM_USE_BATCHING,
- info->strategy,
index,
MAIN_FORKNUM,
block_range_read_stream_cb,
@@ -219,7 +218,6 @@ blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
READ_STREAM_FULL |
READ_STREAM_USE_BATCHING,
- info->strategy,
index,
MAIN_FORKNUM,
block_range_read_stream_cb,
diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.cindex d136593edb2..bbe8edf9161 100644--- a/contrib/pageinspect/rawpage.c+++ b/contrib/pageinspect/rawpage.c@@ -188,7 +188,7 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
/* Take a verbatim copy of the page */
- buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL);+ buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL);
LockBuffer(buf, BUFFER_LOCK_SHARE);
memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.cindex deb4c2671b5..33ad88ea8b1 100644--- a/contrib/pg_prewarm/autoprewarm.c+++ b/contrib/pg_prewarm/autoprewarm.c@@ -630,7 +630,6 @@ autoprewarm_database_main(Datum main_arg)
stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
READ_STREAM_DEFAULT |
READ_STREAM_USE_BATCHING,
- NULL,
rel,
p.forknum,
apw_read_stream_next_block,
diff --git a/contrib/pg_prewarm/pg_prewarm.c b/contrib/pg_prewarm/pg_prewarm.cindex c2716086693..716a6754a7e 100644--- a/contrib/pg_prewarm/pg_prewarm.c+++ b/contrib/pg_prewarm/pg_prewarm.c@@ -251,7 +251,6 @@ pg_prewarm(PG_FUNCTION_ARGS)
stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
READ_STREAM_FULL |
READ_STREAM_USE_BATCHING,
- NULL,
rel,
forkNumber,
block_range_read_stream_cb,
diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.cindex dfab0b64cf5..ccb829a09eb 100644--- a/contrib/pg_visibility/pg_visibility.c+++ b/contrib/pg_visibility/pg_visibility.c@@ -488,7 +488,6 @@ collect_visibility_data(Oid relid, bool include_pd)
vbits *info;
BlockNumber blkno;
Buffer vmbuffer = InvalidBuffer;
- BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD);
BlockRangeReadStreamPrivate p;
ReadStream *stream = NULL;
@@ -514,7 +513,6 @@ collect_visibility_data(Oid relid, bool include_pd)
*/
stream = read_stream_begin_relation(READ_STREAM_FULL |
READ_STREAM_USE_BATCHING,
- bstrategy,
rel,
MAIN_FORKNUM,
block_range_read_stream_cb,
@@ -538,8 +536,7 @@ collect_visibility_data(Oid relid, bool include_pd)
/*
* Page-level data requires reading every block, so only get it if the
- * caller needs it. Use a buffer access strategy, too, to prevent- * cache-trashing.+ * caller needs it.
*/
if (include_pd)
{
@@ -700,7 +697,6 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
Relation rel;
corrupt_items *items;
Buffer vmbuffer = InvalidBuffer;
- BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD);
TransactionId OldestXmin = InvalidTransactionId;
struct collect_corrupt_items_read_stream_private p;
ReadStream *stream;
@@ -734,7 +730,6 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
p.all_frozen = all_frozen;
p.all_visible = all_visible;
stream = read_stream_begin_relation(READ_STREAM_FULL,
- bstrategy,
rel,
MAIN_FORKNUM,
collect_corrupt_items_read_stream_next_block,
diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.cindex 21e0b50fb4b..8e17d48991e 100644--- a/contrib/pgstattuple/pgstatapprox.c+++ b/contrib/pgstattuple/pgstatapprox.c@@ -116,13 +116,11 @@ static void
statapprox_heap(Relation rel, output_type *stat)
{
BlockNumber nblocks;
- BufferAccessStrategy bstrategy;
TransactionId OldestXmin;
StatApproxReadStreamPrivate p;
ReadStream *stream;
OldestXmin = GetOldestNonRemovableTransactionId(rel);
- bstrategy = GetAccessStrategy(BAS_BULKREAD);
nblocks = RelationGetNumberOfBlocks(rel);
@@ -141,7 +139,6 @@ statapprox_heap(Relation rel, output_type *stat)
* caution.
*/
stream = read_stream_begin_relation(READ_STREAM_FULL,
- bstrategy,
rel,
MAIN_FORKNUM,
statapprox_heap_read_stream_next,
diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.cindex 8951ad0aac4..a0922b386a0 100644--- a/contrib/pgstattuple/pgstatindex.c+++ b/contrib/pgstattuple/pgstatindex.c@@ -217,7 +217,6 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
BlockNumber nblocks;
BlockNumber blkno;
BTIndexStat indexStat;
- BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD);
BlockRangeReadStreamPrivate p;
ReadStream *stream;
BlockNumber startblk;
@@ -254,7 +253,7 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
* Read metapage
*/
{
- Buffer buffer = ReadBufferExtended(rel, MAIN_FORKNUM, 0, RBM_NORMAL, bstrategy);+ Buffer buffer = ReadBufferExtended(rel, MAIN_FORKNUM, 0, RBM_NORMAL);
Page page = BufferGetPage(buffer);
BTMetaPageData *metad = BTPageGetMeta(page);
@@ -291,7 +290,6 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
*/
stream = read_stream_begin_relation(READ_STREAM_FULL |
READ_STREAM_USE_BATCHING,
- bstrategy,
rel,
MAIN_FORKNUM,
block_range_read_stream_cb,
@@ -612,7 +610,6 @@ pgstathashindex(PG_FUNCTION_ARGS)
BlockNumber blkno;
Relation rel;
HashIndexStat stats;
- BufferAccessStrategy bstrategy;
HeapTuple tuple;
TupleDesc tupleDesc;
Datum values[8];
@@ -665,9 +662,6 @@ pgstathashindex(PG_FUNCTION_ARGS)
/* Get the current relation length */
nblocks = RelationGetNumberOfBlocks(rel);
- /* prepare access strategy for this index */- bstrategy = GetAccessStrategy(BAS_BULKREAD);-
/* Scan all blocks except the metapage (0th page) using streaming reads */
startblk = HASH_METAPAGE + 1;
@@ -680,7 +674,6 @@ pgstathashindex(PG_FUNCTION_ARGS)
*/
stream = read_stream_begin_relation(READ_STREAM_FULL |
READ_STREAM_USE_BATCHING,
- bstrategy,
rel,
MAIN_FORKNUM,
block_range_read_stream_cb,
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.cindex 6a7f8cb4a7c..90f7938e953 100644--- a/contrib/pgstattuple/pgstattuple.c+++ b/contrib/pgstattuple/pgstattuple.c@@ -64,22 +64,18 @@ typedef struct pgstattuple_type
uint64 free_space; /* free/reusable space in bytes */
} pgstattuple_type;
-typedef void (*pgstat_page) (pgstattuple_type *, Relation, BlockNumber,- BufferAccessStrategy);+typedef void (*pgstat_page) (pgstattuple_type *, Relation, BlockNumber);
static Datum build_pgstattuple_type(pgstattuple_type *stat,
FunctionCallInfo fcinfo);
static Datum pgstat_relation(Relation rel, FunctionCallInfo fcinfo);
static Datum pgstat_heap(Relation rel, FunctionCallInfo fcinfo);
static void pgstat_btree_page(pgstattuple_type *stat,
- Relation rel, BlockNumber blkno,- BufferAccessStrategy bstrategy);+ Relation rel, BlockNumber blkno);
static void pgstat_hash_page(pgstattuple_type *stat,
- Relation rel, BlockNumber blkno,- BufferAccessStrategy bstrategy);+ Relation rel, BlockNumber blkno);
static void pgstat_gist_page(pgstattuple_type *stat,
- Relation rel, BlockNumber blkno,- BufferAccessStrategy bstrategy);+ Relation rel, BlockNumber blkno);
static Datum pgstat_index(Relation rel, BlockNumber start,
pgstat_page pagefn, FunctionCallInfo fcinfo);
static void pgstat_index_page(pgstattuple_type *stat, Page page,
@@ -376,7 +372,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
CHECK_FOR_INTERRUPTS();
buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
- RBM_NORMAL, hscan->rs_strategy);+ RBM_NORMAL);
LockBuffer(buffer, BUFFER_LOCK_SHARE);
stat.free_space += PageGetExactFreeSpace(BufferGetPage(buffer));
UnlockReleaseBuffer(buffer);
@@ -389,7 +385,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
CHECK_FOR_INTERRUPTS();
buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
- RBM_NORMAL, hscan->rs_strategy);+ RBM_NORMAL);
LockBuffer(buffer, BUFFER_LOCK_SHARE);
stat.free_space += PageGetExactFreeSpace(BufferGetPage(buffer));
UnlockReleaseBuffer(buffer);
@@ -408,13 +404,12 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
* pgstat_btree_page -- check tuples in a btree page
*/
static void
-pgstat_btree_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,- BufferAccessStrategy bstrategy)+pgstat_btree_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno)
{
Buffer buf;
Page page;
- buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy);+ buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL);
LockBuffer(buf, BT_READ);
page = BufferGetPage(buf);
@@ -452,13 +447,12 @@ pgstat_btree_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
* pgstat_hash_page -- check tuples in a hash page
*/
static void
-pgstat_hash_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,- BufferAccessStrategy bstrategy)+pgstat_hash_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno)
{
Buffer buf;
Page page;
- buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy);+ buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL);
LockBuffer(buf, HASH_READ);
page = BufferGetPage(buf);
@@ -500,13 +494,12 @@ pgstat_hash_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,
* pgstat_gist_page -- check tuples in a gist page
*/
static void
-pgstat_gist_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno,- BufferAccessStrategy bstrategy)+pgstat_gist_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno)
{
Buffer buf;
Page page;
- buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy);+ buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL);
LockBuffer(buf, GIST_SHARE);
page = BufferGetPage(buf);
if (PageIsNew(page))
@@ -539,12 +532,8 @@ pgstat_index(Relation rel, BlockNumber start, pgstat_page pagefn,
{
BlockNumber nblocks;
BlockNumber blkno;
- BufferAccessStrategy bstrategy;
pgstattuple_type stat = {0};
- /* prepare access strategy for this index */- bstrategy = GetAccessStrategy(BAS_BULKREAD);-
blkno = start;
for (;;)
{
@@ -565,7 +554,7 @@ pgstat_index(Relation rel, BlockNumber start, pgstat_page pagefn,
{
CHECK_FOR_INTERRUPTS();
- pagefn(&stat, rel, blkno, bstrategy);+ pagefn(&stat, rel, blkno);
}
}
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgmlindex 0848c18d329..7cb8839245c 100644--- a/doc/src/sgml/config.sgml+++ b/doc/src/sgml/config.sgml@@ -2107,36 +2107,6 @@ include_dir 'conf.d'
</listitem>
</varlistentry>
- <varlistentry id="guc-vacuum-buffer-usage-limit" xreflabel="vacuum_buffer_usage_limit">- <term>- <varname>vacuum_buffer_usage_limit</varname> (<type>integer</type>)- <indexterm>- <primary><varname>vacuum_buffer_usage_limit</varname> configuration parameter</primary>- </indexterm>- </term>- <listitem>- <para>- Specifies the size of the- <glossterm linkend="glossary-buffer-access-strategy">Buffer Access Strategy</glossterm>- used by the <command>VACUUM</command> and <command>ANALYZE</command>- commands. A setting of <literal>0</literal> will allow the operation- to use any number of <varname>shared_buffers</varname>. Otherwise- valid sizes range from <literal>128 kB</literal> to- <literal>16 GB</literal>. If the specified size would exceed 1/8 the- size of <varname>shared_buffers</varname>, the size is silently capped- to that value. The default value is <literal>2MB</literal>. If- this value is specified without units, it is taken as kilobytes. This- parameter can be set at any time. It can be overridden for- <xref linkend="sql-vacuum"/> and <xref linkend="sql-analyze"/>- when passing the <option>BUFFER_USAGE_LIMIT</option> option. Higher- settings can allow <command>VACUUM</command> and- <command>ANALYZE</command> to run more quickly, but having too large a- setting may cause too many other useful pages to be evicted from- shared buffers.- </para>- </listitem>- </varlistentry>-
<varlistentry id="guc-logical-decoding-work-mem" xreflabel="logical_decoding_work_mem">
<term><varname>logical_decoding_work_mem</varname> (<type>integer</type>)
<indexterm>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgmlindex d1a20d001e9..18d17540430 100644--- a/doc/src/sgml/monitoring.sgml+++ b/doc/src/sgml/monitoring.sgml@@ -2993,28 +2993,6 @@ description | Waiting for a newly initialized WAL file to reach durable storage
<literal>init</literal>.
</para>
</listitem>
- <listitem>- <para>- <literal>vacuum</literal>: I/O operations performed outside of shared- buffers while vacuuming and analyzing permanent relations. Temporary- table vacuums use the same local buffer pool as other temporary table- I/O operations and are tracked in <varname>context</varname>- <literal>normal</literal>.- </para>- </listitem>- <listitem>- <para>- <literal>bulkread</literal>: Certain large read I/O operations- done outside of shared buffers, for example, a sequential scan of a- large table.- </para>- </listitem>- <listitem>- <para>- <literal>bulkwrite</literal>: Certain large write I/O operations- done outside of shared buffers, such as <command>COPY</command>.- </para>- </listitem>
</itemizedlist>
</entry>
</row>
@@ -3180,13 +3158,8 @@ description | Waiting for a newly initialized WAL file to reach durable storage
buffer in order to make it available for another use.
</para>
<para>
- In <varname>context</varname> <literal>normal</literal>, this counts- the number of times a block was evicted from a buffer and replaced with- another block. In <varname>context</varname>s- <literal>bulkwrite</literal>, <literal>bulkread</literal>, and- <literal>vacuum</literal>, this counts the number of times a block was- evicted from shared buffers in order to add the shared buffer to a- separate, size-limited ring buffer for use in a bulk I/O operation.+ This counts the number of times a block was evicted from a buffer and+ replaced with another block.
</para>
</entry>
</row>
@@ -3197,10 +3170,9 @@ description | Waiting for a newly initialized WAL file to reach durable storage
<structfield>reuses</structfield> <type>bigint</type>
</para>
<para>
- The number of times an existing buffer in a size-limited ring buffer- outside of shared buffers was reused as part of an I/O operation in the- <literal>bulkread</literal>, <literal>bulkwrite</literal>, or- <literal>vacuum</literal> <varname>context</varname>s.+ Always zero. This column previously counted reuses of buffers in a+ size-limited ring buffer (buffer access strategy); ring buffers have+ been removed, so no reuses are tracked.
</para>
</entry>
</row>
diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgmlindex ec81f00fecf..7f5159111d8 100644--- a/doc/src/sgml/ref/analyze.sgml+++ b/doc/src/sgml/ref/analyze.sgml@@ -27,7 +27,6 @@ ANALYZE [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <r
VERBOSE [ <replaceable class="parameter">boolean</replaceable> ]
SKIP_LOCKED [ <replaceable class="parameter">boolean</replaceable> ]
- BUFFER_USAGE_LIMIT <replaceable class="parameter">size</replaceable>
<phrase>and <replaceable class="parameter">table_and_columns</replaceable> is:</phrase>
@@ -88,26 +87,6 @@ ANALYZE [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <r
</listitem>
</varlistentry>
- <varlistentry>- <term><literal>BUFFER_USAGE_LIMIT</literal></term>- <listitem>- <para>- Specifies the- <glossterm linkend="glossary-buffer-access-strategy">Buffer Access Strategy</glossterm>- ring buffer size for <command>ANALYZE</command>. This size is used to- calculate the number of shared buffers which will be reused as part of- this strategy. <literal>0</literal> disables use of a- <literal>Buffer Access Strategy</literal>. When this option is not- specified, <command>ANALYZE</command> uses the value from- <xref linkend="guc-vacuum-buffer-usage-limit"/>. Higher settings can- allow <command>ANALYZE</command> to run more quickly, but having too- large a setting may cause too many other useful pages to be evicted from- shared buffers. The minimum value is <literal>128 kB</literal> and the- maximum value is <literal>16 GB</literal>.- </para>- </listitem>- </varlistentry>-
<varlistentry>
<term><replaceable class="parameter">boolean</replaceable></term>
<listitem>
diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgmlindex 38ee973ea05..d44a49f8efe 100644--- a/doc/src/sgml/ref/vacuum.sgml+++ b/doc/src/sgml/ref/vacuum.sgml@@ -37,7 +37,6 @@ VACUUM [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <re
PARALLEL <replaceable class="parameter">integer</replaceable>
SKIP_DATABASE_STATS [ <replaceable class="parameter">boolean</replaceable> ]
ONLY_DATABASE_STATS [ <replaceable class="parameter">boolean</replaceable> ]
- BUFFER_USAGE_LIMIT <replaceable class="parameter">size</replaceable>
FULL [ <replaceable class="parameter">boolean</replaceable> ]
<phrase>and <replaceable class="parameter">table_and_columns</replaceable> is:</phrase>
@@ -309,30 +308,6 @@ VACUUM [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <re
</listitem>
</varlistentry>
- <varlistentry>- <term><literal>BUFFER_USAGE_LIMIT</literal></term>- <listitem>- <para>- Specifies the- <glossterm linkend="glossary-buffer-access-strategy">Buffer Access Strategy</glossterm>- ring buffer size for <command>VACUUM</command>. This size is used to- calculate the number of shared buffers which will be reused as part of- this strategy. <literal>0</literal> disables use of a- <literal>Buffer Access Strategy</literal>. If <option>ANALYZE</option>- is also specified, the <option>BUFFER_USAGE_LIMIT</option> value is used- for both the vacuum and analyze stages. This option can't be used with- the <option>FULL</option> option except if <option>ANALYZE</option> is- also specified. When this option is not specified,- <command>VACUUM</command> uses the value from- <xref linkend="guc-vacuum-buffer-usage-limit"/>. Higher settings can- allow <command>VACUUM</command> to run more quickly, but having too- large a setting may cause too many other useful pages to be evicted from- shared buffers. The minimum value is <literal>128 kB</literal> and the- maximum value is <literal>16 GB</literal>.- </para>- </listitem>- </varlistentry>-
<varlistentry>
<term><literal>FULL</literal></term>
<listitem>
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.cindex bdb30752e09..5d6539f5483 100644--- a/src/backend/access/brin/brin.c+++ b/src/backend/access/brin/brin.c@@ -224,7 +224,7 @@ static void form_and_insert_tuple(BrinBuildState *state);
static void form_and_spill_tuple(BrinBuildState *state);
static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a,
BrinTuple *b);
-static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy);+static void brin_vacuum_scan(Relation idxrel);
static bool add_values_to_range(Relation idxRel, BrinDesc *bdesc,
BrinMemTuple *dtup, const Datum *values, const bool *nulls);
static bool check_null_keys(BrinValues *bval, ScanKey *nullkeys, int nnullkeys);
@@ -1129,7 +1129,7 @@ brinbuild(Relation heap, Relation index, IndexInfo *indexInfo)
* whole relation will be rolled back.
*/
- meta = ExtendBufferedRel(BMR_REL(index), MAIN_FORKNUM, NULL,+ meta = ExtendBufferedRel(BMR_REL(index), MAIN_FORKNUM,
EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK);
Assert(BufferGetBlockNumber(meta) == BRIN_METAPAGE_BLKNO);
@@ -1281,7 +1281,7 @@ brinbuildempty(Relation index)
Buffer metabuf;
/* An empty BRIN index has a metapage only. */
- metabuf = ExtendBufferedRel(BMR_REL(index), INIT_FORKNUM, NULL,+ metabuf = ExtendBufferedRel(BMR_REL(index), INIT_FORKNUM,
EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK);
/* Initialize and xlog metabuffer. */
@@ -1336,7 +1336,7 @@ brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
heapRel = table_open(IndexGetRelation(RelationGetRelid(info->index), false),
AccessShareLock);
- brin_vacuum_scan(info->index, info->strategy);+ brin_vacuum_scan(info->index);
brinsummarize(info->index, heapRel, BRIN_ALL_BLOCKRANGES, false,
&stats->num_index_tuples, &stats->num_index_tuples);
@@ -2171,7 +2171,7 @@ union_tuples(BrinDesc *bdesc, BrinMemTuple *a, BrinTuple *b)
* and such.
*/
static void
-brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy)+brin_vacuum_scan(Relation idxrel)
{
BlockRangeReadStreamPrivate p;
ReadStream *stream;
@@ -2187,7 +2187,6 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy)
stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
READ_STREAM_FULL |
READ_STREAM_USE_BATCHING,
- strategy,
idxrel,
MAIN_FORKNUM,
block_range_read_stream_cb,
diff --git a/src/backend/access/brin/brin_revmap.c b/src/backend/access/brin/brin_revmap.cindex 233355cb2d5..01c4104ac88 100644--- a/src/backend/access/brin/brin_revmap.c+++ b/src/backend/access/brin/brin_revmap.c@@ -559,7 +559,7 @@ revmap_physical_extend(BrinRevmap *revmap)
}
else
{
- buf = ExtendBufferedRel(BMR_REL(irel), MAIN_FORKNUM, NULL,+ buf = ExtendBufferedRel(BMR_REL(irel), MAIN_FORKNUM,
EB_LOCK_FIRST);
if (BufferGetBlockNumber(buf) != mapBlk)
{
diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.cindex cb9ed3b563c..32ad65cc95f 100644--- a/src/backend/access/gin/gininsert.c+++ b/src/backend/access/gin/gininsert.c@@ -815,9 +815,9 @@ ginbuildempty(Relation index)
MetaBuffer;
/* An empty GIN index has two pages. */
- MetaBuffer = ExtendBufferedRel(BMR_REL(index), INIT_FORKNUM, NULL,+ MetaBuffer = ExtendBufferedRel(BMR_REL(index), INIT_FORKNUM,
EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK);
- RootBuffer = ExtendBufferedRel(BMR_REL(index), INIT_FORKNUM, NULL,+ RootBuffer = ExtendBufferedRel(BMR_REL(index), INIT_FORKNUM,
EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK);
/* Initialize and xlog metabuffer and root buffer. */
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.cindex e7cba81d477..2d7394aedd5 100644--- a/src/backend/access/gin/ginutil.c+++ b/src/backend/access/gin/ginutil.c@@ -336,7 +336,7 @@ GinNewBuffer(Relation index)
}
/* Must extend the file */
- buffer = ExtendBufferedRel(BMR_REL(index), MAIN_FORKNUM, NULL,+ buffer = ExtendBufferedRel(BMR_REL(index), MAIN_FORKNUM,
EB_LOCK_FIRST);
return buffer;
diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.cindex 840543eb664..86538de39d6 100644--- a/src/backend/access/gin/ginvacuum.c+++ b/src/backend/access/gin/ginvacuum.c@@ -32,7 +32,6 @@ struct GinVacuumState
IndexBulkDeleteCallback callback;
void *callback_state;
GinState ginstate;
- BufferAccessStrategy strategy;
MemoryContext tmpCxt;
};
@@ -289,7 +288,7 @@ ginScanPostingTreeToDelete(GinVacuumState *gvs, DataPageDeleteStack *myStackItem
childBuffer = ReadBufferExtended(gvs->index,
MAIN_FORKNUM,
PostingItemGetBlockNumber(pitem),
- RBM_NORMAL, gvs->strategy);+ RBM_NORMAL);
LockBuffer(childBuffer, GIN_EXCLUSIVE);
/* Allocate a child stack entry on first use; reuse thereafter */
@@ -389,7 +388,7 @@ ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber blkno)
PostingItem *pitem;
buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
- RBM_NORMAL, gvs->strategy);+ RBM_NORMAL);
LockBuffer(buffer, GIN_SHARE);
page = BufferGetPage(buffer);
@@ -430,7 +429,7 @@ ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber blkno)
break;
buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
- RBM_NORMAL, gvs->strategy);+ RBM_NORMAL);
LockBuffer(buffer, GIN_EXCLUSIVE);
page = BufferGetPage(buffer);
}
@@ -454,7 +453,7 @@ ginVacuumPostingTree(GinVacuumState *gvs, BlockNumber rootBlkno)
bool deleted PG_USED_FOR_ASSERTS_ONLY;
buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, rootBlkno,
- RBM_NORMAL, gvs->strategy);+ RBM_NORMAL);
/*
* Lock posting tree root for cleanup to ensure there are no
@@ -615,7 +614,6 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
gvs.index = index;
gvs.callback = callback;
gvs.callback_state = callback_state;
- gvs.strategy = info->strategy;
initGinState(&gvs.ginstate, index);
/* first time through? */
@@ -636,7 +634,7 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
gvs.result = stats;
buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
- RBM_NORMAL, info->strategy);+ RBM_NORMAL);
/* find leaf page */
for (;;)
@@ -669,7 +667,7 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
UnlockReleaseBuffer(buffer);
buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
- RBM_NORMAL, info->strategy);+ RBM_NORMAL);
}
/* right now we found leftmost page in entry's BTree */
@@ -712,7 +710,7 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
break;
buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
- RBM_NORMAL, info->strategy);+ RBM_NORMAL);
LockBuffer(buffer, GIN_EXCLUSIVE);
}
@@ -794,7 +792,6 @@ ginvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
READ_STREAM_FULL |
READ_STREAM_USE_BATCHING,
- info->strategy,
index,
MAIN_FORKNUM,
block_range_read_stream_cb,
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.cindex 8565e225be7..ae494742a4b 100644--- a/src/backend/access/gist/gist.c+++ b/src/backend/access/gist/gist.c@@ -142,7 +142,7 @@ gistbuildempty(Relation index)
Buffer buffer;
/* Initialize the root page */
- buffer = ExtendBufferedRel(BMR_REL(index), INIT_FORKNUM, NULL,+ buffer = ExtendBufferedRel(BMR_REL(index), INIT_FORKNUM,
EB_SKIP_EXTENSION_LOCK | EB_LOCK_FIRST);
/* Initialize and xlog buffer */
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.cindex 0f58f61879f..170174f62fd 100644--- a/src/backend/access/gist/gistutil.c+++ b/src/backend/access/gist/gistutil.c@@ -877,7 +877,7 @@ gistNewBuffer(Relation r, Relation heaprel)
}
/* Must extend the file */
- buffer = ExtendBufferedRel(BMR_REL(r), MAIN_FORKNUM, NULL,+ buffer = ExtendBufferedRel(BMR_REL(r), MAIN_FORKNUM,
EB_LOCK_FIRST);
return buffer;
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.cindex 686a0418054..c366ed35ea8 100644--- a/src/backend/access/gist/gistvacuum.c+++ b/src/backend/access/gist/gistvacuum.c@@ -218,7 +218,6 @@ gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
READ_STREAM_FULL |
READ_STREAM_USE_BATCHING,
- info->strategy,
rel,
MAIN_FORKNUM,
block_range_read_stream_cb,
@@ -491,8 +490,7 @@ restart:
/* check for vacuum delay while not holding any buffer lock */
vacuum_delay_point(false);
- buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,- info->strategy);+ buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL);
goto restart;
}
}
@@ -524,8 +522,7 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistVacState *vstate)
int ntodelete;
int deleted;
- buffer = ReadBufferExtended(rel, MAIN_FORKNUM, (BlockNumber) blkno,- RBM_NORMAL, info->strategy);+ buffer = ReadBufferExtended(rel, MAIN_FORKNUM, (BlockNumber) blkno, RBM_NORMAL);
LockBuffer(buffer, GIST_SHARE);
page = BufferGetPage(buffer);
@@ -590,8 +587,7 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistVacState *vstate)
if (PageGetMaxOffsetNumber(page) == FirstOffsetNumber)
break;
- leafbuf = ReadBufferExtended(rel, MAIN_FORKNUM, leafs_to_delete[i],- RBM_NORMAL, info->strategy);+ leafbuf = ReadBufferExtended(rel, MAIN_FORKNUM, leafs_to_delete[i], RBM_NORMAL);
LockBuffer(leafbuf, GIST_EXCLUSIVE);
gistcheckpage(rel, leafbuf);
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.cindex 8d8cd30dc38..1bbb7224bfc 100644--- a/src/backend/access/hash/hash.c+++ b/src/backend/access/hash/hash.c@@ -542,7 +542,6 @@ hashbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
*/
stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
READ_STREAM_USE_BATCHING,
- info->strategy,
rel,
MAIN_FORKNUM,
hash_bulkdelete_read_stream_cb,
@@ -616,7 +615,7 @@ bucket_loop:
bucket_buf = buf;
- hashbucketcleanup(rel, cur_bucket, bucket_buf, blkno, info->strategy,+ hashbucketcleanup(rel, cur_bucket, bucket_buf, blkno,
cachedmetap->hashm_maxbucket,
cachedmetap->hashm_highmask,
cachedmetap->hashm_lowmask, &tuples_removed,
@@ -765,7 +764,7 @@ hashvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
*/
void
hashbucketcleanup(Relation rel, Bucket cur_bucket, Buffer bucket_buf,
- BlockNumber bucket_blkno, BufferAccessStrategy bstrategy,+ BlockNumber bucket_blkno,
uint32 maxbucket, uint32 highmask, uint32 lowmask,
double *tuples_removed, double *num_index_tuples,
bool split_cleanup,
@@ -935,9 +934,8 @@ hashbucketcleanup(Relation rel, Bucket cur_bucket, Buffer bucket_buf,
if (!BlockNumberIsValid(blkno))
break;
- next_buf = _hash_getbuf_with_strategy(rel, blkno, HASH_WRITE,- LH_OVERFLOW_PAGE,- bstrategy);+ next_buf = _hash_getbuf(rel, blkno, HASH_WRITE,+ LH_OVERFLOW_PAGE);
/*
* release the lock on previous page after acquiring the lock on next
@@ -1004,8 +1002,7 @@ hashbucketcleanup(Relation rel, Bucket cur_bucket, Buffer bucket_buf,
* ordering of tuples for a scan that has started before it.
*/
if (bucket_dirty && IsBufferCleanupOK(bucket_buf))
- _hash_squeezebucket(rel, cur_bucket, bucket_blkno, bucket_buf,- bstrategy);+ _hash_squeezebucket(rel, cur_bucket, bucket_blkno, bucket_buf);
else
LockBuffer(bucket_buf, BUFFER_LOCK_UNLOCK);
}
diff --git a/src/backend/access/hash/hashovfl.c b/src/backend/access/hash/hashovfl.cindex dbc57ef958c..5b35fed33f9 100644--- a/src/backend/access/hash/hashovfl.c+++ b/src/backend/access/hash/hashovfl.c@@ -491,8 +491,7 @@ _hash_firstfreebit(uint32 map)
BlockNumber
_hash_freeovflpage(Relation rel, Buffer bucketbuf, Buffer ovflbuf,
Buffer wbuf, IndexTuple *itups, OffsetNumber *itup_offsets,
- Size *tups_size, uint16 nitups,- BufferAccessStrategy bstrategy)+ Size *tups_size, uint16 nitups)
{
HashMetaPage metap;
Buffer metabuf;
@@ -539,20 +538,16 @@ _hash_freeovflpage(Relation rel, Buffer bucketbuf, Buffer ovflbuf,
if (prevblkno == writeblkno)
prevbuf = wbuf;
else
- prevbuf = _hash_getbuf_with_strategy(rel,- prevblkno,- HASH_WRITE,- LH_BUCKET_PAGE | LH_OVERFLOW_PAGE,- bstrategy);+ prevbuf = _hash_getbuf(rel,+ prevblkno,+ HASH_WRITE,+ LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
}
if (BlockNumberIsValid(nextblkno))
- nextbuf = _hash_getbuf_with_strategy(rel,- nextblkno,- HASH_WRITE,- LH_OVERFLOW_PAGE,- bstrategy);-- /* Note: bstrategy is intentionally not used for metapage and bitmap */+ nextbuf = _hash_getbuf(rel,+ nextblkno,+ HASH_WRITE,+ LH_OVERFLOW_PAGE);
/* Read the metapage so we can determine which bitmap page to use */
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
@@ -843,8 +838,7 @@ void
_hash_squeezebucket(Relation rel,
Bucket bucket,
BlockNumber bucket_blkno,
- Buffer bucket_buf,- BufferAccessStrategy bstrategy)+ Buffer bucket_buf)
{
BlockNumber wblkno;
BlockNumber rblkno;
@@ -886,11 +880,10 @@ _hash_squeezebucket(Relation rel,
rblkno = ropaque->hasho_nextblkno;
if (rbuf != InvalidBuffer)
_hash_relbuf(rel, rbuf);
- rbuf = _hash_getbuf_with_strategy(rel,- rblkno,- HASH_WRITE,- LH_OVERFLOW_PAGE,- bstrategy);+ rbuf = _hash_getbuf(rel,+ rblkno,+ HASH_WRITE,+ LH_OVERFLOW_PAGE);
rpage = BufferGetPage(rbuf);
ropaque = HashPageGetOpaque(rpage);
Assert(ropaque->hasho_bucket == bucket);
@@ -952,11 +945,10 @@ readpage:
/* don't need to move to next page if we reached the read page */
if (wblkno != rblkno)
- next_wbuf = _hash_getbuf_with_strategy(rel,- wblkno,- HASH_WRITE,- LH_OVERFLOW_PAGE,- bstrategy);+ next_wbuf = _hash_getbuf(rel,+ wblkno,+ HASH_WRITE,+ LH_OVERFLOW_PAGE);
if (nitups > 0)
{
@@ -1098,7 +1090,7 @@ readpage:
/* free this overflow page (releases rbuf) */
_hash_freeovflpage(rel, bucket_buf, rbuf, wbuf, itups, itup_offsets,
- tups_size, nitups, bstrategy);+ tups_size, nitups);
/* be tidy */
for (i = 0; i < nitups; i++)
@@ -1115,11 +1107,10 @@ readpage:
return;
}
- rbuf = _hash_getbuf_with_strategy(rel,- rblkno,- HASH_WRITE,- LH_OVERFLOW_PAGE,- bstrategy);+ rbuf = _hash_getbuf(rel,+ rblkno,+ HASH_WRITE,+ LH_OVERFLOW_PAGE);
rpage = BufferGetPage(rbuf);
ropaque = HashPageGetOpaque(rpage);
Assert(ropaque->hasho_bucket == bucket);
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.cindex 8099b0d021f..bdf1a255f1f 100644--- a/src/backend/access/hash/hashpage.c+++ b/src/backend/access/hash/hashpage.c@@ -139,8 +139,7 @@ _hash_getinitbuf(Relation rel, BlockNumber blkno)
if (blkno == P_NEW)
elog(ERROR, "hash AM does not use P_NEW");
- buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_ZERO_AND_LOCK,- NULL);+ buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_ZERO_AND_LOCK);
/* ref count and lock type are correct */
@@ -209,7 +208,7 @@ _hash_getnewbuf(Relation rel, BlockNumber blkno, ForkNumber forkNum)
/* smgr insists we explicitly extend the relation */
if (blkno == nblocks)
{
- buf = ExtendBufferedRel(BMR_REL(rel), forkNum, NULL,+ buf = ExtendBufferedRel(BMR_REL(rel), forkNum,
EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK);
if (BufferGetBlockNumber(buf) != blkno)
elog(ERROR, "unexpected hash relation size: %u, should be %u",
@@ -217,8 +216,7 @@ _hash_getnewbuf(Relation rel, BlockNumber blkno, ForkNumber forkNum)
}
else
{
- buf = ReadBufferExtended(rel, forkNum, blkno, RBM_ZERO_AND_LOCK,- NULL);+ buf = ReadBufferExtended(rel, forkNum, blkno, RBM_ZERO_AND_LOCK);
}
/* ref count and lock type are correct */
@@ -229,34 +227,6 @@ _hash_getnewbuf(Relation rel, BlockNumber blkno, ForkNumber forkNum)
return buf;
}
-/*- * _hash_getbuf_with_strategy() -- Get a buffer with nondefault strategy.- *- * This is identical to _hash_getbuf() but also allows a buffer access- * strategy to be specified. We use this for VACUUM operations.- */-Buffer-_hash_getbuf_with_strategy(Relation rel, BlockNumber blkno,- int access, int flags,- BufferAccessStrategy bstrategy)-{- Buffer buf;-- if (blkno == P_NEW)- elog(ERROR, "hash AM does not use P_NEW");-- buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy);-- if (access != HASH_NOLOCK)- LockBuffer(buf, access);-- /* ref count and lock type are correct */-- _hash_checkpage(rel, buf, flags);-- return buf;-}-
/*
* _hash_relbuf() -- release a locked buffer.
*
@@ -758,7 +728,7 @@ restart_expand:
/* Release the metapage lock. */
LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
- hashbucketcleanup(rel, old_bucket, buf_oblkno, start_oblkno, NULL,+ hashbucketcleanup(rel, old_bucket, buf_oblkno, start_oblkno,
maxbucket, highmask, lowmask, NULL, NULL, true,
NULL, NULL);
@@ -1333,7 +1303,7 @@ _hash_splitbucket(Relation rel,
{
LockBuffer(bucket_nbuf, BUFFER_LOCK_UNLOCK);
hashbucketcleanup(rel, obucket, bucket_obuf,
- BufferGetBlockNumber(bucket_obuf), NULL,+ BufferGetBlockNumber(bucket_obuf),
maxbucket, highmask, lowmask, NULL, NULL, true,
NULL, NULL);
}
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.cindex 9cdc221675b..48e7c421bf4 100644--- a/src/backend/access/heap/heapam.c+++ b/src/backend/access/heap/heapam.c@@ -359,7 +359,6 @@ static void
initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
{
ParallelBlockTableScanDesc bpscan = NULL;
- bool allow_strat;
bool allow_sync;
/*
@@ -396,24 +395,10 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
if (!RelationUsesLocalBuffers(scan->rs_base.rs_rd) &&
scan->rs_nblocks > NBuffers / 4)
{
- allow_strat = (scan->rs_base.rs_flags & SO_ALLOW_STRAT) != 0;
allow_sync = (scan->rs_base.rs_flags & SO_ALLOW_SYNC) != 0;
}
else
- allow_strat = allow_sync = false;-- if (allow_strat)- {- /* During a rescan, keep the previous strategy object. */- if (scan->rs_strategy == NULL)- scan->rs_strategy = GetAccessStrategy(BAS_BULKREAD);- }- else- {- if (scan->rs_strategy != NULL)- FreeAccessStrategy(scan->rs_strategy);- scan->rs_strategy = NULL;- }+ allow_sync = false;
if (scan->rs_base.rs_parallel != NULL)
{
@@ -1202,7 +1187,6 @@ heap_beginscan(Relation relation, Snapshot snapshot,
scan->rs_base.rs_flags = flags;
scan->rs_base.rs_parallel = parallel_scan;
scan->rs_base.rs_instrument = NULL;
- scan->rs_strategy = NULL; /* set in initscan */
scan->rs_cbuf = InvalidBuffer;
/*
@@ -1273,8 +1257,7 @@ heap_beginscan(Relation relation, Snapshot snapshot,
/*
* Set up a read stream for sequential scans and TID range scans. This
- * should be done after initscan() because initscan() allocates the- * BufferAccessStrategy object passed to the read stream API.+ * should be done after initscan().
*/
if (scan->rs_base.rs_flags & SO_TYPE_SEQSCAN ||
scan->rs_base.rs_flags & SO_TYPE_TIDRANGESCAN)
@@ -1295,7 +1278,6 @@ heap_beginscan(Relation relation, Snapshot snapshot,
*/
scan->rs_read_stream = read_stream_begin_relation(READ_STREAM_SEQUENTIAL |
READ_STREAM_USE_BATCHING,
- scan->rs_strategy,
scan->rs_base.rs_rd,
MAIN_FORKNUM,
cb,
@@ -1306,7 +1288,6 @@ heap_beginscan(Relation relation, Snapshot snapshot,
{
scan->rs_read_stream = read_stream_begin_relation(READ_STREAM_DEFAULT |
READ_STREAM_USE_BATCHING,
- scan->rs_strategy,
scan->rs_base.rs_rd,
MAIN_FORKNUM,
bitmapheap_stream_read_next,
@@ -1402,9 +1383,6 @@ heap_endscan(TableScanDesc sscan)
if (BufferIsValid(scan->rs_vmbuffer))
ReleaseBuffer(scan->rs_vmbuffer);
- /*- * Must free the read stream before freeing the BufferAccessStrategy.- */
if (scan->rs_read_stream)
read_stream_end(scan->rs_read_stream);
@@ -1416,9 +1394,6 @@ heap_endscan(TableScanDesc sscan)
if (scan->rs_base.rs_key)
pfree(scan->rs_base.rs_key);
- if (scan->rs_strategy != NULL)- FreeAccessStrategy(scan->rs_strategy);-
if (scan->rs_parallelworkerdata != NULL)
pfree(scan->rs_parallelworkerdata);
@@ -1939,7 +1914,6 @@ GetBulkInsertState(void)
BulkInsertState bistate;
bistate = (BulkInsertState) palloc_object(BulkInsertStateData);
- bistate->strategy = GetAccessStrategy(BAS_BULKWRITE);
bistate->current_buf = InvalidBuffer;
bistate->next_free = InvalidBlockNumber;
bistate->last_free = InvalidBlockNumber;
@@ -1955,7 +1929,6 @@ FreeBulkInsertState(BulkInsertState bistate)
{
if (bistate->current_buf != InvalidBuffer)
ReleaseBuffer(bistate->current_buf);
- FreeAccessStrategy(bistate->strategy);
pfree(bistate);
}
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.cindex bf87430cf01..d14658d7218 100644--- a/src/backend/access/heap/heapam_handler.c+++ b/src/backend/access/heap/heapam_handler.c@@ -2213,9 +2213,9 @@ heapam_scan_sample_next_block(TableScanDesc scan, SampleScanState *scanstate)
*/
CHECK_FOR_INTERRUPTS();
- /* Read page using selected strategy */+ /* Read page */
hscan->rs_cbuf = ReadBufferExtended(hscan->rs_base.rs_rd, MAIN_FORKNUM,
- blockno, RBM_NORMAL, hscan->rs_strategy);+ blockno, RBM_NORMAL);
/* in pagemode, prune the page and determine visible tuple offsets */
if (hscan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.cindex e96e0f77d92..9bf7abc247f 100644--- a/src/backend/access/heap/hio.c+++ b/src/backend/access/heap/hio.c@@ -80,7 +80,8 @@ RelationPutHeapTuple(Relation relation,
}
/*
- * Read in a buffer in mode, using bulk-insert strategy if bistate isn't NULL.+ * Read in a buffer in mode, using the bistate's pinned target page if+ * available.
*/
static Buffer
ReadBufferBI(Relation relation, BlockNumber targetBlock,
@@ -91,7 +92,7 @@ ReadBufferBI(Relation relation, BlockNumber targetBlock,
/* If not bulk-insert, exactly like ReadBuffer */
if (!bistate)
return ReadBufferExtended(relation, MAIN_FORKNUM, targetBlock,
- mode, NULL);+ mode);
/* If we have the desired block already pinned, re-pin and return it */
if (bistate->current_buf != InvalidBuffer)
@@ -113,9 +114,9 @@ ReadBufferBI(Relation relation, BlockNumber targetBlock,
bistate->current_buf = InvalidBuffer;
}
- /* Perform a read using the buffer strategy */+ /* Perform the read */
buffer = ReadBufferExtended(relation, MAIN_FORKNUM, targetBlock,
- mode, bistate->strategy);+ mode);
/* Save the selected block as target for future inserts */
IncrBufferRefCount(buffer);
@@ -337,7 +338,6 @@ RelationAddBlocks(Relation relation, BulkInsertState bistate,
* way larger.
*/
first_block = ExtendBufferedRelBy(BMR_REL(relation), MAIN_FORKNUM,
- bistate ? bistate->strategy : NULL,
EB_LOCK_FIRST,
extend_by_pages,
victim_buffers,
@@ -484,8 +484,7 @@ RelationAddBlocks(Relation relation, BulkInsertState bistate,
*
* The caller can also provide a BulkInsertState object to optimize many
* insertions into the same relation. This keeps a pin on the current
- * insertion target page (to save pin/unpin cycles) and also passes a- * BULKWRITE buffer selection strategy object to the buffer manager.+ * insertion target page (to save pin/unpin cycles).
* Passing NULL for bistate selects the default behavior.
*
* We don't fill existing pages further than the fillfactor, except for large
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.cindex 39395aed0d5..8bc8e470af3 100644--- a/src/backend/access/heap/vacuumlazy.c+++ b/src/backend/access/heap/vacuumlazy.c@@ -257,7 +257,6 @@ typedef struct LVRelState
int nindexes;
/* Buffer access strategy and parallel vacuum state */
- BufferAccessStrategy bstrategy;
ParallelVacuumState *pvs;
/* Aggressive VACUUM? (must set relfrozenxid >= FreezeLimit) */
@@ -621,8 +620,7 @@ heap_vacuum_eager_scan_setup(LVRelState *vacrel, const VacuumParams *params)
* and locked the relation.
*/
void
-heap_vacuum_rel(Relation rel, const VacuumParams *params,- BufferAccessStrategy bstrategy)+heap_vacuum_rel(Relation rel, const VacuumParams *params)
{
LVRelState *vacrel;
bool verbose,
@@ -699,7 +697,6 @@ heap_vacuum_rel(Relation rel, const VacuumParams *params,
vacrel->rel = rel;
vac_open_indexes(vacrel->rel, RowExclusiveLock, &vacrel->nindexes,
&vacrel->indrels);
- vacrel->bstrategy = bstrategy;
if (instrument && vacrel->nindexes > 0)
{
/* Copy index names used by instrumentation (not error reporting) */
@@ -1311,7 +1308,6 @@ lazy_scan_heap(LVRelState *vacrel)
* explicit work in heap_vac_scan_next_block.
*/
stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE,
- vacrel->bstrategy,
vacrel->rel,
MAIN_FORKNUM,
heap_vac_scan_next_block,
@@ -2670,7 +2666,6 @@ lazy_vacuum_heap_rel(LVRelState *vacrel)
*/
stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
READ_STREAM_USE_BATCHING,
- vacrel->bstrategy,
vacrel->rel,
MAIN_FORKNUM,
vacuum_reap_lp_read_stream_next,
@@ -2904,13 +2899,6 @@ lazy_check_wraparound_failsafe(LVRelState *vacrel)
VacuumFailsafeActive = true;
- /*- * Abandon use of a buffer access strategy to allow use of all of- * shared buffers. We assume the caller who allocated the memory for- * the BufferAccessStrategy will free it.- */- vacrel->bstrategy = NULL;-
/* Disable index vacuuming, index cleanup, and heap rel truncation */
vacrel->do_index_vacuuming = false;
vacrel->do_index_cleanup = false;
@@ -3023,7 +3011,6 @@ lazy_vacuum_one_index(Relation indrel, IndexBulkDeleteResult *istat,
ivinfo.estimated_count = true;
ivinfo.message_level = DEBUG2;
ivinfo.num_heap_tuples = reltuples;
- ivinfo.strategy = vacrel->bstrategy;
/*
* Update error traceback information.
@@ -3074,7 +3061,6 @@ lazy_cleanup_one_index(Relation indrel, IndexBulkDeleteResult *istat,
ivinfo.message_level = DEBUG2;
ivinfo.num_heap_tuples = reltuples;
- ivinfo.strategy = vacrel->bstrategy;
/*
* Update error traceback information.
@@ -3354,8 +3340,7 @@ count_nondeletable_pages(LVRelState *vacrel, bool *lock_waiter_detected)
prefetchedUntil = prefetchStart;
}
- buf = ReadBufferExtended(vacrel->rel, MAIN_FORKNUM, blkno, RBM_NORMAL,- vacrel->bstrategy);+ buf = ReadBufferExtended(vacrel->rel, MAIN_FORKNUM, blkno, RBM_NORMAL);
/* In this phase we only need shared access to the buffer */
LockBuffer(buf, BUFFER_LOCK_SHARE);
@@ -3446,8 +3431,7 @@ dead_items_alloc(LVRelState *vacrel, int nworkers)
vacrel->pvs = parallel_vacuum_init(vacrel->rel, vacrel->indrels,
vacrel->nindexes, nworkers,
vac_work_mem,
- vacrel->verbose ? INFO : DEBUG2,- vacrel->bstrategy);+ vacrel->verbose ? INFO : DEBUG2);
/*
* If parallel mode started, dead_items and dead_items_info spaces are
diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.cindex 4fd470702aa..b2612b1f710 100644--- a/src/backend/access/heap/visibilitymap.c+++ b/src/backend/access/heap/visibilitymap.c@@ -575,7 +575,7 @@ vm_readbuf(Relation rel, BlockNumber blkno, bool extend)
}
else
buf = ReadBufferExtended(rel, VISIBILITYMAP_FORKNUM, blkno,
- RBM_ZERO_ON_ERROR, NULL);+ RBM_ZERO_ON_ERROR);
/*
* Initializing the page when needed is trickier than it looks, because of
@@ -611,7 +611,7 @@ vm_extend(Relation rel, BlockNumber vm_nblocks)
{
Buffer buf;
- buf = ExtendBufferedRelTo(BMR_REL(rel), VISIBILITYMAP_FORKNUM, NULL,+ buf = ExtendBufferedRelTo(BMR_REL(rel), VISIBILITYMAP_FORKNUM,
EB_CREATE_FORK_IF_NEEDED |
EB_CLEAR_SIZE_CACHE,
vm_nblocks,
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.cindex 109017d6b52..7b5fc16d75c 100644--- a/src/backend/access/nbtree/nbtpage.c+++ b/src/backend/access/nbtree/nbtpage.c@@ -960,7 +960,7 @@ _bt_allocbuf(Relation rel, Relation heaprel)
* otherwise would make, as we can't use _bt_lockbuf() without introducing
* a race.
*/
- buf = ExtendBufferedRel(BMR_REL(rel), MAIN_FORKNUM, NULL, EB_LOCK_FIRST);+ buf = ExtendBufferedRel(BMR_REL(rel), MAIN_FORKNUM, EB_LOCK_FIRST);
if (!RelationUsesLocalBuffers(rel))
VALGRIND_MAKE_MEM_DEFINED(BufferGetPage(buf), BLCKSZ);
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.cindex 3df2c752ead..379a2d37def 100644--- a/src/backend/access/nbtree/nbtree.c+++ b/src/backend/access/nbtree/nbtree.c@@ -1324,7 +1324,6 @@ btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
READ_STREAM_FULL |
READ_STREAM_USE_BATCHING,
- info->strategy,
rel,
MAIN_FORKNUM,
block_range_read_stream_cb,
@@ -1730,8 +1729,7 @@ backtrack:
* recycle all-zero pages, not fail. Also, we want to use a
* nondefault buffer access strategy.
*/
- buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,- info->strategy);+ buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL);
goto backtrack;
}
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.cindex f2ee333f60d..785e385aab6 100644--- a/src/backend/access/spgist/spgutils.c+++ b/src/backend/access/spgist/spgutils.c@@ -432,7 +432,7 @@ SpGistNewBuffer(Relation index)
ReleaseBuffer(buffer);
}
- buffer = ExtendBufferedRel(BMR_REL(index), MAIN_FORKNUM, NULL,+ buffer = ExtendBufferedRel(BMR_REL(index), MAIN_FORKNUM,
EB_LOCK_FIRST);
return buffer;
diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.cindex c461f8dc02d..9f644edaf82 100644--- a/src/backend/access/spgist/spgvacuum.c+++ b/src/backend/access/spgist/spgvacuum.c@@ -704,8 +704,7 @@ spgprocesspending(spgBulkDeleteState *bds)
/* examine the referenced page */
blkno = ItemPointerGetBlockNumber(&pitem->tid);
- buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,- RBM_NORMAL, bds->info->strategy);+ buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno, RBM_NORMAL);
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
page = BufferGetPage(buffer);
@@ -830,7 +829,6 @@ spgvacuumscan(spgBulkDeleteState *bds)
stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
READ_STREAM_FULL |
READ_STREAM_USE_BATCHING,
- bds->info->strategy,
index,
MAIN_FORKNUM,
block_range_read_stream_cb,
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.cindex f2e10b82b7d..0c16b7fef0f 100644--- a/src/backend/access/transam/xloginsert.c+++ b/src/backend/access/transam/xloginsert.c@@ -1348,7 +1348,7 @@ log_newpage_range(Relation rel, ForkNumber forknum,
while (nbufs < XLR_MAX_BLOCK_ID && blkno < endblk)
{
Buffer buf = ReadBufferExtended(rel, forknum, blkno,
- RBM_NORMAL, NULL);+ RBM_NORMAL);
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.cindex d8c179c5dcc..e5be49f8b5d 100644--- a/src/backend/access/transam/xlogutils.c+++ b/src/backend/access/transam/xlogutils.c@@ -519,7 +519,7 @@ XLogReadBufferExtended(RelFileLocator rlocator, ForkNumber forknum,
{
/* page exists in file */
buffer = ReadBufferWithoutRelcache(rlocator, forknum, blkno,
- mode, NULL, true);+ mode, true);
}
else
{
@@ -536,7 +536,6 @@ XLogReadBufferExtended(RelFileLocator rlocator, ForkNumber forknum,
Assert(InRecovery);
buffer = ExtendBufferedRelTo(BMR_SMGR(smgr, RELPERSISTENCE_PERMANENT),
forknum,
- NULL,
EB_PERFORMING_RECOVERY |
EB_SKIP_EXTENSION_LOCK,
blkno + 1,
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.cindex 81bba4beac7..b0a7b8a33cd 100644--- a/src/backend/catalog/index.c+++ b/src/backend/catalog/index.c@@ -3431,7 +3431,6 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot)
ivinfo.estimated_count = true;
ivinfo.message_level = DEBUG2;
ivinfo.num_heap_tuples = heapRelation->rd_rel->reltuples;
- ivinfo.strategy = NULL;
/*
* Encode TIDs as int8 values for the sort, rather than directly sorting
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.cindex f66e80b757c..9b117a53124 100644--- a/src/backend/commands/analyze.c+++ b/src/backend/commands/analyze.c@@ -72,7 +72,6 @@ int default_statistics_target = 100;
/* A few variables that don't seem worth passing around as parameters */
static MemoryContext anl_context = NULL;
-static BufferAccessStrategy vac_strategy;
static void do_analyze_rel(Relation onerel,
@@ -108,8 +107,7 @@ static Datum ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull);
*/
void
analyze_rel(Oid relid, RangeVar *relation,
- const VacuumParams *params, List *va_cols, bool in_outer_xact,- BufferAccessStrategy bstrategy)+ const VacuumParams *params, List *va_cols, bool in_outer_xact)
{
Relation onerel;
int elevel;
@@ -124,7 +122,6 @@ analyze_rel(Oid relid, RangeVar *relation,
elevel = DEBUG2;
/* Set up static variables */
- vac_strategy = bstrategy;
/*
* Check for user-requested abort.
@@ -730,7 +727,6 @@ do_analyze_rel(Relation onerel, const VacuumParams *params,
ivinfo.estimated_count = true;
ivinfo.message_level = elevel;
ivinfo.num_heap_tuples = onerel->rd_rel->reltuples;
- ivinfo.strategy = vac_strategy;
stats = index_vacuum_cleanup(&ivinfo, NULL);
@@ -1302,7 +1298,6 @@ acquire_sample_rows(Relation onerel, int elevel,
*/
stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
READ_STREAM_USE_BATCHING,
- vac_strategy,
scan->rs_rd,
MAIN_FORKNUM,
block_sampling_read_stream_next,
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.cindex f0819d15ab7..6f65d48b6bd 100644--- a/src/backend/commands/dbcommands.c+++ b/src/backend/commands/dbcommands.c@@ -262,7 +262,6 @@ ScanSourceDatabasePgClass(Oid tbid, Oid dbid, char *srcpath)
LockRelId relid;
Snapshot snapshot;
SMgrRelation smgr;
- BufferAccessStrategy bstrategy;
/* Get pg_class relfilenumber. */
relfilenumber = RelationMapOidToFilenumberForDatabase(srcpath,
@@ -282,9 +281,6 @@ ScanSourceDatabasePgClass(Oid tbid, Oid dbid, char *srcpath)
nblocks = smgrnblocks(smgr, MAIN_FORKNUM);
smgrclose(smgr);
- /* Use a buffer access strategy since this is a bulk read operation. */- bstrategy = GetAccessStrategy(BAS_BULKREAD);-
/*
* As explained in the function header comments, we need a snapshot that
* will see all committed transactions as committed, and our transaction
@@ -299,7 +295,7 @@ ScanSourceDatabasePgClass(Oid tbid, Oid dbid, char *srcpath)
CHECK_FOR_INTERRUPTS();
buf = ReadBufferWithoutRelcache(rlocator, MAIN_FORKNUM, blkno,
- RBM_NORMAL, bstrategy, true);+ RBM_NORMAL, true);
LockBuffer(buf, BUFFER_LOCK_SHARE);
page = BufferGetPage(buf);
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.cindex 02883fe34a4..d8c15a8b1bc 100644--- a/src/backend/commands/repack.c+++ b/src/backend/commands/repack.c@@ -2447,7 +2447,7 @@ process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel,
if (params->options & CLUOPT_VERBOSE)
vac_params.options |= VACOPT_VERBOSE;
analyze_rel(tableOid, NULL, &vac_params,
- stmt->relation->va_cols, true, NULL);+ stmt->relation->va_cols, true);
PopActiveSnapshot();
CommandCounterIncrement();
}
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.cindex 551667650ba..ac1ab948211 100644--- a/src/backend/commands/sequence.c+++ b/src/backend/commands/sequence.c@@ -358,7 +358,7 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum)
/* Initialize first page of relation with special magic number */
- buf = ExtendBufferedRel(BMR_REL(rel), forkNum, NULL,+ buf = ExtendBufferedRel(BMR_REL(rel), forkNum,
EB_LOCK_FIRST | EB_SKIP_EXTENSION_LOCK);
Assert(BufferGetBlockNumber(buf) == 0);
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.cindex 38539a6fd3d..66acd27bd48 100644--- a/src/backend/commands/vacuum.c+++ b/src/backend/commands/vacuum.c@@ -127,32 +127,11 @@ static void vac_truncate_clog(TransactionId frozenXID,
TransactionId lastSaneFrozenXid,
MultiXactId lastSaneMinMulti);
static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params,
- BufferAccessStrategy bstrategy, bool isTopLevel);+ bool isTopLevel);
static double compute_parallel_delay(void);
static VacOptValue get_vacoptval_from_boolean(DefElem *def);
static bool vac_tid_reaped(ItemPointer itemptr, void *state);
-/*- * GUC check function to ensure GUC value specified is within the allowable- * range.- */-bool-check_vacuum_buffer_usage_limit(int *newval, void **extra,- GucSource source)-{- /* Value upper and lower hard limits are inclusive */- if (*newval == 0 || (*newval >= MIN_BAS_VAC_RING_SIZE_KB &&- *newval <= MAX_BAS_VAC_RING_SIZE_KB))- return true;-- /* Value does not fall within any allowable range */- GUC_check_errdetail("\"%s\" must be 0 or between %d kB and %d kB.",- "vacuum_buffer_usage_limit",- MIN_BAS_VAC_RING_SIZE_KB, MAX_BAS_VAC_RING_SIZE_KB);-- return false;-}-
/*
* Primary entry point for manual VACUUM and ANALYZE commands
*
@@ -163,7 +142,6 @@ void
ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
{
VacuumParams params;
- BufferAccessStrategy bstrategy = NULL;
bool verbose = false;
bool skip_locked = false;
bool analyze = false;
@@ -172,7 +150,6 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
bool disable_page_skipping = false;
bool process_main = true;
bool process_toast = true;
- int ring_size;
bool skip_database_stats = false;
bool only_database_stats = false;
MemoryContext vac_context;
@@ -188,12 +165,6 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
/* Will be set later if we recurse to a TOAST table. */
params.toast_parent = InvalidOid;
- /*- * Set this to an invalid value so it is clear whether or not a- * BUFFER_USAGE_LIMIT was specified when making the access strategy.- */- ring_size = -1;-
/* Parse options list */
foreach(lc, vacstmt->options)
{
@@ -204,32 +175,6 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
verbose = defGetBoolean(opt);
else if (strcmp(opt->defname, "skip_locked") == 0)
skip_locked = defGetBoolean(opt);
- else if (strcmp(opt->defname, "buffer_usage_limit") == 0)- {- const char *hintmsg;- int result;- char *vac_buffer_size;-- vac_buffer_size = defGetString(opt);-- /*- * Check that the specified value is valid and the size falls- * within the hard upper and lower limits if it is not 0.- */- if (!parse_int(vac_buffer_size, &result, GUC_UNIT_KB, &hintmsg) ||- (result != 0 &&- (result < MIN_BAS_VAC_RING_SIZE_KB || result > MAX_BAS_VAC_RING_SIZE_KB)))- {- ereport(ERROR,- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),- errmsg("%s option must be 0 or between %d kB and %d kB",- "BUFFER_USAGE_LIMIT",- MIN_BAS_VAC_RING_SIZE_KB, MAX_BAS_VAC_RING_SIZE_KB),- hintmsg ? errhint_internal("%s", _(hintmsg)) : 0));- }-- ring_size = result;- }
else if (!vacstmt->is_vacuumcmd)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -325,17 +270,6 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("VACUUM FULL cannot be performed in parallel")));
- /*- * BUFFER_USAGE_LIMIT does nothing for VACUUM (FULL) so just raise an- * ERROR for that case. VACUUM (FULL, ANALYZE) does make use of it, so- * we'll permit that.- */- if (ring_size != -1 && (params.options & VACOPT_FULL) &&- !(params.options & VACOPT_ANALYZE))- ereport(ERROR,- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),- errmsg("BUFFER_USAGE_LIMIT cannot be specified for VACUUM FULL")));-
/*
* Make sure VACOPT_ANALYZE is specified if any column lists are present.
*/
@@ -431,38 +365,8 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
"Vacuum",
ALLOCSET_DEFAULT_SIZES);
- /*- * Make a buffer strategy object in the cross-transaction memory context.- * We needn't bother making this for VACUUM (FULL) or VACUUM- * (ONLY_DATABASE_STATS) as they'll not make use of it. VACUUM (FULL,- * ANALYZE) is possible, so we'd better ensure that we make a strategy- * when we see ANALYZE.- */- if ((params.options & (VACOPT_ONLY_DATABASE_STATS |- VACOPT_FULL)) == 0 ||- (params.options & VACOPT_ANALYZE) != 0)- {-- MemoryContext old_context = MemoryContextSwitchTo(vac_context);-- Assert(ring_size >= -1);-- /*- * If BUFFER_USAGE_LIMIT was specified by the VACUUM or ANALYZE- * command, it overrides the value of VacuumBufferUsageLimit. Either- * value may be 0, in which case GetAccessStrategyWithSize() will- * return NULL, effectively allowing full use of shared buffers.- */- if (ring_size == -1)- ring_size = VacuumBufferUsageLimit;-- bstrategy = GetAccessStrategyWithSize(BAS_VACUUM, ring_size);-- MemoryContextSwitchTo(old_context);- }-
/* Now go through the common routine */
- vacuum(vacstmt->rels, ¶ms, bstrategy, vac_context, isTopLevel);+ vacuum(vacstmt->rels, ¶ms, vac_context, isTopLevel);
/* Finally, clean up the vacuum memory context */
MemoryContextDelete(vac_context);
@@ -479,19 +383,13 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
* params contains a set of parameters that can be used to customize the
* behavior.
*
- * bstrategy may be passed in as NULL when the caller does not want to- * restrict the number of shared_buffers that VACUUM / ANALYZE can use,- * otherwise, the caller must build a BufferAccessStrategy with the number of- * shared_buffers that VACUUM / ANALYZE should try to limit themselves to- * using.- *
* isTopLevel should be passed down from ProcessUtility.
*
* It is the caller's responsibility that all parameters are allocated in a
* memory context that will not disappear at transaction commit.
*/
void
-vacuum(List *relations, const VacuumParams *params, BufferAccessStrategy bstrategy,+vacuum(List *relations, const VacuumParams *params,
MemoryContext vac_context, bool isTopLevel)
{
static bool in_vacuum = false;
@@ -630,7 +528,7 @@ vacuum(List *relations, const VacuumParams *params, BufferAccessStrategy bstrate
if (params->options & VACOPT_VACUUM)
{
- if (!vacuum_rel(vrel->oid, vrel->relation, *params, bstrategy,+ if (!vacuum_rel(vrel->oid, vrel->relation, *params,
isTopLevel))
continue;
}
@@ -649,7 +547,7 @@ vacuum(List *relations, const VacuumParams *params, BufferAccessStrategy bstrate
}
analyze_rel(vrel->oid, vrel->relation, params,
- vrel->va_cols, in_outer_xact, bstrategy);+ vrel->va_cols, in_outer_xact);
if (use_own_xacts)
{
@@ -2010,7 +1908,7 @@ vac_truncate_clog(TransactionId frozenXID,
*/
static bool
vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params,
- BufferAccessStrategy bstrategy, bool isTopLevel)+ bool isTopLevel)
{
LOCKMODE lmode;
Relation rel;
@@ -2307,7 +2205,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params,
rel = NULL;
}
else
- table_relation_vacuum(rel, ¶ms, bstrategy);+ table_relation_vacuum(rel, ¶ms);
}
/* Roll back any GUC changes executed by index functions */
@@ -2344,7 +2242,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params,
toast_vacuum_params.options |= VACOPT_PROCESS_MAIN;
toast_vacuum_params.toast_parent = relid;
- vacuum_rel(toast_relid, NULL, toast_vacuum_params, bstrategy,+ vacuum_rel(toast_relid, NULL, toast_vacuum_params,
isTopLevel);
}
diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.cindex 41cefcfde54..8b4b835db9b 100644--- a/src/backend/commands/vacuumparallel.c+++ b/src/backend/commands/vacuumparallel.c@@ -125,12 +125,6 @@ typedef struct PVShared
*/
int maintenance_work_mem_worker;
- /*- * The number of buffers each worker's Buffer Access Strategy ring should- * contain.- */- int ring_nbuffers;-
/*
* Shared vacuum cost balance. During parallel vacuum,
* VacuumSharedCostBalance points to this value and it accumulates the
@@ -257,9 +251,6 @@ struct ParallelVacuumState
int nindexes_parallel_cleanup;
int nindexes_parallel_condcleanup;
- /* Buffer access strategy used by leader process */- BufferAccessStrategy bstrategy;-
/*
* Error reporting state. The error callback is set only for workers
* processes during parallel index vacuum.
@@ -304,7 +295,7 @@ static void parallel_vacuum_dsm_detach(dsm_segment *seg, Datum arg);
ParallelVacuumState *
parallel_vacuum_init(Relation rel, Relation *indrels, int nindexes,
int nrequested_workers, int vac_work_mem,
- int elevel, BufferAccessStrategy bstrategy)+ int elevel)
{
ParallelVacuumState *pvs;
ParallelContext *pcxt;
@@ -345,7 +336,6 @@ parallel_vacuum_init(Relation rel, Relation *indrels, int nindexes,
pvs->indrels = indrels;
pvs->nindexes = nindexes;
pvs->will_parallel_vacuum = will_parallel_vacuum;
- pvs->bstrategy = bstrategy;
pvs->heaprel = rel;
EnterParallelMode();
@@ -447,9 +437,6 @@ parallel_vacuum_init(Relation rel, Relation *indrels, int nindexes,
shared->dead_items_handle = TidStoreGetHandle(dead_items);
shared->dead_items_dsa_handle = dsa_get_handle(TidStoreGetDSA(dead_items));
- /* Use the same buffer size for all workers */- shared->ring_nbuffers = GetAccessStrategyBufferCount(bstrategy);-
pg_atomic_init_u32(&(shared->cost_balance), 0);
pg_atomic_init_u32(&(shared->active_nworkers), 0);
pg_atomic_init_u32(&(shared->idx), 0);
@@ -1091,7 +1078,6 @@ parallel_vacuum_process_one_index(ParallelVacuumState *pvs, Relation indrel,
ivinfo.message_level = DEBUG2;
ivinfo.estimated_count = pvs->shared->estimated_count;
ivinfo.num_heap_tuples = pvs->shared->reltuples;
- ivinfo.strategy = pvs->bstrategy;
/* Update error traceback information */
pvs->indname = pstrdup(RelationGetRelationName(indrel));
@@ -1294,10 +1280,6 @@ parallel_vacuum_main(dsm_segment *seg, shm_toc *toc)
pvs.indname = NULL;
pvs.status = PARALLEL_INDVAC_STATUS_INITIAL;
- /* Each parallel VACUUM worker gets its own access strategy. */- pvs.bstrategy = GetAccessStrategyWithSize(BAS_VACUUM,- shared->ring_nbuffers * (BLCKSZ / 1024));-
/* Setup error traceback support for ereport() */
errcallback.callback = parallel_vacuum_error_callback;
errcallback.arg = &pvs;
@@ -1328,7 +1310,6 @@ parallel_vacuum_main(dsm_segment *seg, shm_toc *toc)
vac_close_indexes(nindexes, indrels, RowExclusiveLock);
table_close(rel, ShareUpdateExclusiveLock);
- FreeAccessStrategy(pvs.bstrategy);
if (shared->is_autovacuum)
pv_shared_cost_params = NULL;
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.cindex 45abf48768a..e021e798805 100644--- a/src/backend/postmaster/autovacuum.c+++ b/src/backend/postmaster/autovacuum.c@@ -386,8 +386,7 @@ static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
bool *dovacuum, bool *doanalyze, bool *wraparound,
AutoVacuumScores *scores);
-static void autovacuum_do_vac_analyze(autovac_table *tab,- BufferAccessStrategy bstrategy);+static void autovacuum_do_vac_analyze(autovac_table *tab);
static AutoVacOpts *extract_autovac_opts(HeapTuple tup,
TupleDesc pg_class_desc);
static void perform_work_item(AutoVacuumWorkItem *workitem);
@@ -1936,7 +1935,6 @@ do_autovacuum(void)
HASHCTL ctl;
HTAB *table_toast_map;
ListCell *volatile cell;
- BufferAccessStrategy bstrategy;
ScanKeyData key;
TupleDesc pg_class_desc;
int effective_multixact_freeze_max_age;
@@ -2322,23 +2320,6 @@ do_autovacuum(void)
autovacuum_analyze_score_weight != 0.0)
list_sort(tables_to_process, TableToProcessComparator);
- /*- * Optionally, create a buffer access strategy object for VACUUM to use.- * We use the same BufferAccessStrategy object for all tables VACUUMed by- * this worker to prevent autovacuum from blowing out shared buffers.- *- * VacuumBufferUsageLimit being set to 0 results in- * GetAccessStrategyWithSize returning NULL, effectively meaning we can- * use up to all of shared buffers.- *- * If we later enter failsafe mode on any of the tables being vacuumed, we- * will cease use of the BufferAccessStrategy only for that table.- *- * XXX should we consider adding code to adjust the size of this if- * VacuumBufferUsageLimit changes?- */- bstrategy = GetAccessStrategyWithSize(BAS_VACUUM, VacuumBufferUsageLimit);-
/*
* create a memory context to act as fake PortalContext, so that the
* contexts created in the vacuum code are cleaned up for each table.
@@ -2516,7 +2497,7 @@ do_autovacuum(void)
MemoryContextSwitchTo(PortalContext);
/* have at it */
- autovacuum_do_vac_analyze(tab, bstrategy);+ autovacuum_do_vac_analyze(tab);
/*
* Clear a possible query-cancel signal, to avoid a late reaction
@@ -2636,8 +2617,6 @@ deleted:
#ifdef USE_VALGRIND
hash_destroy(table_toast_map);
FreeTupleDesc(pg_class_desc);
- if (bstrategy)- pfree(bstrategy);
#endif
/* Run the rest in xact context, mainly to avoid Valgrind leak warnings */
@@ -3348,7 +3327,7 @@ relation_needs_vacanalyze(Oid relid,
* disappear at transaction commit.
*/
static void
-autovacuum_do_vac_analyze(autovac_table *tab, BufferAccessStrategy bstrategy)+autovacuum_do_vac_analyze(autovac_table *tab)
{
RangeVar *rangevar;
VacuumRelation *rel;
@@ -3371,7 +3350,7 @@ autovacuum_do_vac_analyze(autovac_table *tab, BufferAccessStrategy bstrategy)
rel_list = list_make1(rel);
MemoryContextSwitchTo(old_context);
- vacuum(rel_list, &tab->at_params, bstrategy, vac_context, true);+ vacuum(rel_list, &tab->at_params, vac_context, true);
MemoryContextDelete(vac_context);
}
diff --git a/src/backend/postmaster/datachecksum_state.c b/src/backend/postmaster/datachecksum_state.cindex 73dc539836b..b6612f1e517 100644--- a/src/backend/postmaster/datachecksum_state.c+++ b/src/backend/postmaster/datachecksum_state.c@@ -390,7 +390,7 @@ static List *BuildRelationList(bool temp_relations, bool include_shared);
static void FreeDatabaseList(List *dblist);
static DataChecksumsWorkerResult ProcessDatabase(DataChecksumsWorkerDatabase *db);
static bool ProcessAllDatabases(void);
-static bool ProcessSingleRelationFork(Relation reln, ForkNumber forkNum, BufferAccessStrategy strategy);+static bool ProcessSingleRelationFork(Relation reln, ForkNumber forkNum);
static void ResetDataChecksumsProgressCounters(void);
static void launcher_cancel_handler(SIGNAL_ARGS);
static void WaitForAllTransactionsToFinish(void);
@@ -687,7 +687,7 @@ StartDataChecksumsWorkerLauncher(DataChecksumsWorkerOperation op,
* error is raised in the lower levels.
*/
static bool
-ProcessSingleRelationFork(Relation reln, ForkNumber forkNum, BufferAccessStrategy strategy)+ProcessSingleRelationFork(Relation reln, ForkNumber forkNum)
{
BlockNumber numblocks = RelationGetNumberOfBlocksInFork(reln, forkNum);
char activity[NAMEDATALEN * 2 + 128];
@@ -722,7 +722,7 @@ ProcessSingleRelationFork(Relation reln, ForkNumber forkNum, BufferAccessStrateg
*/
for (BlockNumber blknum = 0; blknum < numblocks; blknum++)
{
- Buffer buf = ReadBufferExtended(reln, forkNum, blknum, RBM_NORMAL, strategy);+ Buffer buf = ReadBufferExtended(reln, forkNum, blknum, RBM_NORMAL);
/* Need to get an exclusive lock to mark the buffer as dirty */
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
@@ -808,7 +808,7 @@ ResetDataChecksumsProgressCounters(void)
* error is raised in the lower levels.
*/
static bool
-ProcessSingleRelationByOid(Oid relationId, BufferAccessStrategy strategy)+ProcessSingleRelationByOid(Oid relationId)
{
Relation rel;
bool aborted = false;
@@ -835,7 +835,7 @@ ProcessSingleRelationByOid(Oid relationId, BufferAccessStrategy strategy)
{
if (smgrexists(rel->rd_smgr, fnum))
{
- if (!ProcessSingleRelationFork(rel, fnum, strategy))+ if (!ProcessSingleRelationFork(rel, fnum))
{
aborted = true;
break;
@@ -1582,7 +1582,6 @@ DataChecksumsWorkerMain(Datum arg)
Oid dboid;
List *RelationList = NIL;
List *InitialTempTableList = NIL;
- BufferAccessStrategy strategy;
bool aborted = false;
int64 rels_done;
bool process_shared;
@@ -1650,11 +1649,6 @@ DataChecksumsWorkerMain(Datum arg)
VacuumUpdateCosts();
VacuumCostBalance = 0;
- /*- * Create and set the vacuum strategy as our buffer strategy.- */- strategy = GetAccessStrategy(BAS_VACUUM);-
RelationList = BuildRelationList(false, process_shared);
/* Update the total number of relations to be processed in this DB. */
@@ -1676,9 +1670,7 @@ DataChecksumsWorkerMain(Datum arg)
rels_done = 0;
foreach_oid(reloid, RelationList)
{
- bool costs_updated = false;-- if (!ProcessSingleRelationByOid(reloid, strategy))+ if (!ProcessSingleRelationByOid(reloid))
{
aborted = true;
break;
@@ -1694,8 +1686,7 @@ DataChecksumsWorkerMain(Datum arg)
/*
* Check if the cost settings changed during runtime and if so, update
- * to reflect the new values and signal that the access strategy needs- * to be refreshed.+ * to reflect the new values.
*/
LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
if (DataChecksumState->worker_invocation != worker_invocation)
@@ -1706,7 +1697,6 @@ DataChecksumsWorkerMain(Datum arg)
if ((DataChecksumState->launch_cost_delay != DataChecksumState->cost_delay)
|| (DataChecksumState->launch_cost_limit != DataChecksumState->cost_limit))
{
- costs_updated = true;
VacuumCostDelay = DataChecksumState->launch_cost_delay;
VacuumCostLimit = DataChecksumState->launch_cost_limit;
VacuumUpdateCosts();
@@ -1714,19 +1704,10 @@ DataChecksumsWorkerMain(Datum arg)
DataChecksumState->cost_delay = DataChecksumState->launch_cost_delay;
DataChecksumState->cost_limit = DataChecksumState->launch_cost_limit;
}
- else- costs_updated = false;
LWLockRelease(DataChecksumsWorkerLock);
-- if (costs_updated)- {- FreeAccessStrategy(strategy);- strategy = GetAccessStrategy(BAS_VACUUM);- }
}
list_free(RelationList);
- FreeAccessStrategy(strategy);
if (aborted || abort_requested)
{
diff --git a/src/backend/storage/aio/read_stream.c b/src/backend/storage/aio/read_stream.cindex a318539e56c..08d344f063d 100644--- a/src/backend/storage/aio/read_stream.c+++ b/src/backend/storage/aio/read_stream.c@@ -757,7 +757,6 @@ read_stream_look_ahead(ReadStream *stream)
*/
static ReadStream *
read_stream_begin_impl(int flags,
- BufferAccessStrategy strategy,
Relation rel,
SMgrRelation smgr,
char persistence,
@@ -771,7 +770,6 @@ read_stream_begin_impl(int flags,
int16 queue_size;
int16 queue_overflow;
int max_ios;
- int strategy_pin_limit;
uint32 max_pinned_buffers;
uint32 max_possible_buffer_limit;
Oid tablespace_id;
@@ -835,10 +833,6 @@ read_stream_begin_impl(int flags,
max_pinned_buffers = Min(max_pinned_buffers,
PG_INT16_MAX - queue_overflow - 1);
- /* Give the strategy a chance to limit the number of buffers we pin. */- strategy_pin_limit = GetAccessStrategyPinLimit(strategy);- max_pinned_buffers = Min(strategy_pin_limit, max_pinned_buffers);-
/*
* Also limit our queue to the maximum number of pins we could ever be
* allowed to acquire according to the buffer manager. We may not really
@@ -962,7 +956,6 @@ read_stream_begin_impl(int flags,
stream->ios[i].op.smgr = smgr;
stream->ios[i].op.persistence = persistence;
stream->ios[i].op.forknum = forknum;
- stream->ios[i].op.strategy = strategy;
}
return stream;
@@ -974,7 +967,6 @@ read_stream_begin_impl(int flags,
*/
ReadStream *
read_stream_begin_relation(int flags,
- BufferAccessStrategy strategy,
Relation rel,
ForkNumber forknum,
ReadStreamBlockNumberCB callback,
@@ -982,7 +974,6 @@ read_stream_begin_relation(int flags,
size_t per_buffer_data_size)
{
return read_stream_begin_impl(flags,
- strategy,
rel,
RelationGetSmgr(rel),
rel->rd_rel->relpersistence,
@@ -998,7 +989,6 @@ read_stream_begin_relation(int flags,
*/
ReadStream *
read_stream_begin_smgr_relation(int flags,
- BufferAccessStrategy strategy,
SMgrRelation smgr,
char smgr_persistence,
ForkNumber forknum,
@@ -1007,7 +997,6 @@ read_stream_begin_smgr_relation(int flags,
size_t per_buffer_data_size)
{
return read_stream_begin_impl(flags,
- strategy,
NULL,
smgr,
smgr_persistence,
@@ -1370,13 +1359,11 @@ read_stream_next_buffer(ReadStream *stream, void **per_buffer_data)
* Transitional support for code that would like to perform or skip reads
* itself, without using the stream. Returns, and consumes, the next block
* number that would be read by the stream's look-ahead algorithm, or
- * InvalidBlockNumber if the end of the stream is reached. Also reports the- * strategy that would be used to read it.+ * InvalidBlockNumber if the end of the stream is reached.
*/
BlockNumber
-read_stream_next_block(ReadStream *stream, BufferAccessStrategy *strategy)+read_stream_next_block(ReadStream *stream)
{
- *strategy = stream->ios[0].op.strategy;
return read_stream_get_block(stream, NULL);
}
diff --git a/src/backend/storage/buffer/README b/src/backend/storage/buffer/READMEindex b332e002ba1..58df6bd011a 100644--- a/src/backend/storage/buffer/README+++ b/src/backend/storage/buffer/README@@ -203,50 +203,6 @@ have to give up and try another buffer. This however is not a concern
of the basic select-a-victim-buffer algorithm.)
-Buffer Ring Replacement Strategy------------------------------------When running a query that needs to access a large number of pages just once,-such as VACUUM or a large sequential scan, a different strategy is used.-A page that has been touched only by such a scan is unlikely to be needed-again soon, so instead of running the normal clock-sweep algorithm and-blowing out the entire buffer cache, a small ring of buffers is allocated-using the normal clock-sweep algorithm and those buffers are reused for the-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.--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-from the ring. Instead, the WAL is flushed if needed to allow reuse of the-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).-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,-so we let it use up a bit more of the buffer arena.--
Background Writer's Processing
------------------------------
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.cindex 1f5a00849e6..cc2fab959df 100644--- a/src/backend/storage/buffer/bufmgr.c+++ b/src/backend/storage/buffer/bufmgr.c@@ -612,10 +612,9 @@ ForgetPrivateRefCountEntry(PrivateRefCountEntry *ref)
static Buffer ReadBuffer_common(Relation rel,
SMgrRelation smgr, char smgr_persistence,
ForkNumber forkNum, BlockNumber blockNum,
- ReadBufferMode mode, BufferAccessStrategy strategy);+ ReadBufferMode mode);
static BlockNumber ExtendBufferedRelCommon(BufferManagerRelation bmr,
ForkNumber fork,
- BufferAccessStrategy strategy,
uint32 flags,
uint32 extend_by,
BlockNumber extend_upto,
@@ -623,13 +622,12 @@ static BlockNumber ExtendBufferedRelCommon(BufferManagerRelation bmr,
uint32 *extended_by);
static BlockNumber ExtendBufferedRelShared(BufferManagerRelation bmr,
ForkNumber fork,
- BufferAccessStrategy strategy,
uint32 flags,
uint32 extend_by,
BlockNumber extend_upto,
Buffer *buffers,
uint32 *extended_by);
-static bool PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy,+static bool PinBuffer(BufferDesc *buf,
bool skip_if_not_valid);
static void PinBuffer_Locked(BufferDesc *buf);
static void UnpinBuffer(BufferDesc *buf);
@@ -645,7 +643,6 @@ static inline BufferDesc *BufferAlloc(SMgrRelation smgr,
char relpersistence,
ForkNumber forkNum,
BlockNumber blockNum,
- BufferAccessStrategy strategy,
bool *foundPtr, IOContext io_context);
static bool AsyncReadBuffers(ReadBuffersOperation *operation, int *nblocks_progress);
static void CheckReadBuffersOperation(ReadBuffersOperation *operation, bool is_complete);
@@ -654,7 +651,7 @@ static pg_always_inline void TrackBufferHit(IOObject io_object,
IOContext io_context,
Relation rel, char persistence, SMgrRelation smgr,
ForkNumber forknum, BlockNumber blocknum);
-static Buffer GetVictimBuffer(BufferAccessStrategy strategy, IOContext io_context);+static Buffer GetVictimBuffer(IOContext io_context);
static void FlushUnlockedBuffer(BufferDesc *buf, SMgrRelation reln,
IOObject io_object, IOContext io_context);
static void FlushBuffer(BufferDesc *buf, SMgrRelation reln,
@@ -858,7 +855,7 @@ ReadRecentBuffer(RelFileLocator rlocator, ForkNumber forkNum, BlockNumber blockN
* pin.
*/
if (BufferTagsEqual(&tag, &bufHdr->tag) &&
- PinBuffer(bufHdr, NULL, true))+ PinBuffer(bufHdr, true))
{
if (BufferTagsEqual(&tag, &bufHdr->tag))
{
@@ -874,12 +871,12 @@ ReadRecentBuffer(RelFileLocator rlocator, ForkNumber forkNum, BlockNumber blockN
/*
* ReadBuffer -- a shorthand for ReadBufferExtended, for reading from main
- * fork with RBM_NORMAL mode and default strategy.+ * fork with RBM_NORMAL mode.
*/
Buffer
ReadBuffer(Relation reln, BlockNumber blockNum)
{
- return ReadBufferExtended(reln, MAIN_FORKNUM, blockNum, RBM_NORMAL, NULL);+ return ReadBufferExtended(reln, MAIN_FORKNUM, blockNum, RBM_NORMAL);
}
/*
@@ -919,13 +916,10 @@ ReadBuffer(Relation reln, BlockNumber blockNum)
* a cleanup-strength lock on the page.
*
* RBM_NORMAL_NO_LOG mode is treated the same as RBM_NORMAL here.
- *- * If strategy is not NULL, a nondefault buffer access strategy is used.- * See buffer/README for details.
*/
inline Buffer
ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
- ReadBufferMode mode, BufferAccessStrategy strategy)+ ReadBufferMode mode)
{
Buffer buf;
@@ -935,7 +929,7 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
* ReadBuffer_common().
*/
buf = ReadBuffer_common(reln, RelationGetSmgr(reln), 0,
- forkNum, blockNum, mode, strategy);+ forkNum, blockNum, mode);
return buf;
}
@@ -954,14 +948,14 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
Buffer
ReadBufferWithoutRelcache(RelFileLocator rlocator, ForkNumber forkNum,
BlockNumber blockNum, ReadBufferMode mode,
- BufferAccessStrategy strategy, bool permanent)+ bool permanent)
{
SMgrRelation smgr = smgropen(rlocator, INVALID_PROC_NUMBER);
return ReadBuffer_common(NULL, smgr,
permanent ? RELPERSISTENCE_PERMANENT : RELPERSISTENCE_UNLOGGED,
forkNum, blockNum,
- mode, strategy);+ mode);
}
/*
@@ -970,13 +964,12 @@ ReadBufferWithoutRelcache(RelFileLocator rlocator, ForkNumber forkNum,
Buffer
ExtendBufferedRel(BufferManagerRelation bmr,
ForkNumber forkNum,
- BufferAccessStrategy strategy,
uint32 flags)
{
Buffer buf;
uint32 extend_by = 1;
- ExtendBufferedRelBy(bmr, forkNum, strategy, flags, extend_by,+ ExtendBufferedRelBy(bmr, forkNum, flags, extend_by,
&buf, &extend_by);
return buf;
@@ -1002,7 +995,6 @@ ExtendBufferedRel(BufferManagerRelation bmr,
BlockNumber
ExtendBufferedRelBy(BufferManagerRelation bmr,
ForkNumber fork,
- BufferAccessStrategy strategy,
uint32 flags,
uint32 extend_by,
Buffer *buffers,
@@ -1015,7 +1007,7 @@ ExtendBufferedRelBy(BufferManagerRelation bmr,
if (bmr.relpersistence == '\0')
bmr.relpersistence = bmr.rel->rd_rel->relpersistence;
- return ExtendBufferedRelCommon(bmr, fork, strategy, flags,+ return ExtendBufferedRelCommon(bmr, fork, flags,
extend_by, InvalidBlockNumber,
buffers, extended_by);
}
@@ -1031,7 +1023,6 @@ ExtendBufferedRelBy(BufferManagerRelation bmr,
Buffer
ExtendBufferedRelTo(BufferManagerRelation bmr,
ForkNumber fork,
- BufferAccessStrategy strategy,
uint32 flags,
BlockNumber extend_to,
ReadBufferMode mode)
@@ -1097,7 +1088,7 @@ ExtendBufferedRelTo(BufferManagerRelation bmr,
if ((uint64) current_size + num_pages > extend_to)
num_pages = extend_to - current_size;
- first_block = ExtendBufferedRelCommon(bmr, fork, strategy, flags,+ first_block = ExtendBufferedRelCommon(bmr, fork, flags,
num_pages, extend_to,
buffers, &extended_by);
@@ -1123,7 +1114,7 @@ ExtendBufferedRelTo(BufferManagerRelation bmr,
{
Assert(extended_by == 0);
buffer = ReadBuffer_common(bmr.rel, BMR_GET_SMGR(bmr), bmr.relpersistence,
- fork, extend_to - 1, mode, strategy);+ fork, extend_to - 1, mode);
}
return buffer;
@@ -1226,7 +1217,6 @@ PinBufferForBlock(Relation rel,
char persistence,
ForkNumber forkNum,
BlockNumber blockNum,
- BufferAccessStrategy strategy,
IOObject io_object,
IOContext io_context,
bool *foundPtr)
@@ -1250,7 +1240,7 @@ PinBufferForBlock(Relation rel,
bufHdr = LocalBufferAlloc(smgr, forkNum, blockNum, foundPtr);
else
bufHdr = BufferAlloc(smgr, persistence, forkNum, blockNum,
- strategy, foundPtr, io_context);+ foundPtr, io_context);
if (*foundPtr)
TrackBufferHit(io_object, io_context, rel, persistence, smgr, forkNum, blockNum);
@@ -1276,8 +1266,7 @@ PinBufferForBlock(Relation rel,
static pg_always_inline Buffer
ReadBuffer_common(Relation rel, SMgrRelation smgr, char smgr_persistence,
ForkNumber forkNum,
- BlockNumber blockNum, ReadBufferMode mode,- BufferAccessStrategy strategy)+ BlockNumber blockNum, ReadBufferMode mode)
{
ReadBuffersOperation operation;
Buffer buffer;
@@ -1313,7 +1302,7 @@ ReadBuffer_common(Relation rel, SMgrRelation smgr, char smgr_persistence,
if (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK)
flags |= EB_LOCK_FIRST;
- return ExtendBufferedRel(BMR_REL(rel), forkNum, strategy, flags);+ return ExtendBufferedRel(BMR_REL(rel), forkNum, flags);
}
if (rel)
@@ -1335,12 +1324,12 @@ ReadBuffer_common(Relation rel, SMgrRelation smgr, char smgr_persistence,
}
else
{
- io_context = IOContextForStrategy(strategy);+ io_context = IOCONTEXT_NORMAL;
io_object = IOOBJECT_RELATION;
}
buffer = PinBufferForBlock(rel, smgr, persistence,
- forkNum, blockNum, strategy,+ forkNum, blockNum,
io_object, io_context, &found);
ZeroAndLockBuffer(buffer, mode, found);
return buffer;
@@ -1358,7 +1347,6 @@ ReadBuffer_common(Relation rel, SMgrRelation smgr, char smgr_persistence,
operation.rel = rel;
operation.persistence = persistence;
operation.forknum = forkNum;
- operation.strategy = strategy;
if (StartReadBuffer(&operation,
&buffer,
blockNum,
@@ -1399,7 +1387,7 @@ StartReadBuffersImpl(ReadBuffersOperation *operation,
}
else
{
- io_context = IOContextForStrategy(operation->strategy);+ io_context = IOCONTEXT_NORMAL;
io_object = IOOBJECT_RELATION;
}
@@ -1450,7 +1438,6 @@ StartReadBuffersImpl(ReadBuffersOperation *operation,
operation->persistence,
operation->forknum,
blockNum + i,
- operation->strategy,
io_object, io_context,
&found);
}
@@ -1771,7 +1758,7 @@ WaitReadBuffers(ReadBuffersOperation *operation)
}
else
{
- io_context = IOContextForStrategy(operation->strategy);+ io_context = IOCONTEXT_NORMAL;
io_object = IOOBJECT_RELATION;
}
@@ -1961,7 +1948,7 @@ AsyncReadBuffers(ReadBuffersOperation *operation, int *nblocks_progress)
}
else
{
- io_context = IOContextForStrategy(operation->strategy);+ io_context = IOCONTEXT_NORMAL;
io_object = IOOBJECT_RELATION;
}
@@ -2180,24 +2167,18 @@ AsyncReadBuffers(ReadBuffersOperation *operation, int *nblocks_progress)
* buffer. If no buffer exists already, selects a replacement victim and
* evicts the old page, but does NOT read in new page.
*
- * "strategy" can be a buffer replacement strategy object, or NULL for- * the default strategy. The selected buffer's usage_count is advanced when- * using the default strategy, but otherwise possibly not (see PinBuffer).- *
* The returned buffer is pinned and is already marked as holding the
* desired page. If it already did have the desired page, *foundPtr is
* set true. Otherwise, *foundPtr is set false.
*
- * io_context is passed as an output parameter to avoid calling- * IOContextForStrategy() when there is a shared buffers hit and no IO- * statistics need be captured.+ * io_context is passed as an output parameter to avoid capturing IO+ * statistics when there is a shared buffers hit and no IO occurs.
*
* No locks are held either at entry or exit.
*/
static pg_always_inline BufferDesc *
BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
BlockNumber blockNum,
- BufferAccessStrategy strategy,
bool *foundPtr, IOContext io_context)
{
BufferTag newTag; /* identity of requested block */
@@ -2235,7 +2216,7 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
*/
buf = GetBufferDescriptor(existing_buf_id);
- valid = PinBuffer(buf, strategy, false);+ valid = PinBuffer(buf, false);
/* Can release the mapping lock as soon as we've pinned it */
LWLockRelease(newPartitionLock);
@@ -2266,7 +2247,7 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
* don't hold any conflicting locks. If so we'll have to undo our work
* later.
*/
- victim_buffer = GetVictimBuffer(strategy, io_context);+ victim_buffer = GetVictimBuffer(io_context);
victim_buf_hdr = GetBufferDescriptor(victim_buffer - 1);
/*
@@ -2297,7 +2278,7 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
existing_buf_hdr = GetBufferDescriptor(existing_buf_id);
- valid = PinBuffer(existing_buf_hdr, strategy, false);+ valid = PinBuffer(existing_buf_hdr, false);
/* Can release the mapping lock as soon as we've pinned it */
LWLockRelease(newPartitionLock);
@@ -2335,9 +2316,12 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
* just like permanent relations.
*/
set_bits |= BM_TAG_VALID;
- /* Admit the newly loaded page COOL (probation); a second access via++ /*+ * Admit the newly loaded page COOL (probation); a second access via
* PinBuffer promotes it to HOT. This is what makes a one-touch scan
- * self-evicting -- see the cooling-state notes in buf_internals.h. */+ * self-evicting -- see the cooling-state notes in buf_internals.h.+ */
if (relpersistence == RELPERSISTENCE_PERMANENT || forkNum == INIT_FORKNUM)
set_bits |= BM_PERMANENT;
@@ -2549,12 +2533,11 @@ InvalidateVictimBuffer(BufferDesc *buf_hdr)
}
static Buffer
-GetVictimBuffer(BufferAccessStrategy strategy, IOContext io_context)+GetVictimBuffer(IOContext io_context)
{
BufferDesc *buf_hdr;
Buffer buf;
uint64 buf_state;
- bool from_ring;
/*
* Ensure, before we pin a victim buffer, that there's a free refcount
@@ -2570,7 +2553,7 @@ again:
* Select a victim buffer. The buffer is returned pinned and owned by
* this backend.
*/
- buf_hdr = StrategyGetBuffer(strategy, &buf_state, &from_ring);+ buf_hdr = StrategyGetBuffer(&buf_state);
buf = BufferDescriptorGetBuffer(buf_hdr);
/*
@@ -2614,26 +2597,6 @@ again:
goto again;
}
- /*- * If using a nondefault strategy, and this victim came from the- * strategy ring, let the strategy decide whether to reject it when- * reusing it would require a WAL flush. This only applies to- * permanent buffers; unlogged buffers can have fake LSNs, so- * XLogNeedsFlush() is not meaningful for them.- *- * We need to hold the content lock in at least share-exclusive mode- * to safely inspect the page LSN, so this couldn't have been done- * inside StrategyGetBuffer().- */- if (strategy && from_ring &&- buf_state & BM_PERMANENT &&- XLogNeedsFlush(BufferGetLSN(buf_hdr)) &&- StrategyRejectBuffer(strategy, buf_hdr, from_ring))- {- UnlockReleaseBuffer(buf);- goto again;- }-
/* OK, do the I/O */
FlushBuffer(buf_hdr, NULL, IOOBJECT_RELATION, io_context);
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
@@ -2646,23 +2609,15 @@ again:
if (buf_state & BM_VALID)
{
/*
- * When a BufferAccessStrategy is in use, blocks evicted from shared- * buffers are counted as IOOP_EVICT in the corresponding context- * (e.g. IOCONTEXT_BULKWRITE). Shared buffers are evicted by a- * strategy in two cases: 1) while initially claiming buffers for the- * strategy ring 2) to replace an existing strategy ring buffer- * because it is pinned or in use and cannot be reused.+ * Blocks evicted from shared buffers are counted as IOOP_EVICT.
*
- * Blocks evicted from buffers already in the strategy ring are- * counted as IOOP_REUSE in the corresponding strategy context.- *- * At this point, we can accurately count evictions and reuses,- * because we have successfully claimed the valid buffer. Previously,- * we may have been forced to release the buffer due to concurrent- * pinners or erroring out.+ * At this point, we can accurately count evictions, because we have+ * successfully claimed the valid buffer. Previously, we may have been+ * forced to release the buffer due to concurrent pinners or erroring+ * out.
*/
pgstat_count_io_op(IOOBJECT_RELATION, io_context,
- from_ring ? IOOP_REUSE : IOOP_EVICT, 1, 0);+ IOOP_EVICT, 1, 0);
}
/*
@@ -2754,7 +2709,6 @@ LimitAdditionalPins(uint32 *additional_pins)
static BlockNumber
ExtendBufferedRelCommon(BufferManagerRelation bmr,
ForkNumber fork,
- BufferAccessStrategy strategy,
uint32 flags,
uint32 extend_by,
BlockNumber extend_upto,
@@ -2789,7 +2743,7 @@ ExtendBufferedRelCommon(BufferManagerRelation bmr,
buffers, &extend_by);
}
else
- first_block = ExtendBufferedRelShared(bmr, fork, strategy, flags,+ first_block = ExtendBufferedRelShared(bmr, fork, flags,
extend_by, extend_upto,
buffers, &extend_by);
*extended_by = extend_by;
@@ -2812,7 +2766,6 @@ ExtendBufferedRelCommon(BufferManagerRelation bmr,
static BlockNumber
ExtendBufferedRelShared(BufferManagerRelation bmr,
ForkNumber fork,
- BufferAccessStrategy strategy,
uint32 flags,
uint32 extend_by,
BlockNumber extend_upto,
@@ -2820,7 +2773,7 @@ ExtendBufferedRelShared(BufferManagerRelation bmr,
uint32 *extended_by)
{
BlockNumber first_block;
- IOContext io_context = IOContextForStrategy(strategy);+ IOContext io_context = IOCONTEXT_NORMAL;
instr_time io_start;
LimitAdditionalPins(&extend_by);
@@ -2839,7 +2792,7 @@ ExtendBufferedRelShared(BufferManagerRelation bmr,
{
Block buf_block;
- buffers[i] = GetVictimBuffer(strategy, io_context);+ buffers[i] = GetVictimBuffer(io_context);
buf_block = BufHdrGetBlock(GetBufferDescriptor(buffers[i] - 1));
/* new buffers are zero-filled */
@@ -2957,7 +2910,7 @@ ExtendBufferedRelShared(BufferManagerRelation bmr,
* Pin the existing buffer before releasing the partition lock,
* preventing it from being evicted.
*/
- valid = PinBuffer(existing_hdr, strategy, false);+ valid = PinBuffer(existing_hdr, false);
LWLockRelease(partition_lock);
UnpinBuffer(victim_buf_hdr);
@@ -3007,8 +2960,11 @@ ExtendBufferedRelShared(BufferManagerRelation bmr,
victim_buf_hdr->tag = tag;
set_bits |= BM_TAG_VALID;
- /* Admit COOL (probation); see the comment at the other admission- * site and the cooling-state notes in buf_internals.h. */++ /*+ * Admit COOL (probation); see the comment at the other admission+ * site and the cooling-state notes in buf_internals.h.+ */
if (bmr.relpersistence == RELPERSISTENCE_PERMANENT || fork == INIT_FORKNUM)
set_bits |= BM_PERMANENT;
@@ -3275,13 +3231,8 @@ ReleaseAndReadBuffer(Buffer buffer,
/*
* PinBuffer -- make buffer unavailable for replacement.
*
- * For the default access strategy, the buffer's usage_count is incremented- * when we first pin it; for other strategies we just make sure the usage_count- * isn't zero. (The idea of the latter is that we don't want synchronized- * heap scans to inflate the count, but we need it to not be zero to discourage- * other backends from stealing buffers from our ring. As long as we cycle- * through the ring faster than the global clock-sweep cycles, buffers in- * our ring won't be chosen as victims for replacement by other backends.)+ * The buffer's cooling state is promoted to HOT (the 2Q rescue) when we pin+ * it; see the cooling-state notes in buf_internals.h.
*
* This should be applied only to shared buffers, never local ones.
*
@@ -3298,7 +3249,7 @@ ReleaseAndReadBuffer(Buffer buffer,
* (recently) invalid and has not been pinned.
*/
static bool
-PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy,+PinBuffer(BufferDesc *buf,
bool skip_if_not_valid)
{
Buffer b = BufferDescriptorGetBuffer(buf);
@@ -3396,7 +3347,7 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy,
* ResourceOwnerEnlarge(CurrentResourceOwner);
*
* Currently, no callers of this function want to modify the buffer's
- * usage_count at all, so there's no need for a strategy parameter.+ * cooling state at all.
* Also we don't bother with a BM_VALID test (the caller could check that for
* itself).
*
@@ -4674,22 +4625,8 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln, IOObject io_object,
false);
/*
- * When a strategy is in use, only flushes of dirty buffers already in the- * strategy ring are counted as strategy writes (IOCONTEXT- * [BULKREAD|BULKWRITE|VACUUM] IOOP_WRITE) for the purpose of IO- * statistics tracking.- *- * If a shared buffer initially added to the ring must be flushed before- * being used, this is counted as an IOCONTEXT_NORMAL IOOP_WRITE.- *- * If a shared buffer which was added to the ring later because the- * current strategy buffer is pinned or in use or because all strategy- * buffers were dirty and rejected (for BAS_BULKREAD operations only)- * requires flushing, this is counted as an IOCONTEXT_NORMAL IOOP_WRITE- * (from_ring will be false).- *- * When a strategy is not in use, the write can only be a "regular" write- * of a dirty shared buffer (IOCONTEXT_NORMAL IOOP_WRITE).+ * All writes of a dirty shared buffer are counted as an IOCONTEXT_NORMAL+ * IOOP_WRITE for the purpose of IO statistics tracking.
*/
pgstat_count_io_op_time(io_object, io_context,
IOOP_WRITE, io_start, 1, BLCKSZ);
@@ -5450,8 +5387,6 @@ RelationCopyStorageUsingBuffer(RelFileLocator srclocator,
BlockNumber nblocks;
BlockNumber blkno;
PGIOAlignedBlock buf;
- BufferAccessStrategy bstrategy_src;- BufferAccessStrategy bstrategy_dst;
BlockRangeReadStreamPrivate p;
ReadStream *src_stream;
SMgrRelation src_smgr;
@@ -5479,10 +5414,6 @@ RelationCopyStorageUsingBuffer(RelFileLocator srclocator,
smgrextend(smgropen(dstlocator, INVALID_PROC_NUMBER), forkNum, nblocks - 1,
buf.data, true);
- /* This is a bulk operation, so use buffer access strategies. */- bstrategy_src = GetAccessStrategy(BAS_BULKREAD);- bstrategy_dst = GetAccessStrategy(BAS_BULKWRITE);-
/* Initialize streaming read */
p.current_blocknum = 0;
p.last_exclusive = nblocks;
@@ -5494,7 +5425,6 @@ RelationCopyStorageUsingBuffer(RelFileLocator srclocator,
*/
src_stream = read_stream_begin_smgr_relation(READ_STREAM_FULL |
READ_STREAM_USE_BATCHING,
- bstrategy_src,
src_smgr,
permanent ? RELPERSISTENCE_PERMANENT : RELPERSISTENCE_UNLOGGED,
forkNum,
@@ -5514,7 +5444,7 @@ RelationCopyStorageUsingBuffer(RelFileLocator srclocator,
dstBuf = ReadBufferWithoutRelcache(dstlocator, forkNum,
BufferGetBlockNumber(srcBuf),
- RBM_ZERO_AND_LOCK, bstrategy_dst,+ RBM_ZERO_AND_LOCK,
permanent);
dstPage = BufferGetPage(dstBuf);
@@ -5535,9 +5465,6 @@ RelationCopyStorageUsingBuffer(RelFileLocator srclocator,
}
Assert(read_stream_next_buffer(src_stream, NULL) == InvalidBuffer);
read_stream_end(src_stream);
-- FreeAccessStrategy(bstrategy_src);- FreeAccessStrategy(bstrategy_dst);
}
/* ---------------------------------------------------------------------
diff --git a/src/backend/storage/buffer/freelist.c b/src/backend/storage/buffer/freelist.cindex e8cf13f3362..ee62b336680 100644--- a/src/backend/storage/buffer/freelist.c+++ b/src/backend/storage/buffer/freelist.c@@ -77,40 +77,6 @@ const ShmemCallbacks StrategyCtlShmemCallbacks = {
.init_fn = StrategyCtlShmemInit,
};
-/*- * Private (non-shared) state for managing a ring of shared buffers to re-use.- * This is currently the only kind of BufferAccessStrategy object, but someday- * we might have more kinds.- */-typedef struct BufferAccessStrategyData-{- /* Overall strategy type */- BufferAccessStrategyType btype;- /* Number of elements in buffers[] array */- int nbuffers;-- /*- * Index of the "current" slot in the ring, ie, the one most recently- * returned by GetBufferFromRing.- */- int current;-- /*- * Array of buffer numbers. InvalidBuffer (that is, zero) indicates we- * have not yet selected a buffer for this ring slot. For allocation- * simplicity this is palloc'd together with the fixed fields of the- * struct.- */- Buffer buffers[FLEXIBLE_ARRAY_MEMBER];-} BufferAccessStrategyData;---/* Prototypes for internal functions */-static BufferDesc *GetBufferFromRing(BufferAccessStrategy strategy,- uint64 *buf_state);-static void AddBufferToRing(BufferAccessStrategy strategy,- BufferDesc *buf);-
/*
* Per-backend state for the batched clock sweep. Each backend claims a run
* of consecutive clock-hand values with a single atomic fetch-add and then
@@ -157,8 +123,8 @@ ClockSweepTick(void)
* must hold the spinlock so StrategySyncStart() can read
* nextVictimBuffer and completePasses consistently.
*
- * With batching, multiple backends may each land a fetch-add- * that returns a value past NBuffers in the same pass. After+ * With batching, multiple backends may each land a fetch-add that+ * returns a value past NBuffers in the same pass. After
* acquiring the spinlock we re-read the counter: if another
* backend already wrapped it below NBuffers we are done.
*/
@@ -214,8 +180,6 @@ ClockSweepTick(void)
* GetVictimBuffer(). The only hard requirement GetVictimBuffer() has is that
* the selected buffer must not currently be pinned by anyone.
*
- * strategy is a BufferAccessStrategy object, or NULL for default strategy.- *
* It is the callers responsibility to ensure the buffer ownership can be
* tracked via TrackNewBufferPin().
*
@@ -223,29 +187,13 @@ ClockSweepTick(void)
* before returning.
*/
BufferDesc *
-StrategyGetBuffer(BufferAccessStrategy strategy, uint64 *buf_state, bool *from_ring)+StrategyGetBuffer(uint64 *buf_state)
{
BufferDesc *buf;
int bgwprocno;
int trycounter;
bool force_cool;
- *from_ring = false;-- /*- * If given a strategy object, see whether it can select a buffer. We- * assume strategy objects don't need buffer_strategy_lock.- */- if (strategy != NULL)- {- buf = GetBufferFromRing(strategy, buf_state);- if (buf != NULL)- {- *from_ring = true;- return buf;- }- }-
/*
* If asked, we need to waken the bgwriter. Since we don't want to rely on
* a spinlock for this we force a read from shared memory once, and then
@@ -274,8 +222,7 @@ StrategyGetBuffer(BufferAccessStrategy strategy, uint64 *buf_state, bool *from_r
/*
* We count buffer allocation requests so that the bgwriter can estimate
- * the rate of buffer consumption. Note that buffers recycled by a- * strategy object are intentionally not counted here.+ * the rate of buffer consumption.
*/
pg_atomic_fetch_add_u32(&StrategyControl->numBufferAllocs, 1);
@@ -347,7 +294,7 @@ StrategyGetBuffer(BufferAccessStrategy strategy, uint64 *buf_state, bool *from_r
if (!force_cool)
{
no_progress = true;
- break; /* advance the hand, look for COOL */+ break; /* advance the hand, look for COOL */
}
if (BUF_STATE_GET_REFBIT(local_buf_state))
@@ -387,8 +334,6 @@ StrategyGetBuffer(BufferAccessStrategy strategy, uint64 *buf_state, bool *from_r
local_buf_state))
{
/* Found a usable buffer */
- if (strategy != NULL)- AddBufferToRing(strategy, buf);
*buf_state = local_buf_state;
TrackNewBufferPin(BufferDescriptorGetBuffer(buf));
@@ -399,11 +344,12 @@ StrategyGetBuffer(BufferAccessStrategy strategy, uint64 *buf_state, bool *from_r
}
/*
- * A tick that made no progress toward a victim counts down trycounter.- * A full unproductive pass escalates to force_cool (cool HOT buffers- * into victims); a second unproductive full pass means everything is- * pinned, so fail rather than spin forever. (A failed CAS above is- * neither progress nor a full miss: we simply retry the same buffer.)+ * A tick that made no progress toward a victim counts down+ * trycounter. A full unproductive pass escalates to force_cool (cool+ * HOT buffers into victims); a second unproductive full pass means+ * everything is pinned, so fail rather than spin forever. (A failed+ * CAS above is neither progress nor a full miss: we simply retry the+ * same buffer.)
*/
if (no_progress && --trycounter == 0)
{
@@ -531,361 +477,3 @@ StrategyCtlShmemInit(void *arg)
else
StrategyControl->batchSize = 1;
}
---/* ----------------------------------------------------------------- * Backend-private buffer ring management- * ----------------------------------------------------------------- */---/*- * GetAccessStrategy -- create a BufferAccessStrategy object- *- * The object is allocated in the current memory context.- */-BufferAccessStrategy-GetAccessStrategy(BufferAccessStrategyType btype)-{- int ring_size_kb;-- /*- * Select ring size to use. See buffer/README for rationales.- *- * Note: if you change the ring size for BAS_BULKREAD, see also- * SYNC_SCAN_REPORT_INTERVAL in access/heap/syncscan.c.- */- switch (btype)- {- case BAS_NORMAL:- /* if someone asks for NORMAL, just give 'em a "default" object */- return NULL;-- case BAS_BULKREAD:- {- int ring_max_kb;-- /*- * The ring always needs to be large enough to allow some- * separation in time between providing a buffer to the user- * of the strategy and that buffer being reused. Otherwise the- * user's pin will prevent reuse of the buffer, even without- * concurrent activity.- *- * We also need to ensure the ring always is large enough for- * SYNC_SCAN_REPORT_INTERVAL, as noted above.- *- * Thus we start out a minimal size and increase the size- * further if appropriate.- */- ring_size_kb = 256;-- /*- * There's no point in a larger ring if we won't be allowed to- * pin sufficiently many buffers. But we never limit to less- * than the minimal size above.- */- ring_max_kb = GetPinLimit() * (BLCKSZ / 1024);- ring_max_kb = Max(ring_size_kb, ring_max_kb);-- /*- * We would like the ring to additionally have space for the- * configured degree of IO concurrency. While being read in,- * buffers can obviously not yet be reused.- *- * Each IO can be up to io_combine_limit blocks large, and we- * want to start up to effective_io_concurrency IOs.- *- * Note that effective_io_concurrency may be 0, which disables- * AIO.- */- ring_size_kb += (BLCKSZ / 1024) *- io_combine_limit * effective_io_concurrency;-- if (ring_size_kb > ring_max_kb)- ring_size_kb = ring_max_kb;- break;- }- case BAS_BULKWRITE:- ring_size_kb = 16 * 1024;- break;- case BAS_VACUUM:- ring_size_kb = 2048;- break;-- default:- elog(ERROR, "unrecognized buffer access strategy: %d",- (int) btype);- return NULL; /* keep compiler quiet */- }-- return GetAccessStrategyWithSize(btype, ring_size_kb);-}--/*- * GetAccessStrategyWithSize -- create a BufferAccessStrategy object with a- * number of buffers equivalent to the passed in size.- *- * If the given ring size is 0, no BufferAccessStrategy will be created and- * the function will return NULL. ring_size_kb must not be negative.- */-BufferAccessStrategy-GetAccessStrategyWithSize(BufferAccessStrategyType btype, int ring_size_kb)-{- int ring_buffers;- BufferAccessStrategy strategy;-- Assert(ring_size_kb >= 0);-- /* Figure out how many buffers ring_size_kb is */- ring_buffers = ring_size_kb / (BLCKSZ / 1024);-- /* 0 means unlimited, so no BufferAccessStrategy required */- if (ring_buffers == 0)- return NULL;-- /* Cap to 1/8th of shared_buffers */- ring_buffers = Min(NBuffers / 8, ring_buffers);-- /* NBuffers should never be less than 16, so this shouldn't happen */- Assert(ring_buffers > 0);-- /* Allocate the object and initialize all elements to zeroes */- strategy = (BufferAccessStrategy)- palloc0(offsetof(BufferAccessStrategyData, buffers) +- ring_buffers * sizeof(Buffer));-- /* Set fields that don't start out zero */- strategy->btype = btype;- strategy->nbuffers = ring_buffers;-- return strategy;-}--/*- * GetAccessStrategyBufferCount -- an accessor for the number of buffers in- * the ring- *- * Returns 0 on NULL input to match behavior of GetAccessStrategyWithSize()- * returning NULL with 0 size.- */-int-GetAccessStrategyBufferCount(BufferAccessStrategy strategy)-{- if (strategy == NULL)- return 0;-- return strategy->nbuffers;-}--/*- * GetAccessStrategyPinLimit -- get cap of number of buffers that should be pinned- *- * When pinning extra buffers to look ahead, users of a ring-based strategy are- * in danger of pinning too much of the ring at once while performing look-ahead.- * For some strategies, that means "escaping" from the ring, and in others it- * means forcing dirty data to disk very frequently with associated WAL- * flushing. Since external code has no insight into any of that, allow- * individual strategy types to expose a clamp that should be applied when- * deciding on a maximum number of buffers to pin at once.- *- * Callers should combine this number with other relevant limits and take the- * minimum.- */-int-GetAccessStrategyPinLimit(BufferAccessStrategy strategy)-{- if (strategy == NULL)- return NBuffers;-- switch (strategy->btype)- {- case BAS_BULKREAD:-- /*- * Since BAS_BULKREAD uses StrategyRejectBuffer(), dirty buffers- * shouldn't be a problem and the caller is free to pin up to the- * entire ring at once.- */- return strategy->nbuffers;-- default:-- /*- * Tell caller not to pin more than half the buffers in the ring.- * This is a trade-off between look ahead distance and deferring- * writeback and associated WAL traffic.- */- return strategy->nbuffers / 2;- }-}--/*- * FreeAccessStrategy -- release a BufferAccessStrategy object- *- * A simple pfree would do at the moment, but we would prefer that callers- * don't assume that much about the representation of BufferAccessStrategy.- */-void-FreeAccessStrategy(BufferAccessStrategy strategy)-{- /* don't crash if called on a "default" strategy */- if (strategy != NULL)- pfree(strategy);-}--/*- * GetBufferFromRing -- returns a buffer from the ring, or NULL if the- * ring is empty / not usable.- *- * The buffer is pinned and marked as owned, using TrackNewBufferPin(), before- * returning.- */-static BufferDesc *-GetBufferFromRing(BufferAccessStrategy strategy, uint64 *buf_state)-{- BufferDesc *buf;- Buffer bufnum;- uint64 old_buf_state;- uint64 local_buf_state; /* to avoid repeated (de-)referencing */--- /* Advance to next ring slot */- if (++strategy->current >= strategy->nbuffers)- strategy->current = 0;-- /*- * If the slot hasn't been filled yet, tell the caller to allocate a new- * buffer with the normal allocation strategy. He will then fill this- * slot by calling AddBufferToRing with the new buffer.- */- bufnum = strategy->buffers[strategy->current];- if (bufnum == InvalidBuffer)- return NULL;-- buf = GetBufferDescriptor(bufnum - 1);-- /*- * Check whether the buffer can be used and pin it if so. Do this using a- * CAS loop, to avoid having to lock the buffer header.- */- old_buf_state = pg_atomic_read_u64(&buf->state);- for (;;)- {- local_buf_state = old_buf_state;-- /*- * If the buffer is pinned we cannot use it under any circumstances.- *- * With the cooling-state replacement the field holds only COOL or HOT,- * so the stock "usage_count > 1 means another backend touched it"- * heuristic no longer applies: a ring element is reusable whenever it- * is unpinned. (The whole ring mechanism is removed in a later patch;- * scan resistance is now intrinsic to the sweep.)- */- if (BUF_STATE_GET_REFCOUNT(local_buf_state) != 0)- break;-- /* See equivalent code in PinBuffer() */- if (unlikely(local_buf_state & BM_LOCKED))- {- old_buf_state = WaitBufHdrUnlocked(buf);- continue;- }-- /* pin the buffer if the CAS succeeds */- local_buf_state += BUF_REFCOUNT_ONE;-- if (pg_atomic_compare_exchange_u64(&buf->state, &old_buf_state,- local_buf_state))- {- *buf_state = local_buf_state;-- TrackNewBufferPin(BufferDescriptorGetBuffer(buf));- return buf;- }- }-- /*- * Tell caller to allocate a new buffer with the normal allocation- * strategy. He'll then replace this ring element via AddBufferToRing.- */- return NULL;-}--/*- * AddBufferToRing -- add a buffer to the buffer ring- *- * Caller must hold the buffer header spinlock on the buffer. Since this- * is called with the spinlock held, it had better be quite cheap.- */-static void-AddBufferToRing(BufferAccessStrategy strategy, BufferDesc *buf)-{- strategy->buffers[strategy->current] = BufferDescriptorGetBuffer(buf);-}--/*- * Utility function returning the IOContext of a given BufferAccessStrategy's- * strategy ring.- */-IOContext-IOContextForStrategy(BufferAccessStrategy strategy)-{- if (!strategy)- return IOCONTEXT_NORMAL;-- switch (strategy->btype)- {- case BAS_NORMAL:-- /*- * Currently, GetAccessStrategy() returns NULL for- * BufferAccessStrategyType BAS_NORMAL, so this case is- * unreachable.- */- pg_unreachable();- return IOCONTEXT_NORMAL;- case BAS_BULKREAD:- return IOCONTEXT_BULKREAD;- case BAS_BULKWRITE:- return IOCONTEXT_BULKWRITE;- case BAS_VACUUM:- return IOCONTEXT_VACUUM;- }-- elog(ERROR, "unrecognized BufferAccessStrategyType: %d", strategy->btype);- pg_unreachable();-}--/*- * StrategyRejectBuffer -- consider rejecting a dirty buffer- *- * When a nondefault strategy is used, the buffer manager calls this function- * when it turns out that the buffer selected by StrategyGetBuffer needs to- * be written out and doing so would require flushing WAL too. This gives us- * a chance to choose a different victim.- *- * Returns true if buffer manager should ask for a new victim, and false- * if this buffer should be written and re-used.- */-bool-StrategyRejectBuffer(BufferAccessStrategy strategy, BufferDesc *buf, bool from_ring)-{- /* We only do this in bulkread mode */- if (strategy->btype != BAS_BULKREAD)- return false;-- /* Don't muck with behavior of normal buffer-replacement strategy */- if (!from_ring ||- strategy->buffers[strategy->current] != BufferDescriptorGetBuffer(buf))- return false;-- /*- * Remove the dirty buffer from the ring; necessary to prevent infinite- * loop if all ring members are dirty.- */- strategy->buffers[strategy->current] = InvalidBuffer;-- return true;-}diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.cindex 006edab9d77..29cb2d70f66 100644--- a/src/backend/storage/freespace/freespace.c+++ b/src/backend/storage/freespace/freespace.c@@ -603,7 +603,7 @@ fsm_readbuf(Relation rel, FSMAddress addr, bool extend)
return InvalidBuffer;
}
else
- buf = ReadBufferExtended(rel, FSM_FORKNUM, blkno, RBM_ZERO_ON_ERROR, NULL);+ buf = ReadBufferExtended(rel, FSM_FORKNUM, blkno, RBM_ZERO_ON_ERROR);
/*
* Initializing the page when needed is trickier than it looks, because of
@@ -638,7 +638,7 @@ fsm_readbuf(Relation rel, FSMAddress addr, bool extend)
static Buffer
fsm_extend(Relation rel, BlockNumber fsm_nblocks)
{
- return ExtendBufferedRelTo(BMR_REL(rel), FSM_FORKNUM, NULL,+ return ExtendBufferedRelTo(BMR_REL(rel), FSM_FORKNUM,
EB_CREATE_FORK_IF_NEEDED |
EB_CLEAR_SIZE_CACHE,
fsm_nblocks,
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.cindex 718c1cfc0f9..3c3e35ab8a8 100644--- a/src/backend/storage/smgr/md.c+++ b/src/backend/storage/smgr/md.c@@ -1540,15 +1540,8 @@ register_dirty_segment(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
FilePathName(seg->mdfd_vfd))));
/*
- * We have no way of knowing if the current IOContext is- * IOCONTEXT_NORMAL or IOCONTEXT_[BULKREAD, BULKWRITE, VACUUM] at this- * point, so count the fsync as being in the IOCONTEXT_NORMAL- * IOContext. This is probably okay, because the number of backend- * fsyncs doesn't say anything about the efficacy of the- * BufferAccessStrategy. And counting both fsyncs done in- * IOCONTEXT_NORMAL and IOCONTEXT_[BULKREAD, BULKWRITE, VACUUM] under- * IOCONTEXT_NORMAL is likely clearer when investigating the number of- * backend fsyncs.+ * Count the fsync in the IOCONTEXT_NORMAL IOContext, the only+ * IOContext relations are read/written under.
*/
pgstat_count_io_op_time(IOOBJECT_RELATION, IOCONTEXT_NORMAL,
IOOP_FSYNC, io_start, 1, 0);
diff --git a/src/backend/utils/activity/pgstat_io.c b/src/backend/utils/activity/pgstat_io.cindex 38bae7b15d2..d66a23749d5 100644--- a/src/backend/utils/activity/pgstat_io.c+++ b/src/backend/utils/activity/pgstat_io.c@@ -241,16 +241,10 @@ pgstat_get_io_context_name(IOContext io_context)
{
switch (io_context)
{
- case IOCONTEXT_BULKREAD:- return "bulkread";- case IOCONTEXT_BULKWRITE:- return "bulkwrite";
case IOCONTEXT_INIT:
return "init";
case IOCONTEXT_NORMAL:
return "normal";
- case IOCONTEXT_VACUUM:- return "vacuum";
}
elog(ERROR, "unrecognized IOContext value: %d", io_context);
@@ -451,18 +445,6 @@ pgstat_tracks_io_object(BackendType bktype, IOObject io_object,
* IOContexts, and, while it may not be inherently incorrect for them to
* do so, excluding those rows from the view makes the view easier to use.
*/
- if ((bktype == B_CHECKPOINTER || bktype == B_BG_WRITER) &&- (io_context == IOCONTEXT_BULKREAD ||- io_context == IOCONTEXT_BULKWRITE ||- io_context == IOCONTEXT_VACUUM))- return false;-- if (bktype == B_AUTOVAC_LAUNCHER && io_context == IOCONTEXT_VACUUM)- return false;-- if ((bktype == B_AUTOVAC_WORKER || bktype == B_AUTOVAC_LAUNCHER) &&- io_context == IOCONTEXT_BULKWRITE)- return false;
return true;
}
@@ -479,8 +461,6 @@ bool
pgstat_tracks_io_op(BackendType bktype, IOObject io_object,
IOContext io_context, IOOp io_op)
{
- bool strategy_io_context;-
/* if (io_context, io_object) will never collect stats, we're done */
if (!pgstat_tracks_io_object(bktype, io_object, io_context))
return false;
@@ -521,17 +501,13 @@ pgstat_tracks_io_op(BackendType bktype, IOObject io_object,
/*
* Some IOOps are not valid in certain IOContexts and some IOOps are only
* valid in certain contexts.
+ *+ * IOOP_REUSE was only relevant when a BufferAccessStrategy was in use.+ * Buffer access strategies (ring buffers) have been removed -- scan+ * resistance is now intrinsic to the cooling-stage clock sweep -- so+ * IOOP_REUSE never occurs.
*/
- if (io_context == IOCONTEXT_BULKREAD && io_op == IOOP_EXTEND)- return false;-- strategy_io_context = io_context == IOCONTEXT_BULKREAD ||- io_context == IOCONTEXT_BULKWRITE || io_context == IOCONTEXT_VACUUM;-- /*- * IOOP_REUSE is only relevant when a BufferAccessStrategy is in use.- */- if (!strategy_io_context && io_op == IOOP_REUSE)+ if (io_op == IOOP_REUSE)
return false;
/*
@@ -545,14 +521,5 @@ pgstat_tracks_io_op(BackendType bktype, IOObject io_object,
!(io_op == IOOP_WRITE || io_op == IOOP_READ || io_op == IOOP_FSYNC))
return false;
- /*- * IOOP_FSYNC IOOps done by a backend using a BufferAccessStrategy are- * counted in the IOCONTEXT_NORMAL IOContext. See comment in- * register_dirty_segment() for more details.- */- if (strategy_io_context && io_op == IOOP_FSYNC)- return false;--
return true;
}
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.cindex bbd28d14d99..555c04272f5 100644--- a/src/backend/utils/init/globals.c+++ b/src/backend/utils/init/globals.c@@ -149,7 +149,6 @@ int autovacuum_max_parallel_workers = 0;
int MaxBackends = 0;
/* GUC parameters for vacuum */
-int VacuumBufferUsageLimit = 2048;
int VacuumCostPageHit = 1;
int VacuumCostPageMiss = 2;
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.datindex d421cdbde76..b1ea81f536c 100644--- a/src/backend/utils/misc/guc_parameters.dat+++ b/src/backend/utils/misc/guc_parameters.dat@@ -3326,16 +3326,6 @@
boot_val => 'DEFAULT_UPDATE_PROCESS_TITLE',
},
-{ name => 'vacuum_buffer_usage_limit', type => 'int', context => 'PGC_USERSET', group => 'RESOURCES_MEM',- short_desc => 'Sets the buffer pool size for VACUUM, ANALYZE, and autovacuum.',- flags => 'GUC_UNIT_KB',- variable => 'VacuumBufferUsageLimit',- boot_val => '2048',- min => '0',- max => 'MAX_BAS_VAC_RING_SIZE_KB',- check_hook => 'check_vacuum_buffer_usage_limit',-},-
{ name => 'vacuum_cost_delay', type => 'real', context => 'PGC_USERSET', group => 'VACUUM_COST_DELAY',
short_desc => 'Vacuum cost delay in milliseconds.',
flags => 'GUC_UNIT_MS',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sampleindex 7958653077b..cb1eb8ef3ff 100644--- a/src/backend/utils/misc/postgresql.conf.sample+++ b/src/backend/utils/misc/postgresql.conf.sample@@ -164,9 +164,6 @@
# mmap
# (change requires restart)
#min_dynamic_shared_memory = 0MB # (change requires restart)
-#vacuum_buffer_usage_limit = 2MB # size of vacuum and analyze buffer access strategy ring;- # 0 to disable vacuum buffer access strategy;- # range 128kB to 16GB
# SLRU buffers (change requires restart)
#commit_timestamp_buffers = 0 # memory for pg_commit_ts (0 = auto)
diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.cindex f8158ca6a78..f78e57ff2c3 100644--- a/src/bin/scripts/vacuumdb.c+++ b/src/bin/scripts/vacuumdb.c@@ -57,7 +57,6 @@ main(int argc, char *argv[])
{"no-truncate", no_argument, NULL, 10},
{"no-process-toast", no_argument, NULL, 11},
{"no-process-main", no_argument, NULL, 12},
- {"buffer-usage-limit", required_argument, NULL, 13},
{"missing-stats-only", no_argument, NULL, 14},
{"dry-run", no_argument, NULL, 15},
{NULL, 0, NULL, 0}
@@ -202,9 +201,6 @@ main(int argc, char *argv[])
case 12:
vacopts.process_main = false;
break;
- case 13:- vacopts.buffer_usage_limit = escape_quotes(optarg);- break;
case 14:
vacopts.missing_stats_only = true;
break;
@@ -290,14 +286,6 @@ main(int argc, char *argv[])
pg_fatal("cannot use the \"%s\" option with the \"%s\" option",
"no-index-cleanup", "force-index-cleanup");
- /*- * buffer-usage-limit is not allowed with VACUUM FULL unless ANALYZE is- * included too.- */- if (vacopts.buffer_usage_limit && vacopts.full && !vacopts.and_analyze)- pg_fatal("cannot use the \"%s\" option with the \"%s\" option",- "buffer-usage-limit", "full");-
/*
* Prohibit --missing-stats-only without --analyze-only or
* --analyze-in-stages.
@@ -352,7 +340,6 @@ help(const char *progname)
printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
printf(_("\nOptions:\n"));
printf(_(" -a, --all vacuum all databases\n"));
- printf(_(" --buffer-usage-limit=SIZE size of ring buffer used for vacuum\n"));
printf(_(" -d, --dbname=DBNAME database to vacuum\n"));
printf(_(" --disable-page-skipping disable all page-skipping behavior\n"));
printf(_(" --dry-run show the commands that would be sent to the server\n"));
diff --git a/src/bin/scripts/vacuuming.c b/src/bin/scripts/vacuuming.cindex 855a5754c98..078a3bbb516 100644--- a/src/bin/scripts/vacuuming.c+++ b/src/bin/scripts/vacuuming.c@@ -264,13 +264,6 @@ vacuum_one_database(ConnParams *cparams,
"--parallel", "13");
}
- if (vacopts->buffer_usage_limit && PQserverVersion(conn) < 160000)- {- PQfinish(conn);- pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",- "--buffer-usage-limit", "16");- }-
if (vacopts->missing_stats_only && PQserverVersion(conn) < 150000)
{
PQfinish(conn);
@@ -865,13 +858,6 @@ prepare_vacuum_command(PGconn *conn, PQExpBuffer sql,
appendPQExpBuffer(sql, "%sVERBOSE", sep);
sep = comma;
}
- if (vacopts->buffer_usage_limit)- {- Assert(serverVersion >= 160000);- appendPQExpBuffer(sql, "%sBUFFER_USAGE_LIMIT '%s'", sep,- vacopts->buffer_usage_limit);- sep = comma;- }
if (sep != paren)
appendPQExpBufferChar(sql, ')');
}
@@ -974,13 +960,6 @@ prepare_vacuum_command(PGconn *conn, PQExpBuffer sql,
vacopts->parallel_workers);
sep = comma;
}
- if (vacopts->buffer_usage_limit)- {- Assert(serverVersion >= 160000);- appendPQExpBuffer(sql, "%sBUFFER_USAGE_LIMIT '%s'", sep,- vacopts->buffer_usage_limit);- sep = comma;- }
if (sep != paren)
appendPQExpBufferChar(sql, ')');
}
diff --git a/src/bin/scripts/vacuuming.h b/src/bin/scripts/vacuuming.hindex 5a491db2526..83a2469a6aa 100644--- a/src/bin/scripts/vacuuming.h+++ b/src/bin/scripts/vacuuming.h@@ -49,7 +49,6 @@ typedef struct vacuumingOptions
bool process_main;
bool process_toast;
bool skip_database_stats;
- char *buffer_usage_limit;
bool missing_stats_only;
bool echo;
bool quiet;
diff --git a/src/include/access/genam.h b/src/include/access/genam.hindex 68bfe405db3..bc87f6f7cc0 100644--- a/src/include/access/genam.h+++ b/src/include/access/genam.h@@ -58,7 +58,6 @@ typedef struct IndexVacuumInfo
bool estimated_count; /* num_heap_tuples is an estimate */
int message_level; /* ereport level for progress messages */
double num_heap_tuples; /* tuples remaining in heap */
- BufferAccessStrategy strategy; /* access strategy for reads */
} IndexVacuumInfo;
/*
diff --git a/src/include/access/hash.h b/src/include/access/hash.hindex a8702f0e5ea..7c9268fe953 100644--- a/src/include/access/hash.h+++ b/src/include/access/hash.h@@ -405,12 +405,11 @@ extern void _hash_pgaddmultitup(Relation rel, Buffer buf, IndexTuple *itups,
extern Buffer _hash_addovflpage(Relation rel, Buffer metabuf, Buffer buf, bool retain_pin);
extern BlockNumber _hash_freeovflpage(Relation rel, Buffer bucketbuf, Buffer ovflbuf,
Buffer wbuf, IndexTuple *itups, OffsetNumber *itup_offsets,
- Size *tups_size, uint16 nitups, BufferAccessStrategy bstrategy);+ Size *tups_size, uint16 nitups);
extern void _hash_initbitmapbuffer(Buffer buf, uint16 bmsize, bool initpage);
extern void _hash_squeezebucket(Relation rel,
Bucket bucket, BlockNumber bucket_blkno,
- Buffer bucket_buf,- BufferAccessStrategy bstrategy);+ Buffer bucket_buf);
extern uint32 _hash_ovflblkno_to_bitno(HashMetaPage metap, BlockNumber ovflblkno);
/* hashpage.c */
@@ -428,9 +427,6 @@ extern void _hash_initbuf(Buffer buf, uint32 max_bucket, uint32 num_bucket,
uint32 flag, bool initpage);
extern Buffer _hash_getnewbuf(Relation rel, BlockNumber blkno,
ForkNumber forkNum);
-extern Buffer _hash_getbuf_with_strategy(Relation rel, BlockNumber blkno,- int access, int flags,- BufferAccessStrategy bstrategy);
extern void _hash_relbuf(Relation rel, Buffer buf);
extern void _hash_dropbuf(Relation rel, Buffer buf);
extern void _hash_dropscanbuf(Relation rel, HashScanOpaque so);
@@ -481,7 +477,6 @@ extern void _hash_kill_items(IndexScanDesc scan);
/* hash.c */
extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
Buffer bucket_buf, BlockNumber bucket_blkno,
- BufferAccessStrategy bstrategy,
uint32 maxbucket, uint32 highmask, uint32 lowmask,
double *tuples_removed, double *num_index_tuples,
bool split_cleanup,
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.hindex 5176478c295..0967b659794 100644--- a/src/include/access/heapam.h+++ b/src/include/access/heapam.h@@ -72,8 +72,6 @@ typedef struct HeapScanDescData
Buffer rs_cbuf; /* current buffer in scan, if any */
/* NB: if rs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
- BufferAccessStrategy rs_strategy; /* access strategy for reads */-
HeapTupleData rs_ctup; /* current tuple in scan, if any */
/* For scans that stream reads */
@@ -466,7 +464,7 @@ extern void log_heap_prune_and_freeze(Relation relation, Buffer buffer,
/* in heap/vacuumlazy.c */
extern void heap_vacuum_rel(Relation rel,
- const VacuumParams *params, BufferAccessStrategy bstrategy);+ const VacuumParams *params);
#ifdef USE_ASSERT_CHECKING
extern bool heap_page_is_all_visible(Relation rel, Buffer buf,
GlobalVisState *vistest,
diff --git a/src/include/access/hio.h b/src/include/access/hio.hindex 60cfc375fd5..63bfcc884ee 100644--- a/src/include/access/hio.h+++ b/src/include/access/hio.h@@ -28,7 +28,6 @@
*/
typedef struct BulkInsertStateData
{
- BufferAccessStrategy strategy; /* our BULKWRITE strategy object */
Buffer current_buf; /* current insertion target page */
/*
diff --git a/src/include/access/tableam.h b/src/include/access/tableam.hindex f2c36696bca..83ba0f3ca73 100644--- a/src/include/access/tableam.h+++ b/src/include/access/tableam.h@@ -689,8 +689,7 @@ typedef struct TableAmRoutine
* integrate with autovacuum's scheduling.
*/
void (*relation_vacuum) (Relation rel,
- const VacuumParams *params,- BufferAccessStrategy bstrategy);+ const VacuumParams *params);
/*
* Prepare to analyze block `blockno` of `scan`. The scan has been started
@@ -1774,10 +1773,9 @@ table_relation_copy_for_cluster(Relation OldTable, Relation NewTable,
* routine, even if (for ANALYZE) it is part of the same VACUUM command.
*/
static inline void
-table_relation_vacuum(Relation rel, const VacuumParams *params,- BufferAccessStrategy bstrategy)+table_relation_vacuum(Relation rel, const VacuumParams *params)
{
- rel->rd_tableam->relation_vacuum(rel, params, bstrategy);+ rel->rd_tableam->relation_vacuum(rel, params);
}
/*
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.hindex 956d9cea36d..14a9b530bcf 100644--- a/src/include/commands/vacuum.h+++ b/src/include/commands/vacuum.h@@ -363,7 +363,7 @@ extern PGDLLIMPORT int64 parallel_vacuum_worker_delay_ns;
/* in commands/vacuum.c */
extern void ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel);
extern void vacuum(List *relations, const VacuumParams *params,
- BufferAccessStrategy bstrategy, MemoryContext vac_context,+ MemoryContext vac_context,
bool isTopLevel);
extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
int *nindexes, Relation **Irel);
@@ -407,8 +407,7 @@ extern void VacuumUpdateCosts(void);
/* in commands/vacuumparallel.c */
extern ParallelVacuumState *parallel_vacuum_init(Relation rel, Relation *indrels,
int nindexes, int nrequested_workers,
- int vac_work_mem, int elevel,- BufferAccessStrategy bstrategy);+ int vac_work_mem, int elevel);
extern void parallel_vacuum_end(ParallelVacuumState *pvs, IndexBulkDeleteResult **istats);
extern TidStore *parallel_vacuum_get_dead_items(ParallelVacuumState *pvs,
VacDeadItemsInfo **dead_items_info_p);
@@ -428,8 +427,7 @@ extern void parallel_vacuum_main(dsm_segment *seg, shm_toc *toc);
/* in commands/analyze.c */
extern void analyze_rel(Oid relid, RangeVar *relation,
- const VacuumParams *params, List *va_cols, bool in_outer_xact,- BufferAccessStrategy bstrategy);+ const VacuumParams *params, List *va_cols, bool in_outer_xact);
extern bool attribute_is_analyzable(Relation onerel, int attnum, Form_pg_attribute attr,
int *p_attstattarget);
extern bool std_typanalyze(VacAttrStats *stats);
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.hindex 7170a4bff98..c5d582b152b 100644--- a/src/include/miscadmin.h+++ b/src/include/miscadmin.h@@ -273,15 +273,6 @@ extern PGDLLIMPORT double hash_mem_multiplier;
extern PGDLLIMPORT int maintenance_work_mem;
extern PGDLLIMPORT int max_parallel_maintenance_workers;
-/*- * Upper and lower hard limits for the buffer access strategy ring size- * specified by the VacuumBufferUsageLimit GUC and BUFFER_USAGE_LIMIT option- * to VACUUM and ANALYZE.- */-#define MIN_BAS_VAC_RING_SIZE_KB 128-#define MAX_BAS_VAC_RING_SIZE_KB (16 * 1024 * 1024)--extern PGDLLIMPORT int VacuumBufferUsageLimit;
extern PGDLLIMPORT int VacuumCostPageHit;
extern PGDLLIMPORT int VacuumCostPageMiss;
extern PGDLLIMPORT int VacuumCostPageDirty;
diff --git a/src/include/pgstat.h b/src/include/pgstat.hindex 58a44857f13..68b4e5093a2 100644--- a/src/include/pgstat.h+++ b/src/include/pgstat.h@@ -287,14 +287,11 @@ typedef enum IOObject
typedef enum IOContext
{
- IOCONTEXT_BULKREAD,- IOCONTEXT_BULKWRITE,
IOCONTEXT_INIT,
IOCONTEXT_NORMAL,
- IOCONTEXT_VACUUM,
} IOContext;
-#define IOCONTEXT_NUM_TYPES (IOCONTEXT_VACUUM + 1)+#define IOCONTEXT_NUM_TYPES (IOCONTEXT_NORMAL + 1)
/*
* Enumeration of IO operations.
diff --git a/src/include/storage/buf.h b/src/include/storage/buf.hindex b21445522b1..fa942a64f05 100644--- a/src/include/storage/buf.h+++ b/src/include/storage/buf.h@@ -36,11 +36,4 @@ typedef int Buffer;
*/
#define BufferIsLocal(buffer) ((buffer) < 0)
-/*- * Buffer access strategy objects.- *- * BufferAccessStrategyData is private to freelist.c- */-typedef struct BufferAccessStrategyData *BufferAccessStrategy;-
#endif /* BUF_H */
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.hindex 131df126cda..3b44ad3e0b6 100644--- a/src/include/storage/buf_internals.h+++ b/src/include/storage/buf_internals.h@@ -632,11 +632,7 @@ extern void TerminateBufferIO(BufferDesc *buf, bool clear_dirty, uint64 set_flag
/* freelist.c */
-extern IOContext IOContextForStrategy(BufferAccessStrategy strategy);-extern BufferDesc *StrategyGetBuffer(BufferAccessStrategy strategy,- uint64 *buf_state, bool *from_ring);-extern bool StrategyRejectBuffer(BufferAccessStrategy strategy,- BufferDesc *buf, bool from_ring);+extern BufferDesc *StrategyGetBuffer(uint64 *buf_state);
extern int StrategySyncStart(uint32 *complete_passes, uint32 *num_buf_alloc);
extern void StrategyNotifyBgWriter(int bgwprocno);
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.hindex 6837b35fc6d..36023792f36 100644--- a/src/include/storage/bufmgr.h+++ b/src/include/storage/bufmgr.h@@ -25,21 +25,6 @@
typedef void *Block;
-/*- * Possible arguments for GetAccessStrategy().- *- * If adding a new BufferAccessStrategyType, also add a new IOContext so- * IO statistics using this strategy are tracked.- */-typedef enum BufferAccessStrategyType-{- BAS_NORMAL, /* Normal random access */- BAS_BULKREAD, /* Large read-only scan (hint bit updates are- * ok) */- BAS_BULKWRITE, /* Large multi-block write (e.g. COPY IN) */- BAS_VACUUM, /* VACUUM */-} BufferAccessStrategyType;-
/* Possible modes for ReadBufferExtended() */
typedef enum
{
@@ -135,7 +120,6 @@ struct ReadBuffersOperation
SMgrRelation smgr;
char persistence;
ForkNumber forknum;
- BufferAccessStrategy strategy;
/*
* The following private members are private state for communication
@@ -235,11 +219,10 @@ extern bool ReadRecentBuffer(RelFileLocator rlocator, ForkNumber forkNum,
BlockNumber blockNum, Buffer recent_buffer);
extern Buffer ReadBuffer(Relation reln, BlockNumber blockNum);
extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum,
- BlockNumber blockNum, ReadBufferMode mode,- BufferAccessStrategy strategy);+ BlockNumber blockNum, ReadBufferMode mode);
extern Buffer ReadBufferWithoutRelcache(RelFileLocator rlocator,
ForkNumber forkNum, BlockNumber blockNum,
- ReadBufferMode mode, BufferAccessStrategy strategy,+ ReadBufferMode mode,
bool permanent);
extern bool StartReadBuffer(ReadBuffersOperation *operation,
@@ -266,18 +249,15 @@ extern Buffer ReleaseAndReadBuffer(Buffer buffer, Relation relation,
extern Buffer ExtendBufferedRel(BufferManagerRelation bmr,
ForkNumber forkNum,
- BufferAccessStrategy strategy,
uint32 flags);
extern BlockNumber ExtendBufferedRelBy(BufferManagerRelation bmr,
ForkNumber fork,
- BufferAccessStrategy strategy,
uint32 flags,
uint32 extend_by,
Buffer *buffers,
uint32 *extended_by);
extern Buffer ExtendBufferedRelTo(BufferManagerRelation bmr,
ForkNumber fork,
- BufferAccessStrategy strategy,
uint32 flags,
BlockNumber extend_to,
ReadBufferMode mode);
@@ -376,14 +356,6 @@ extern void AtProcExit_LocalBuffers(void);
/* in freelist.c */
-extern BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype);-extern BufferAccessStrategy GetAccessStrategyWithSize(BufferAccessStrategyType btype,- int ring_size_kb);-extern int GetAccessStrategyBufferCount(BufferAccessStrategy strategy);-extern int GetAccessStrategyPinLimit(BufferAccessStrategy strategy);--extern void FreeAccessStrategy(BufferAccessStrategy strategy);-
/* inline functions */
diff --git a/src/include/storage/read_stream.h b/src/include/storage/read_stream.hindex 48995c6d534..9e5fbbed9e8 100644--- a/src/include/storage/read_stream.h+++ b/src/include/storage/read_stream.h@@ -83,17 +83,14 @@ extern BlockNumber block_range_read_stream_cb(ReadStream *stream,
void *callback_private_data,
void *per_buffer_data);
extern ReadStream *read_stream_begin_relation(int flags,
- BufferAccessStrategy strategy,
Relation rel,
ForkNumber forknum,
ReadStreamBlockNumberCB callback,
void *callback_private_data,
size_t per_buffer_data_size);
extern Buffer read_stream_next_buffer(ReadStream *stream, void **per_buffer_data);
-extern BlockNumber read_stream_next_block(ReadStream *stream,- BufferAccessStrategy *strategy);+extern BlockNumber read_stream_next_block(ReadStream *stream);
extern ReadStream *read_stream_begin_smgr_relation(int flags,
- BufferAccessStrategy strategy,
SMgrRelation smgr,
char smgr_persistence,
ForkNumber forknum,
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.hindex 307f4fbaefe..3f28558d47c 100644--- a/src/include/utils/guc_hooks.h+++ b/src/include/utils/guc_hooks.h@@ -31,8 +31,6 @@ extern void assign_application_name(const char *newval, void *extra);
extern const char *show_archive_command(void);
extern bool check_autovacuum_work_mem(int *newval, void **extra,
GucSource source);
-extern bool check_vacuum_buffer_usage_limit(int *newval, void **extra,- GucSource source);
extern bool check_backtrace_functions(char **newval, void **extra,
GucSource source);
extern void assign_backtrace_functions(const char *newval, void *extra);
diff --git a/src/test/modules/test_aio/test_aio.c b/src/test/modules/test_aio/test_aio.cindex 6270775af7c..8d7b2a728d6 100644--- a/src/test/modules/test_aio/test_aio.c+++ b/src/test/modules/test_aio/test_aio.c@@ -194,7 +194,6 @@ grow_rel(PG_FUNCTION_ARGS)
ExtendBufferedRelBy(BMR_REL(rel),
MAIN_FORKNUM,
- NULL,
0,
extend_by_pages,
victim_buffers,
@@ -231,7 +230,7 @@ modify_rel_block(PG_FUNCTION_ARGS)
rel = relation_open(relid, AccessExclusiveLock);
buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno,
- RBM_ZERO_ON_ERROR, NULL);+ RBM_ZERO_ON_ERROR);
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
@@ -331,7 +330,7 @@ create_toy_buffer(Relation rel, BlockNumber blkno)
uint64 unset_bits = 0;
/* place buffer in shared buffers without erroring out */
- buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_ZERO_AND_LOCK, NULL);+ buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_ZERO_AND_LOCK);
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
if (RelationUsesLocalBuffers(rel))
@@ -737,7 +736,6 @@ read_buffers(PG_FUNCTION_ARGS)
operation->rel = rel;
operation->smgr = smgr;
operation->persistence = rel->rd_rel->relpersistence;
- operation->strategy = NULL;
operation->forknum = MAIN_FORKNUM;
io_reqds[nios] = StartReadBuffers(operation,
@@ -879,7 +877,6 @@ read_stream_for_blocks(PG_FUNCTION_ARGS)
rel = relation_open(relid, AccessShareLock);
stream = read_stream_begin_relation(READ_STREAM_FULL,
- NULL,
rel,
MAIN_FORKNUM,
read_stream_for_blocks_cb,
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.outindex 03cbc1cdef5..beb876c48b4 100644--- a/src/test/regress/expected/stats.out+++ b/src/test/regress/expected/stats.out@@ -16,22 +16,16 @@ SHOW track_counts; -- must be on
SELECT backend_type, object, context FROM pg_stat_io
ORDER BY backend_type COLLATE "C", object COLLATE "C", context COLLATE "C";
backend_type|object|context
-autovacuum launcher|relation|bulkread
autovacuum launcher|relation|init
autovacuum launcher|relation|normal
autovacuum launcher|wal|init
autovacuum launcher|wal|normal
-autovacuum worker|relation|bulkread
autovacuum worker|relation|init
autovacuum worker|relation|normal
-autovacuum worker|relation|vacuum
autovacuum worker|wal|init
autovacuum worker|wal|normal
-background worker|relation|bulkread-background worker|relation|bulkwrite
background worker|relation|init
background worker|relation|normal
-background worker|relation|vacuum
background worker|temp relation|normal
background worker|wal|init
background worker|wal|normal
@@ -43,67 +37,43 @@ checkpointer|relation|init
checkpointer|relation|normal
checkpointer|wal|init
checkpointer|wal|normal
-client backend|relation|bulkread-client backend|relation|bulkwrite
client backend|relation|init
client backend|relation|normal
-client backend|relation|vacuum
client backend|temp relation|normal
client backend|wal|init
client backend|wal|normal
-datachecksums launcher|relation|bulkread-datachecksums launcher|relation|bulkwrite
datachecksums launcher|relation|init
datachecksums launcher|relation|normal
-datachecksums launcher|relation|vacuum
datachecksums launcher|temp relation|normal
datachecksums launcher|wal|init
datachecksums launcher|wal|normal
-datachecksums worker|relation|bulkread-datachecksums worker|relation|bulkwrite
datachecksums worker|relation|init
datachecksums worker|relation|normal
-datachecksums worker|relation|vacuum
datachecksums worker|temp relation|normal
datachecksums worker|wal|init
datachecksums worker|wal|normal
-io worker|relation|bulkread-io worker|relation|bulkwrite
io worker|relation|init
io worker|relation|normal
-io worker|relation|vacuum
io worker|temp relation|normal
io worker|wal|init
io worker|wal|normal
-slotsync worker|relation|bulkread-slotsync worker|relation|bulkwrite
slotsync worker|relation|init
slotsync worker|relation|normal
-slotsync worker|relation|vacuum
slotsync worker|temp relation|normal
slotsync worker|wal|init
slotsync worker|wal|normal
-standalone backend|relation|bulkread-standalone backend|relation|bulkwrite
standalone backend|relation|init
standalone backend|relation|normal
-standalone backend|relation|vacuum
standalone backend|wal|init
standalone backend|wal|normal
-startup|relation|bulkread-startup|relation|bulkwrite
startup|relation|init
startup|relation|normal
-startup|relation|vacuum
startup|wal|init
startup|wal|normal
walreceiver|wal|init
walreceiver|wal|normal
-walsender|relation|bulkread-walsender|relation|bulkwrite
walsender|relation|init
walsender|relation|normal
-walsender|relation|vacuum
walsender|temp relation|normal
walsender|wal|init
walsender|wal|normal
@@ -111,7 +81,7 @@ walsummarizer|wal|init
walsummarizer|wal|normal
walwriter|wal|init
walwriter|wal|normal
-(95 rows)+(65 rows)
\a
-- List of registered statistics kinds.
SELECT id, name, fixed_amount,
@@ -1757,80 +1727,20 @@ SELECT :io_sum_local_new_tblspc_writes > :io_sum_local_after_writes;
(1 row)
RESET temp_buffers;
--- Test that reuse of strategy buffers and reads of blocks into these reused--- buffers while VACUUMing are tracked in pg_stat_io. If there is sufficient--- demand for shared buffers from concurrent queries, some buffers may be--- pinned by other backends before they can be reused. In such cases, the--- backend will evict a buffer from outside the ring and add it to the--- ring. This is considered an eviction and not a reuse.--- Set wal_skip_threshold smaller than the expected size of--- test_io_vac_strategy so that, even if wal_level is minimal, VACUUM FULL will--- fsync the newly rewritten test_io_vac_strategy instead of writing it to WAL.--- Writing it to WAL will result in the newly written relation pages being in--- shared buffers -- preventing us from testing BAS_VACUUM BufferAccessStrategy--- reads.-SET wal_skip_threshold = '1 kB';-SELECT sum(reuses) AS reuses, sum(reads) AS reads, sum(evictions) AS evictions- FROM pg_stat_io WHERE context = 'vacuum' \gset io_sum_vac_strategy_before_-CREATE TABLE test_io_vac_strategy(a int, b int) WITH (autovacuum_enabled = 'false');-INSERT INTO test_io_vac_strategy SELECT i, i from generate_series(1, 4500)i;--- Ensure that the next VACUUM will need to perform IO by rewriting the table--- first with VACUUM (FULL).-VACUUM (FULL) test_io_vac_strategy;--- Use the minimum BUFFER_USAGE_LIMIT to cause reuses or evictions with the--- smallest table possible.-VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128) test_io_vac_strategy;-SELECT pg_stat_force_next_flush();- pg_stat_force_next_flush ---------------------------- -(1 row)--SELECT sum(reuses) AS reuses, sum(reads) AS reads, sum(evictions) AS evictions- FROM pg_stat_io WHERE context = 'vacuum' \gset io_sum_vac_strategy_after_-SELECT :io_sum_vac_strategy_after_reads > :io_sum_vac_strategy_before_reads;- ?column? ------------ t-(1 row)--SELECT (:io_sum_vac_strategy_after_reuses + :io_sum_vac_strategy_after_evictions) >- (:io_sum_vac_strategy_before_reuses + :io_sum_vac_strategy_before_evictions);- ?column? ------------ t-(1 row)--RESET wal_skip_threshold;--- Test that extends done by a CTAS, which uses a BAS_BULKWRITE--- BufferAccessStrategy, are tracked in pg_stat_io.-SELECT sum(extends) AS io_sum_bulkwrite_strategy_extends_before- FROM pg_stat_io WHERE context = 'bulkwrite' \gset-CREATE TABLE test_io_bulkwrite_strategy AS SELECT i FROM generate_series(1,100)i;-SELECT pg_stat_force_next_flush();- pg_stat_force_next_flush ---------------------------- -(1 row)--SELECT sum(extends) AS io_sum_bulkwrite_strategy_extends_after- FROM pg_stat_io WHERE context = 'bulkwrite' \gset-SELECT :io_sum_bulkwrite_strategy_extends_after > :io_sum_bulkwrite_strategy_extends_before;- ?column? ------------ t-(1 row)-
-- Test IO stats reset
+-- Note: reuses is intentionally excluded from these totals. With the+-- BufferAccessStrategy ring buffers removed, IOOP_REUSE is never tracked, so+-- the reuses column is NULL for every row; summing it would make the whole+-- total NULL.
SELECT pg_stat_have_stats('io', 0, 0);
pg_stat_have_stats
--------------------
t
(1 row)
-SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS io_stats_pre_reset+SELECT sum(evictions) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS io_stats_pre_reset
FROM pg_stat_io \gset
-SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS my_io_stats_pre_reset+SELECT sum(evictions) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS my_io_stats_pre_reset
FROM pg_stat_get_backend_io(pg_backend_pid()) \gset
SELECT pg_stat_reset_shared('io');
pg_stat_reset_shared
@@ -1838,7 +1748,7 @@ SELECT pg_stat_reset_shared('io');
(1 row)
-SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS io_stats_post_reset+SELECT sum(evictions) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS io_stats_post_reset
FROM pg_stat_io \gset
SELECT :io_stats_post_reset < :io_stats_pre_reset;
?column?
@@ -1846,7 +1756,7 @@ SELECT :io_stats_post_reset < :io_stats_pre_reset;
t
(1 row)
-SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS my_io_stats_post_reset+SELECT sum(evictions) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS my_io_stats_post_reset
FROM pg_stat_get_backend_io(pg_backend_pid()) \gset
-- pg_stat_reset_shared() did not reset backend IO stats
SELECT :my_io_stats_pre_reset <= :my_io_stats_post_reset;
@@ -1862,7 +1772,7 @@ SELECT pg_stat_reset_backend_stats(pg_backend_pid());
(1 row)
-SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS my_io_stats_post_backend_reset+SELECT sum(evictions) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS my_io_stats_post_backend_reset
FROM pg_stat_get_backend_io(pg_backend_pid()) \gset
SELECT :my_io_stats_pre_reset > :my_io_stats_post_backend_reset;
?column?
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.outindex d4696bc3325..85d06907cec 100644--- a/src/test/regress/expected/vacuum.out+++ b/src/test/regress/expected/vacuum.out@@ -526,25 +526,6 @@ SELECT t.relfilenode = :toast_filenode AS is_same_toast_filenode
f
(1 row)
--- BUFFER_USAGE_LIMIT option-VACUUM (BUFFER_USAGE_LIMIT '512 kB') vac_option_tab;-ANALYZE (BUFFER_USAGE_LIMIT '512 kB') vac_option_tab;--- try disabling the buffer usage limit-VACUUM (BUFFER_USAGE_LIMIT 0) vac_option_tab;-ANALYZE (BUFFER_USAGE_LIMIT 0) vac_option_tab;--- value exceeds max size error-VACUUM (BUFFER_USAGE_LIMIT 16777220) vac_option_tab;-ERROR: BUFFER_USAGE_LIMIT option must be 0 or between 128 kB and 16777216 kB--- value is less than min size error-VACUUM (BUFFER_USAGE_LIMIT 120) vac_option_tab;-ERROR: BUFFER_USAGE_LIMIT option must be 0 or between 128 kB and 16777216 kB--- integer overflow error-VACUUM (BUFFER_USAGE_LIMIT 10000000000) vac_option_tab;-ERROR: BUFFER_USAGE_LIMIT option must be 0 or between 128 kB and 16777216 kB-HINT: Value exceeds integer range.--- incompatible with VACUUM FULL error-VACUUM (BUFFER_USAGE_LIMIT '512 kB', FULL) vac_option_tab;-ERROR: BUFFER_USAGE_LIMIT cannot be specified for VACUUM FULL
-- SKIP_DATABASE_STATS option
VACUUM (SKIP_DATABASE_STATS) vactst;
-- ONLY_DATABASE_STATS option
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sqlindex 4c265d1245c..4eb48114e80 100644--- a/src/test/regress/sql/stats.sql+++ b/src/test/regress/sql/stats.sql@@ -814,65 +814,27 @@ SELECT sum(writes) AS io_sum_local_new_tblspc_writes
SELECT :io_sum_local_new_tblspc_writes > :io_sum_local_after_writes;
RESET temp_buffers;
--- Test that reuse of strategy buffers and reads of blocks into these reused--- buffers while VACUUMing are tracked in pg_stat_io. If there is sufficient--- demand for shared buffers from concurrent queries, some buffers may be--- pinned by other backends before they can be reused. In such cases, the--- backend will evict a buffer from outside the ring and add it to the--- ring. This is considered an eviction and not a reuse.---- Set wal_skip_threshold smaller than the expected size of--- test_io_vac_strategy so that, even if wal_level is minimal, VACUUM FULL will--- fsync the newly rewritten test_io_vac_strategy instead of writing it to WAL.--- Writing it to WAL will result in the newly written relation pages being in--- shared buffers -- preventing us from testing BAS_VACUUM BufferAccessStrategy--- reads.-SET wal_skip_threshold = '1 kB';-SELECT sum(reuses) AS reuses, sum(reads) AS reads, sum(evictions) AS evictions- FROM pg_stat_io WHERE context = 'vacuum' \gset io_sum_vac_strategy_before_-CREATE TABLE test_io_vac_strategy(a int, b int) WITH (autovacuum_enabled = 'false');-INSERT INTO test_io_vac_strategy SELECT i, i from generate_series(1, 4500)i;--- Ensure that the next VACUUM will need to perform IO by rewriting the table--- first with VACUUM (FULL).-VACUUM (FULL) test_io_vac_strategy;--- Use the minimum BUFFER_USAGE_LIMIT to cause reuses or evictions with the--- smallest table possible.-VACUUM (PARALLEL 0, BUFFER_USAGE_LIMIT 128) test_io_vac_strategy;-SELECT pg_stat_force_next_flush();-SELECT sum(reuses) AS reuses, sum(reads) AS reads, sum(evictions) AS evictions- FROM pg_stat_io WHERE context = 'vacuum' \gset io_sum_vac_strategy_after_-SELECT :io_sum_vac_strategy_after_reads > :io_sum_vac_strategy_before_reads;-SELECT (:io_sum_vac_strategy_after_reuses + :io_sum_vac_strategy_after_evictions) >- (:io_sum_vac_strategy_before_reuses + :io_sum_vac_strategy_before_evictions);-RESET wal_skip_threshold;---- Test that extends done by a CTAS, which uses a BAS_BULKWRITE--- BufferAccessStrategy, are tracked in pg_stat_io.-SELECT sum(extends) AS io_sum_bulkwrite_strategy_extends_before- FROM pg_stat_io WHERE context = 'bulkwrite' \gset-CREATE TABLE test_io_bulkwrite_strategy AS SELECT i FROM generate_series(1,100)i;-SELECT pg_stat_force_next_flush();-SELECT sum(extends) AS io_sum_bulkwrite_strategy_extends_after- FROM pg_stat_io WHERE context = 'bulkwrite' \gset-SELECT :io_sum_bulkwrite_strategy_extends_after > :io_sum_bulkwrite_strategy_extends_before;-
-- Test IO stats reset
+-- Note: reuses is intentionally excluded from these totals. With the+-- BufferAccessStrategy ring buffers removed, IOOP_REUSE is never tracked, so+-- the reuses column is NULL for every row; summing it would make the whole+-- total NULL.
SELECT pg_stat_have_stats('io', 0, 0);
-SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS io_stats_pre_reset+SELECT sum(evictions) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS io_stats_pre_reset
FROM pg_stat_io \gset
-SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS my_io_stats_pre_reset+SELECT sum(evictions) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS my_io_stats_pre_reset
FROM pg_stat_get_backend_io(pg_backend_pid()) \gset
SELECT pg_stat_reset_shared('io');
-SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS io_stats_post_reset+SELECT sum(evictions) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS io_stats_post_reset
FROM pg_stat_io \gset
SELECT :io_stats_post_reset < :io_stats_pre_reset;
-SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS my_io_stats_post_reset+SELECT sum(evictions) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS my_io_stats_post_reset
FROM pg_stat_get_backend_io(pg_backend_pid()) \gset
-- pg_stat_reset_shared() did not reset backend IO stats
SELECT :my_io_stats_pre_reset <= :my_io_stats_post_reset;
-- but pg_stat_reset_backend_stats() does
SELECT pg_stat_reset_backend_stats(pg_backend_pid());
-SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS my_io_stats_post_backend_reset+SELECT sum(evictions) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(writebacks) + sum(hits) AS my_io_stats_post_backend_reset
FROM pg_stat_get_backend_io(pg_backend_pid()) \gset
SELECT :my_io_stats_pre_reset > :my_io_stats_post_backend_reset;
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sqlindex 247b8e23b23..506c4389765 100644--- a/src/test/regress/sql/vacuum.sql+++ b/src/test/regress/sql/vacuum.sql@@ -390,21 +390,6 @@ SELECT t.relfilenode = :toast_filenode AS is_same_toast_filenode
FROM pg_class c, pg_class t
WHERE c.reltoastrelid = t.oid AND c.relname = 'vac_option_tab';
--- BUFFER_USAGE_LIMIT option-VACUUM (BUFFER_USAGE_LIMIT '512 kB') vac_option_tab;-ANALYZE (BUFFER_USAGE_LIMIT '512 kB') vac_option_tab;--- try disabling the buffer usage limit-VACUUM (BUFFER_USAGE_LIMIT 0) vac_option_tab;-ANALYZE (BUFFER_USAGE_LIMIT 0) vac_option_tab;--- value exceeds max size error-VACUUM (BUFFER_USAGE_LIMIT 16777220) vac_option_tab;--- value is less than min size error-VACUUM (BUFFER_USAGE_LIMIT 120) vac_option_tab;--- integer overflow error-VACUUM (BUFFER_USAGE_LIMIT 10000000000) vac_option_tab;--- incompatible with VACUUM FULL error-VACUUM (BUFFER_USAGE_LIMIT '512 kB', FULL) vac_option_tab;-
-- SKIP_DATABASE_STATS option
VACUUM (SKIP_DATABASE_STATS) vactst;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.listindex 56c1f997f88..064a9878e72 100644--- a/src/tools/pgindent/typedefs.list+++ b/src/tools/pgindent/typedefs.list@@ -355,8 +355,6 @@ BtreeLevel
Bucket
BufFile
Buffer
-BufferAccessStrategy-BufferAccessStrategyType
BufferCacheOsPagesContext
BufferCacheOsPagesRec
BufferDesc
--
2.51.2
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: greg@burd.me, pgsql-hackers@lists.postgresql.org, tomas@vondra.me, nathandbossart@gmail.com, andres@anarazel.de
Subject: Re: [PATCH] Batched clock sweep to reduce cross-socket atomic contention
In-Reply-To: <96dc1db7-58a2-41ba-ae2e-bea86fa2fb3a@app.fastmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox