From 9b7e523bd4960d42847b849cde226f7d97deb1b9 Mon Sep 17 00:00:00 2001 From: Palak Date: Fri, 30 Jun 2023 08:21:06 +0000 Subject: [PATCH v4] Invalidate Buffer By Bufnum --- contrib/pg_buffercache/Makefile | 2 +- .../expected/pg_buffercache.out | 31 ++++++++ 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 | 16 ++++ contrib/pg_buffercache/sql/pg_buffercache.sql | 9 +++ src/backend/storage/buffer/bufmgr.c | 74 +++++++++++++++++++ src/include/storage/bufmgr.h | 2 + 9 files changed, 141 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 d6b58d4da9..eae65ead9e 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/expected/pg_buffercache.out b/contrib/pg_buffercache/expected/pg_buffercache.out index b745dc69ea..75d6f41164 100644 --- a/contrib/pg_buffercache/expected/pg_buffercache.out +++ b/contrib/pg_buffercache/expected/pg_buffercache.out @@ -55,3 +55,34 @@ SELECT count(*) > 0 FROM pg_buffercache_usage_counts(); t (1 row) +-- Check pg_buffercache_invalidate +SELECT pg_buffercache_invalidate(NULL, NULL); + pg_buffercache_invalidate +--------------------------- + +(1 row) + +SELECT pg_buffercache_invalidate(1, NULL); + pg_buffercache_invalidate +--------------------------- + +(1 row) + +SELECT pg_buffercache_invalidate(NULL, false); + pg_buffercache_invalidate +--------------------------- + +(1 row) + +SELECT pg_buffercache_invalidate(NULL); + pg_buffercache_invalidate +--------------------------- + +(1 row) + +SELECT pg_buffercache_invalidate(1); + pg_buffercache_invalidate +--------------------------- + t +(1 row) + diff --git a/contrib/pg_buffercache/meson.build b/contrib/pg_buffercache/meson.build index c86e33cc95..1ca3452918 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 0000000000..c582260f50 --- /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 STRICT; diff --git a/contrib/pg_buffercache/pg_buffercache.control b/contrib/pg_buffercache/pg_buffercache.control index a82ae5f9bb..5ee875f77d 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 3316732365..b010f13c98 100644 --- a/contrib/pg_buffercache/pg_buffercache_pages.c +++ b/contrib/pg_buffercache/pg_buffercache_pages.c @@ -63,6 +63,7 @@ typedef struct 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_pages(PG_FUNCTION_ARGS) @@ -347,3 +348,18 @@ pg_buffercache_usage_counts(PG_FUNCTION_ARGS) return (Datum) 0; } + +/* + * Perform buffer invaidation. + */ +Datum +pg_buffercache_invalidate(PG_FUNCTION_ARGS) +{ + Buffer buf = PG_GETARG_INT32(0); + bool force = PG_GETARG_BOOL(1); + + if (buf > NBuffers || buf < -NLocBuffer || BufferIsInvalid(buf)) + elog(ERROR, "bad buffer ID: %d", buf); + + PG_RETURN_BOOL(TryInvalidateBuffer(buf, force)); +} diff --git a/contrib/pg_buffercache/sql/pg_buffercache.sql b/contrib/pg_buffercache/sql/pg_buffercache.sql index 944fbb1bea..f6671b96cc 100644 --- a/contrib/pg_buffercache/sql/pg_buffercache.sql +++ b/contrib/pg_buffercache/sql/pg_buffercache.sql @@ -26,3 +26,12 @@ SET ROLE pg_monitor; SELECT count(*) > 0 FROM pg_buffercache; SELECT buffers_used + buffers_unused > 0 FROM pg_buffercache_summary(); SELECT count(*) > 0 FROM pg_buffercache_usage_counts(); + +-- Check pg_buffercache_invalidate +SELECT pg_buffercache_invalidate(NULL, NULL); +SELECT pg_buffercache_invalidate(1, NULL); +SELECT pg_buffercache_invalidate(NULL, false); +SELECT pg_buffercache_invalidate(NULL); + + +SELECT pg_buffercache_invalidate(1); diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index f0f8d4259c..4db993d1a6 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -5704,3 +5704,77 @@ ResOwnerPrintBufferPin(Datum res) { return DebugPrintBufferRefcount(DatumGetInt32(res)); } + +/* + * Try Invalidating a shared buffer. + * + * 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 buf, bool force) +{ + BufferDesc *desc; + uint32 buf_state; + + ReservePrivateRefCountEntry(); + + desc = BufferIsLocal(buf) ? GetLocalBufferDescriptor(-buf - 1) : + GetBufferDescriptor(buf - 1); + + buf_state = LockBufHdr(desc); + if ((buf_state & BM_VALID) != BM_VALID) + { + UnlockBufHdr(desc, buf_state); + return false; + } + + /* The buffer is pinned therefore cannot invalidate. */ + if (BUF_STATE_GET_REFCOUNT(buf_state) > 0) + { + UnlockBufHdr(desc, 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) + { + UnlockBufHdr(desc, buf_state); + return false; + } + + /* Try once to flush the dirty buffer. */ + ResourceOwnerEnlarge(CurrentResourceOwner); + PinBuffer_Locked(desc); + LWLockAcquire(BufferDescriptorGetContentLock(desc), LW_SHARED); + FlushBuffer(desc, NULL, IOOBJECT_RELATION, IOCONTEXT_NORMAL); + LWLockRelease(BufferDescriptorGetContentLock(desc)); + + UnpinBuffer(desc); + + /* If buffer is still used by someone, return false. */ + buf_state = LockBufHdr(desc); + if (BUF_STATE_GET_REFCOUNT(buf_state) > 0) + { + UnlockBufHdr(desc, buf_state); + return false; + } + + /* If its dirty again or not valid anymore give up. */ + if ((buf_state & (BM_DIRTY | BM_VALID)) != (BM_VALID)) + { + UnlockBufHdr(desc, buf_state); + return false; + } + + } + + InvalidateBuffer(desc); /* releases spinlock */ + return true; +} diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h index d51d46d335..a0069b0e0e 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 buf, bool force); + /* in buf_init.c */ extern void InitBufferPool(void); extern Size BufferShmemSize(void); -- 2.43.0