Provide ReadRecentBuffer() to re-pin buffers by ID.
authorThomas Munro <tmunro@postgresql.org>
Thu, 8 Apr 2021 05:48:37 +0000 (17:48 +1200)
committerThomas Munro <tmunro@postgresql.org>
Thu, 8 Apr 2021 05:50:25 +0000 (17:50 +1200)
If you know the ID of a buffer that recently held a block that you would
like to pin, this function can be used check if it's still there.  It
can be used to avoid a second lookup in the buffer mapping table after
PrefetchBuffer() reports a cache hit.

Reviewed-by: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/CA+hUKGJ4VJN8ttxScUFM8dOKX0BrBiboo5uz1cq=AovOddfHpA@mail.gmail.com

src/backend/storage/buffer/bufmgr.c
src/include/storage/bufmgr.h

index 852138f9c93a1b4bd5e613e40b0f813d0761f5eb..0c5b87864b90bac45c928c11c94f796cd4c11b21 100644 (file)
@@ -610,6 +610,84 @@ PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
        }
 }
 
+/*
+ * ReadRecentBuffer -- try to pin a block in a recently observed buffer
+ *
+ * Compared to ReadBuffer(), this avoids a buffer mapping lookup when it's
+ * successful.  Return true if the buffer is valid and still has the expected
+ * tag.  In that case, the buffer is pinned and the usage count is bumped.
+ */
+bool
+ReadRecentBuffer(RelFileNode rnode, ForkNumber forkNum, BlockNumber blockNum,
+                                Buffer recent_buffer)
+{
+       BufferDesc *bufHdr;
+       BufferTag       tag;
+       uint32          buf_state;
+       bool            have_private_ref;
+
+       Assert(BufferIsValid(recent_buffer));
+
+       ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
+       ReservePrivateRefCountEntry();
+       INIT_BUFFERTAG(tag, rnode, forkNum, blockNum);
+
+       if (BufferIsLocal(recent_buffer))
+       {
+               bufHdr = GetBufferDescriptor(-recent_buffer - 1);
+               buf_state = pg_atomic_read_u32(&bufHdr->state);
+
+               /* Is it still valid and holding the right tag? */
+               if ((buf_state & BM_VALID) && BUFFERTAGS_EQUAL(tag, bufHdr->tag))
+               {
+                       /* Bump local buffer's ref and usage counts. */
+                       ResourceOwnerRememberBuffer(CurrentResourceOwner, recent_buffer);
+                       LocalRefCount[-recent_buffer - 1]++;
+                       if (BUF_STATE_GET_USAGECOUNT(buf_state) < BM_MAX_USAGE_COUNT)
+                               pg_atomic_write_u32(&bufHdr->state,
+                                                                       buf_state + BUF_USAGECOUNT_ONE);
+
+                       return true;
+               }
+       }
+       else
+       {
+               bufHdr = GetBufferDescriptor(recent_buffer - 1);
+               have_private_ref = GetPrivateRefCount(recent_buffer) > 0;
+
+               /*
+                * Do we already have this buffer pinned with a private reference?  If
+                * so, it must be valid and it is safe to check the tag without
+                * locking.  If not, we have to lock the header first and then check.
+                */
+               if (have_private_ref)
+                       buf_state = pg_atomic_read_u32(&bufHdr->state);
+               else
+                       buf_state = LockBufHdr(bufHdr);
+
+               if ((buf_state & BM_VALID) && BUFFERTAGS_EQUAL(tag, bufHdr->tag))
+               {
+                       /*
+                        * It's now safe to pin the buffer.  We can't pin first and ask
+                        * questions later, because because it might confuse code paths
+                        * like InvalidateBuffer() if we pinned a random non-matching
+                        * buffer.
+                        */
+                       if (have_private_ref)
+                               PinBuffer(bufHdr, NULL);        /* bump pin count */
+                       else
+                               PinBuffer_Locked(bufHdr);       /* pin for first time */
+
+                       return true;
+               }
+
+               /* If we locked the header above, now unlock. */
+               if (!have_private_ref)
+                       UnlockBufHdr(bufHdr, buf_state);
+       }
+
+       return false;
+}
 
 /*
  * ReadBuffer -- a shorthand for ReadBufferExtended, for reading from main
index fb00fda6a7f21fbcc4f1a5337a64ff2643dafb6d..aa64fb42ec45d78bf21af2d48cd154fc6b209dd3 100644 (file)
@@ -176,6 +176,8 @@ extern PrefetchBufferResult PrefetchSharedBuffer(struct SMgrRelationData *smgr_r
                                                                                                 BlockNumber blockNum);
 extern PrefetchBufferResult PrefetchBuffer(Relation reln, ForkNumber forkNum,
                                                                                   BlockNumber blockNum);
+extern bool ReadRecentBuffer(RelFileNode rnode, 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,