Allow BufferAccessStrategy to limit pin count.
authorThomas Munro <tmunro@postgresql.org>
Sat, 6 Apr 2024 09:58:48 +0000 (22:58 +1300)
committerThomas Munro <tmunro@postgresql.org>
Sat, 6 Apr 2024 10:11:45 +0000 (23:11 +1300)
While pinning extra buffers to look ahead, users of strategies are in
danger of using too many buffers.  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.

Reviewed-by: Andres Freund <andres@anarazel.de>
Reviewed-by: Melanie Plageman <melanieplageman@gmail.com>
Discussion: https://postgr.es/m/CAAKRu_aJXnqsyZt6HwFLnxYEBgE17oypkxbKbT1t1geE_wvH2Q%40mail.gmail.com

src/backend/storage/aio/read_stream.c
src/backend/storage/buffer/freelist.c
src/include/storage/bufmgr.h

index b9e11a28312d21ff421351635cf96b24bee65404..9a70a81f7aebbc06673ca081a3987f97808873a3 100644 (file)
@@ -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);
index 3611357fa30b6e31e1851b019d50b25abb81c8be..de2ef1dd5e663f3c9f3fd6fde12e63b6509aa024 100644 (file)
@@ -629,6 +629,48 @@ GetAccessStrategyBufferCount(BufferAccessStrategy strategy)
    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
  *
index f380f9d9a6c92ff511d359eecb153f99f765492b..07ba1a605024eb601e3cd508062ee88d2e39497e 100644 (file)
@@ -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);