diff --git a/contrib/amcheck/expected/check_heap.out b/contrib/amcheck/expected/check_heap.out index 979e5e84e723d..569b0202f1cf1 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.sql index 1745bae634e56..61f710dbedafd 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.c index fa06689ed5b29..ef2e64475c661 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.c index 20ff58aa78259..e4336eb5fc2a4 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.c index 3ef2d66f82675..18cd8928fdd85 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.c index 1a0e42021ec1e..ed62d5c04e6e0 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.c index 5111cdc6dd62f..6c1ec15c59ce9 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.c index 6beb1c20ebb0d..08d7705e36500 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.c index d136593edb267..bbe8edf916113 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_buffercache/pg_buffercache_pages.c b/contrib/pg_buffercache/pg_buffercache_pages.c index 510455998aa74..b94e6f6fda80b 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/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c index deb4c2671b5d8..33ad88ea8b12c 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.c index c2716086693d9..716a6754a7e20 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.c index dfab0b64cf50b..ccb829a09eb89 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.c index 21e0b50fb4bd4..8e17d48991e47 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.c index 8951ad0aac472..a0922b386a00a 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.c index 6a7f8cb4a7ca5..90f7938e95313 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.sgml index 0848c18d329e3..7cb8839245c7d 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -2107,36 +2107,6 @@ include_dir 'conf.d' - - - vacuum_buffer_usage_limit (integer) - - vacuum_buffer_usage_limit configuration parameter - - - - - Specifies the size of the - Buffer Access Strategy - used by the VACUUM and ANALYZE - commands. A setting of 0 will allow the operation - to use any number of shared_buffers. Otherwise - valid sizes range from 128 kB to - 16 GB. If the specified size would exceed 1/8 the - size of shared_buffers, the size is silently capped - to that value. The default value is 2MB. 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 - and - when passing the option. Higher - settings can allow VACUUM and - ANALYZE to run more quickly, but having too large a - setting may cause too many other useful pages to be evicted from - shared buffers. - - - - logical_decoding_work_mem (integer) diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index d1a20d001e9c8..18d17540430db 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 init. - - - vacuum: 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 context - normal. - - - - - bulkread: Certain large read I/O operations - done outside of shared buffers, for example, a sequential scan of a - large table. - - - - - bulkwrite: Certain large write I/O operations - done outside of shared buffers, such as COPY. - - @@ -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. - In context normal, this counts - the number of times a block was evicted from a buffer and replaced with - another block. In contexts - bulkwrite, bulkread, and - vacuum, 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. @@ -3197,10 +3170,9 @@ description | Waiting for a newly initialized WAL file to reach durable storage reuses bigint - 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 - bulkread, bulkwrite, or - vacuum contexts. + 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. diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgml index ec81f00fecf87..7f5159111d888 100644 --- a/doc/src/sgml/ref/analyze.sgml +++ b/doc/src/sgml/ref/analyze.sgml @@ -27,7 +27,6 @@ ANALYZE [ ( option [, ...] ) ] [ boolean ] SKIP_LOCKED [ boolean ] - BUFFER_USAGE_LIMIT size and table_and_columns is: @@ -88,26 +87,6 @@ ANALYZE [ ( option [, ...] ) ] [ - - BUFFER_USAGE_LIMIT - - - Specifies the - Buffer Access Strategy - ring buffer size for ANALYZE. This size is used to - calculate the number of shared buffers which will be reused as part of - this strategy. 0 disables use of a - Buffer Access Strategy. When this option is not - specified, ANALYZE uses the value from - . Higher settings can - allow ANALYZE 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 128 kB and the - maximum value is 16 GB. - - - - boolean diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index 38ee973ea05d6..d44a49f8efe3f 100644 --- a/doc/src/sgml/ref/vacuum.sgml +++ b/doc/src/sgml/ref/vacuum.sgml @@ -37,7 +37,6 @@ VACUUM [ ( option [, ...] ) ] [ integer SKIP_DATABASE_STATS [ boolean ] ONLY_DATABASE_STATS [ boolean ] - BUFFER_USAGE_LIMIT size FULL [ boolean ] and table_and_columns is: @@ -309,30 +308,6 @@ VACUUM [ ( option [, ...] ) ] [ - - BUFFER_USAGE_LIMIT - - - Specifies the - Buffer Access Strategy - ring buffer size for VACUUM. This size is used to - calculate the number of shared buffers which will be reused as part of - this strategy. 0 disables use of a - Buffer Access Strategy. If - is also specified, the value is used - for both the vacuum and analyze stages. This option can't be used with - the option except if is - also specified. When this option is not specified, - VACUUM uses the value from - . Higher settings can - allow VACUUM 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 128 kB and the - maximum value is 16 GB. - - - - FULL diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e098c..5d6539f548357 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.c index 233355cb2d5d1..01c4104ac88dd 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.c index cb9ed3b563c6f..32ad65cc95ff2 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.c index e7cba81d47709..2d7394aedd5d3 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.c index 840543eb6642b..86538de39d628 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.c index 8565e225be7fd..ae494742a4b8f 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.c index 0f58f61879fb0..170174f62fd56 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.c index 686a04180546b..c366ed35ea876 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 @@ gistvacuumpage(GistVacState *vstate, Buffer buffer) /* 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.c index 8d8cd30dc386b..1bbb7224bfcba 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 @@ hashbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, 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.c index dbc57ef958c0d..5b35fed33f9e1 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 @@ _hash_squeezebucket(Relation rel, /* 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 @@ _hash_squeezebucket(Relation rel, /* 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 @@ _hash_squeezebucket(Relation rel, 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.c index 8099b0d021f05..bdf1a255f1fbf 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 @@ _hash_expandtable(Relation rel, Buffer metabuf) /* 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.c index 9cdc221675b2d..48e7c421bf497 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.c index bf87430cf0176..d14658d7218d4 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.c index e96e0f77d9264..9bf7abc247f97 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.c index 39395aed0d592..8bc8e470af39f 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.c index 4fd470702aae7..b2612b1f71063 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.c index 109017d6b5296..7b5fc16d75c46 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.c index 3df2c752eadef..379a2d37def59 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 @@ btvacuumpage(BTVacState *vstate, Buffer buf) * 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.c index f2ee333f60d84..785e385aab635 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.c index c461f8dc02d1b..9f644edaf8242 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.c index f2e10b82b7d3e..0c16b7fef0f4a 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.c index d8c179c5dccb2..e5be49f8b5db9 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.c index 81bba4beac7cb..b0a7b8a33cd02 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.c index f66e80b757cbf..9b117a53124c3 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.c index f0819d15ab701..6f65d48b6bd5a 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.c index 02883fe34a485..d8c15a8b1bcad 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.c index 551667650ba63..ac1ab9482114f 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.c index 38539a6fd3d4e..66acd27bd488d 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.c index 41cefcfde54fe..8b4b835db9bad 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.c index 45abf48768afd..e021e798805ab 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 @@ do_autovacuum(void) #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.c index 73dc539836b01..b6612f1e51794 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.c index a318539e56cbe..08d344f063d5d 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/README index b332e002ba13b..58df6bd011aa1 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.c index 3908529872a31..cc2fab959df5b 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 @@ -611,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, @@ -622,19 +622,18 @@ 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); 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); @@ -644,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); @@ -653,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, @@ -857,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)) { @@ -873,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); } /* @@ -918,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; @@ -934,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; } @@ -953,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); } /* @@ -969,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; @@ -1001,7 +995,6 @@ ExtendBufferedRel(BufferManagerRelation bmr, BlockNumber ExtendBufferedRelBy(BufferManagerRelation bmr, ForkNumber fork, - BufferAccessStrategy strategy, uint32 flags, uint32 extend_by, Buffer *buffers, @@ -1014,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); } @@ -1030,7 +1023,6 @@ ExtendBufferedRelBy(BufferManagerRelation bmr, Buffer ExtendBufferedRelTo(BufferManagerRelation bmr, ForkNumber fork, - BufferAccessStrategy strategy, uint32 flags, BlockNumber extend_to, ReadBufferMode mode) @@ -1096,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); @@ -1122,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; @@ -1225,7 +1217,6 @@ PinBufferForBlock(Relation rel, char persistence, ForkNumber forkNum, BlockNumber blockNum, - BufferAccessStrategy strategy, IOObject io_object, IOContext io_context, bool *foundPtr) @@ -1249,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); @@ -1275,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; @@ -1312,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) @@ -1334,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; @@ -1357,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, @@ -1398,7 +1387,7 @@ StartReadBuffersImpl(ReadBuffersOperation *operation, } else { - io_context = IOContextForStrategy(operation->strategy); + io_context = IOCONTEXT_NORMAL; io_object = IOOBJECT_RELATION; } @@ -1449,7 +1438,6 @@ StartReadBuffersImpl(ReadBuffersOperation *operation, operation->persistence, operation->forknum, blockNum + i, - operation->strategy, io_object, io_context, &found); } @@ -1770,7 +1758,7 @@ WaitReadBuffers(ReadBuffersOperation *operation) } else { - io_context = IOContextForStrategy(operation->strategy); + io_context = IOCONTEXT_NORMAL; io_object = IOOBJECT_RELATION; } @@ -1960,7 +1948,7 @@ AsyncReadBuffers(ReadBuffersOperation *operation, int *nblocks_progress) } else { - io_context = IOContextForStrategy(operation->strategy); + io_context = IOCONTEXT_NORMAL; io_object = IOOBJECT_RELATION; } @@ -2179,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 */ @@ -2234,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); @@ -2265,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); /* @@ -2296,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); @@ -2333,7 +2315,13 @@ 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; @@ -2545,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 @@ -2566,7 +2553,7 @@ GetVictimBuffer(BufferAccessStrategy strategy, IOContext io_context) * 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); /* @@ -2610,26 +2597,6 @@ GetVictimBuffer(BufferAccessStrategy strategy, IOContext io_context) 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); @@ -2642,23 +2609,15 @@ GetVictimBuffer(BufferAccessStrategy strategy, IOContext io_context) 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); } /* @@ -2750,7 +2709,6 @@ LimitAdditionalPins(uint32 *additional_pins) static BlockNumber ExtendBufferedRelCommon(BufferManagerRelation bmr, ForkNumber fork, - BufferAccessStrategy strategy, uint32 flags, uint32 extend_by, BlockNumber extend_upto, @@ -2785,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; @@ -2808,7 +2766,6 @@ ExtendBufferedRelCommon(BufferManagerRelation bmr, static BlockNumber ExtendBufferedRelShared(BufferManagerRelation bmr, ForkNumber fork, - BufferAccessStrategy strategy, uint32 flags, uint32 extend_by, BlockNumber extend_upto, @@ -2816,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); @@ -2835,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 */ @@ -2953,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); @@ -3002,7 +2959,12 @@ 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; @@ -3269,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. * @@ -3292,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); @@ -3332,21 +3289,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)) @@ -3394,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). * @@ -3785,7 +3738,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 +3829,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 +3855,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 +4030,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 +4054,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 +4067,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 +4102,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 +4121,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 +4162,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; } @@ -4604,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); @@ -5380,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; @@ -5409,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; @@ -5424,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, @@ -5444,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); @@ -5465,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.c index fdb5bad7910a2..ee62b33668003 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)))) @@ -48,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. @@ -67,101 +78,98 @@ const ShmemCallbacks StrategyCtlShmemCallbacks = { }; /* - * 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. + * 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. */ -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); +static uint32 MyBatchPos = 0; +static uint32 MyBatchEnd = 0; /* * 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 = StrategyControl->batchSize; - 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; } @@ -172,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(). * @@ -181,27 +187,12 @@ ClockSweepTick(void) * before returning. */ BufferDesc * -StrategyGetBuffer(BufferAccessStrategy strategy, uint64 *buf_state, bool *from_ring) +StrategyGetBuffer(uint64 *buf_state) { BufferDesc *buf; int bgwprocno; int trycounter; - - *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; - } - } + bool force_cool; /* * If asked, we need to waken the bgwriter. Since we don't want to rely on @@ -231,17 +222,36 @@ 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); - /* 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()); @@ -254,25 +264,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; } @@ -283,28 +278,62 @@ 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, local_buf_state)) { /* Found a usable buffer */ - if (strategy != NULL) - AddBufferToRing(strategy, buf); *buf_state = local_buf_state; TrackNewBufferPin(BufferDescriptorGetBuffer(buf)); @@ -313,6 +342,22 @@ 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; + } } } @@ -408,363 +453,27 @@ StrategyCtlShmemInit(void *arg) /* No pending notification */ StrategyControl->bgwprocno = -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. + * Decide whether to batch the clock sweep. * - * 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. - * - * 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. - */ - if (BUF_STATE_GET_REFCOUNT(local_buf_state) != 0 - || BUF_STATE_GET_USAGECOUNT(local_buf_state) > 1) - 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. + * 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.) */ - strategy->buffers[strategy->current] = InvalidBuffer; - - return true; + if (pg_numa_init() != -1 && + pg_numa_get_max_node() >= 1) + StrategyControl->batchSize = Min(PG_CACHE_LINE_SIZE / (uint32) sizeof(uint32), + (uint32) NBuffers); + else + StrategyControl->batchSize = 1; } diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 4870c8e13d010..21c08091c490b 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/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c index 006edab9d77df..29cb2d70f66e4 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.c index 718c1cfc0f9df..3c3e35ab8a86f 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.c index 38bae7b15d2ed..d66a23749d5a9 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.c index bbd28d14d9948..555c04272f532 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.dat index d421cdbde76da..b1ea81f536c99 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.sample index 7958653077b16..cb1eb8ef3ffbc 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.c index f8158ca6a78fa..f78e57ff2c3e2 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.c index 855a5754c98c1..078a3bbb51681 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.h index 5a491db2526d7..83a2469a6aaf8 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.h index 68bfe405db3a0..bc87f6f7cc0ed 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.h index a8702f0e5ea13..7c9268fe95370 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.h index 5176478c29583..0967b659794c2 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.h index 60cfc375fd523..63bfcc884eea1 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.h index f2c36696bcad0..83ba0f3ca730d 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.h index 956d9cea36da6..14a9b530bcf87 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.h index 7170a4bff9896..c5d582b152b60 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.h index 58a44857f1311..68b4e5093a2bc 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.h index b21445522b180..fa942a64f05a5 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.h index e4ff5619b79ca..3b44ad3e0b67b 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. @@ -585,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.h index 6837b35fc6d0b..36023792f3611 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.h index 48995c6d534bd..9e5fbbed9e866 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.h index 307f4fbaefe08..3f28558d47c6d 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.c index 6270775af7ca2..8d7b2a728d680 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.out index 03cbc1cdef59e..beb876c48b4fb 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.out index d4696bc332557..85d06907cec0f 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.sql index 4c265d1245c72..4eb48114e80fa 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.sql index 247b8e23b2357..506c438976552 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.list index 56c1f997f88b1..064a9878e7276 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