From 6dea2983abf8d608c34e02351d70694de99f25f2 Mon Sep 17 00:00:00 2001 From: Thomas Munro Date: Thu, 4 Apr 2024 20:31:26 +1300 Subject: [PATCH v2 1/2] Allow BufferAccessStrategy to limit pin count. When pinning extra buffers to look ahead, users of a strategy are in danger of pinning a lot of the buffers in the ring, or even more than the ring size. 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 how many buffers to pin at once. Discussion: https://postgr.es/m/CAAKRu_aJXnqsyZt6HwFLnxYEBgE17oypkxbKbT1t1geE_wvH2Q%40mail.gmail.com --- src/backend/storage/aio/read_stream.c | 5 ++++ src/backend/storage/buffer/freelist.c | 35 +++++++++++++++++++++++++++ src/include/storage/bufmgr.h | 1 + 3 files changed, 41 insertions(+) diff --git a/src/backend/storage/aio/read_stream.c b/src/backend/storage/aio/read_stream.c index 4f21262ff5..eab87f6f73 100644 --- a/src/backend/storage/aio/read_stream.c +++ b/src/backend/storage/aio/read_stream.c @@ -419,6 +419,7 @@ read_stream_begin_relation(int flags, size_t size; int16 queue_size; int16 max_ios; + int strategy_pin_limit; uint32 max_pinned_buffers; Oid tablespace_id; SMgrRelation smgr; @@ -460,6 +461,10 @@ read_stream_begin_relation(int flags, max_pinned_buffers = Min(max_pinned_buffers, PG_INT16_MAX - io_combine_limit - 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); + /* Don't allow this backend to pin more than its share of buffers. */ if (SmgrIsTemp(smgr)) LimitAdditionalLocalPins(&max_pinned_buffers); diff --git a/src/backend/storage/buffer/freelist.c b/src/backend/storage/buffer/freelist.c index 3611357fa3..c69590d6d8 100644 --- a/src/backend/storage/buffer/freelist.c +++ b/src/backend/storage/buffer/freelist.c @@ -629,6 +629,41 @@ GetAccessStrategyBufferCount(BufferAccessStrategy strategy) return strategy->nbuffers; } +/* + * GetAccessStrategyPinLimit -- get cap of number of buffers that can be pinned + * + * Strategies can specify the maximum number of buffers that a user should pin + * at once when performing look-ahead. 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 call 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 * diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h index f380f9d9a6..07ba1a6050 100644 --- a/src/include/storage/bufmgr.h +++ b/src/include/storage/bufmgr.h @@ -318,6 +318,7 @@ 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); -- 2.39.3 (Apple Git-146)