From 80165aa62e3ba54ac04f6465c9a39380c2932c25 Mon Sep 17 00:00:00 2001 From: Palak Date: Fri, 30 Jun 2023 08:21:06 +0000 Subject: [PATCH v2] 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 | 34 ++++ src/backend/storage/buffer/bufmgr.c | 71 +++++++ src/include/storage/bufmgr.h | 3 + v1-0001-Invalidate-Buffer-By-Bufnum.patch | 191 ++++++++++++++++++ 8 files changed, 308 insertions(+), 2 deletions(-) create mode 100644 contrib/pg_buffercache/pg_buffercache--1.4--1.5.sql create mode 100644 v1-0001-Invalidate-Buffer-By-Bufnum.patch 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/meson.build b/contrib/pg_buffercache/meson.build index c51edf37d1..748463bc19 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..c96848c77d --- /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 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..93ee4ef724 100644 --- a/contrib/pg_buffercache/pg_buffercache_pages.c +++ b/contrib/pg_buffercache/pg_buffercache_pages.c @@ -64,6 +64,40 @@ 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 3c59bbd04e..5b874a83ae 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -5592,3 +5592,74 @@ TestForOldSnapshot_impl(Snapshot snapshot, Relation relation) (errcode(ERRCODE_SNAPSHOT_TOO_OLD), errmsg("snapshot too old"))); } + +/* + * 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. + */ + ResourceOwnerEnlargeBuffers(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 0f5fb6be00..1fdb7ae3f0 100644 --- a/src/include/storage/bufmgr.h +++ b/src/include/storage/bufmgr.h @@ -252,6 +252,9 @@ extern bool BgBufferSync(struct WritebackContext *wb_context); extern void TestForOldSnapshot_impl(Snapshot snapshot, Relation relation); + +extern bool TryInvalidateBuffer(Buffer bufnum, bool force); + /* in buf_init.c */ extern void InitBufferPool(void); extern Size BufferShmemSize(void); diff --git a/v1-0001-Invalidate-Buffer-By-Bufnum.patch b/v1-0001-Invalidate-Buffer-By-Bufnum.patch new file mode 100644 index 0000000000..7ee605f88e --- /dev/null +++ b/v1-0001-Invalidate-Buffer-By-Bufnum.patch @@ -0,0 +1,191 @@ +From 02ea352d84ed87e156617de8b8020811680cb412 Mon Sep 17 00:00:00 2001 +From: Palak +Date: Fri, 30 Jun 2023 08:21:06 +0000 +Subject: [PATCH v1] 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 | 27 ++++++++ + src/backend/storage/buffer/bufmgr.c | 64 +++++++++++++++++++ + src/include/storage/bufmgr.h | 3 + + 7 files changed, 103 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/meson.build b/contrib/pg_buffercache/meson.build +index c51edf37d1..748463bc19 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..c7e47456d7 +--- /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) ++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 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..6dd02fe6af 100644 +--- a/contrib/pg_buffercache/pg_buffercache_pages.c ++++ b/contrib/pg_buffercache/pg_buffercache_pages.c +@@ -64,6 +64,33 @@ 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; ++ ++ 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"))); ++ ++ } ++ ++ result = TryInvalidateBuffer(bufnum); ++ 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 3c59bbd04e..376afcf996 100644 +--- a/src/backend/storage/buffer/bufmgr.c ++++ b/src/backend/storage/buffer/bufmgr.c +@@ -5592,3 +5592,67 @@ TestForOldSnapshot_impl(Snapshot snapshot, Relation relation) + (errcode(ERRCODE_SNAPSHOT_TOO_OLD), + errmsg("snapshot too old"))); + } ++ ++/* ++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) ++{ ++ 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) ++ { ++ /* ++ * Try once to flush the dirty buffer. ++ */ ++ 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 0f5fb6be00..4ba3d9089b 100644 +--- a/src/include/storage/bufmgr.h ++++ b/src/include/storage/bufmgr.h +@@ -252,6 +252,9 @@ extern bool BgBufferSync(struct WritebackContext *wb_context); + + extern void TestForOldSnapshot_impl(Snapshot snapshot, Relation relation); + ++ ++extern bool TryInvalidateBuffer(Buffer bufnum); ++ + /* in buf_init.c */ + extern void InitBufferPool(void); + extern Size BufferShmemSize(void); +-- +2.25.1 + -- 2.25.1