[Sorry to those who received this message twice -- the first time got bounced by the list because of a defunct email address in the CC list.]
Here is a rebase of Palak's v2 patch. I didn't change anything except for the required resource manager API change, a pgindent run, and removal of a stray file, and there is still some feedback to be addressed before we can get this in, but I wanted to fix the bitrot and re-open this CF item because this is very useful work. It's essential for testing the prefetching-related stuff happening in various other threads, where you want to be able to get the buffer pool into various interesting states.
From 911776e07a767e8775f9e43948f4145f72d1de65 Mon Sep 17 00:00:00 2001 From: Palak <palakchaturvedi2...@gmail.com> Date: Fri, 30 Jun 2023 08:21:06 +0000 Subject: [PATCH v3] Invalidate Buffer By Bufnum --- contrib/pg_buffercache/Makefile | 2 +- contrib/pg_buffercache/meson.build | 1 + .../pg_buffercache--1.4--1.5.sql | 6 ++ contrib/pg_buffercache/pg_buffercache.control | 2 +- contrib/pg_buffercache/pg_buffercache_pages.c | 35 +++++++++ src/backend/storage/buffer/bufmgr.c | 74 +++++++++++++++++++ src/include/storage/bufmgr.h | 2 + 7 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 contrib/pg_buffercache/pg_buffercache--1.4--1.5.sql diff --git a/contrib/pg_buffercache/Makefile b/contrib/pg_buffercache/Makefile index d6b58d4da94..eae65ead9e5 100644 --- a/contrib/pg_buffercache/Makefile +++ b/contrib/pg_buffercache/Makefile @@ -8,7 +8,7 @@ OBJS = \ EXTENSION = pg_buffercache DATA = pg_buffercache--1.2.sql pg_buffercache--1.2--1.3.sql \ pg_buffercache--1.1--1.2.sql pg_buffercache--1.0--1.1.sql \ - pg_buffercache--1.3--1.4.sql + pg_buffercache--1.3--1.4.sql pg_buffercache--1.4--1.5.sql PGFILEDESC = "pg_buffercache - monitoring of shared buffer cache in real-time" REGRESS = pg_buffercache diff --git a/contrib/pg_buffercache/meson.build b/contrib/pg_buffercache/meson.build index c86e33cc958..1ca3452918b 100644 --- a/contrib/pg_buffercache/meson.build +++ b/contrib/pg_buffercache/meson.build @@ -22,6 +22,7 @@ install_data( 'pg_buffercache--1.2--1.3.sql', 'pg_buffercache--1.2.sql', 'pg_buffercache--1.3--1.4.sql', + 'pg_buffercache--1.4--1.5.sql', 'pg_buffercache.control', kwargs: contrib_data_args, ) diff --git a/contrib/pg_buffercache/pg_buffercache--1.4--1.5.sql b/contrib/pg_buffercache/pg_buffercache--1.4--1.5.sql new file mode 100644 index 00000000000..c96848c77d8 --- /dev/null +++ b/contrib/pg_buffercache/pg_buffercache--1.4--1.5.sql @@ -0,0 +1,6 @@ +\echo Use "ALTER EXTENSION pg_buffercache UPDATE TO '1.5'" to load this file. \quit + +CREATE FUNCTION pg_buffercache_invalidate(IN int, IN bool default true) +RETURNS bool +AS 'MODULE_PATHNAME', 'pg_buffercache_invalidate' +LANGUAGE C PARALLEL SAFE; diff --git a/contrib/pg_buffercache/pg_buffercache.control b/contrib/pg_buffercache/pg_buffercache.control index a82ae5f9bb5..5ee875f77dd 100644 --- a/contrib/pg_buffercache/pg_buffercache.control +++ b/contrib/pg_buffercache/pg_buffercache.control @@ -1,5 +1,5 @@ # pg_buffercache extension comment = 'examine the shared buffer cache' -default_version = '1.4' +default_version = '1.5' module_pathname = '$libdir/pg_buffercache' relocatable = true diff --git a/contrib/pg_buffercache/pg_buffercache_pages.c b/contrib/pg_buffercache/pg_buffercache_pages.c index 33167323653..485cd988dd4 100644 --- a/contrib/pg_buffercache/pg_buffercache_pages.c +++ b/contrib/pg_buffercache/pg_buffercache_pages.c @@ -64,6 +64,41 @@ PG_FUNCTION_INFO_V1(pg_buffercache_pages); PG_FUNCTION_INFO_V1(pg_buffercache_summary); PG_FUNCTION_INFO_V1(pg_buffercache_usage_counts); +PG_FUNCTION_INFO_V1(pg_buffercache_invalidate); +Datum +pg_buffercache_invalidate(PG_FUNCTION_ARGS) +{ + Buffer bufnum; + bool result; + bool force; + + if (PG_ARGISNULL(0)) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("buffernum cannot be NULL"))); + } + + bufnum = PG_GETARG_INT32(0); + if (bufnum <= 0 || bufnum > NBuffers) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("buffernum is not valid"))); + + } + + /* + * Check whether to force invalidate the dirty buffer. The default value + * of force is true. + */ + + force = PG_GETARG_BOOL(1); + + result = TryInvalidateBuffer(bufnum, force); + PG_RETURN_BOOL(result); +} + Datum pg_buffercache_pages(PG_FUNCTION_ARGS) { diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index bdf89bbc4dc..1d0f185edbc 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -5705,3 +5705,77 @@ ResOwnerPrintBufferPin(Datum res) { return DebugPrintBufferRefcount(DatumGetInt32(res)); } + +/* + * Try Invalidating a buffer using bufnum. + * If the buffer is invalid, the function returns false. + * The function checks for dirty buffer and flushes the dirty buffer before invalidating. + * If the buffer is still dirty it returns false. + */ +bool +TryInvalidateBuffer(Buffer bufnum, bool force) +{ + BufferDesc *bufHdr = GetBufferDescriptor(bufnum - 1); + uint32 buf_state; + + ReservePrivateRefCountEntry(); + + buf_state = LockBufHdr(bufHdr); + if ((buf_state & BM_VALID) == BM_VALID) + { + /* + * The buffer is pinned therefore cannot invalidate. + */ + if (BUF_STATE_GET_REFCOUNT(buf_state) > 0) + { + UnlockBufHdr(bufHdr, buf_state); + return false; + } + if ((buf_state & BM_DIRTY) == BM_DIRTY) + { + /* + * If the buffer is dirty and the user has not asked to clear the + * dirty buffer return false. Otherwise clear the dirty buffer. + */ + if (!force) + { + return false; + } + + /* + * Try once to flush the dirty buffer. + */ + ResourceOwnerEnlarge(CurrentResourceOwner); + PinBuffer_Locked(bufHdr); + LWLockAcquire(BufferDescriptorGetContentLock(bufHdr), LW_SHARED); + FlushBuffer(bufHdr, NULL, IOOBJECT_RELATION, IOCONTEXT_NORMAL); + LWLockRelease(BufferDescriptorGetContentLock(bufHdr)); + UnpinBuffer(bufHdr); + buf_state = LockBufHdr(bufHdr); + if (BUF_STATE_GET_REFCOUNT(buf_state) > 0) + { + UnlockBufHdr(bufHdr, buf_state); + return false; + } + + /* + * If its dirty again or not valid anymore give up. + */ + + if ((buf_state & (BM_DIRTY | BM_VALID)) != (BM_VALID)) + { + UnlockBufHdr(bufHdr, buf_state); + return false; + } + + } + + InvalidateBuffer(bufHdr); + return true; + } + else + { + UnlockBufHdr(bufHdr, buf_state); + return false; + } +} diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h index d51d46d3353..a7d2dd2de0a 100644 --- a/src/include/storage/bufmgr.h +++ b/src/include/storage/bufmgr.h @@ -250,6 +250,8 @@ extern bool HoldingBufferPinThatDelaysRecovery(void); extern bool BgBufferSync(struct WritebackContext *wb_context); +extern bool TryInvalidateBuffer(Buffer bufnum, bool force); + /* in buf_init.c */ extern void InitBufferPool(void); extern Size BufferShmemSize(void); -- 2.39.2