From 8d6f0abb79e5986a77d2ba90a8e8b9d7b2be36af Mon Sep 17 00:00:00 2001 From: Palak Date: Fri, 30 Jun 2023 08:21:06 +0000 Subject: [PATCH v5] Add pg_buffercache_evict() function for testing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When testing buffer pool logic, it is very useful to be able to evict arbitrary pages. It's a pretty crude tool but can be used in SQL queries on the pg_buffercache view to express a wide range of buffer invalidation scenarios. Superuser-only. Author: Palak Chaturvedi Author: Thomas Munro (small adjustments) Reviewed-by: Nitin Jadhav Reviewed-by: Andres Freund Reviewed-by: Cary Huang Reviewed-by: Cédric Villemain Reviewed-by: Jim Nasby Reviewed-by: Maxim Orlov Reviewed-by: Thomas Munro Discussion: https://postgr.es/m/CALfch19pW48ZwWzUoRSpsaV9hqt0UPyaBPC4bOZ4W+c7FF566A@mail.gmail.com --- 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 | 20 +++++++ doc/src/sgml/pgbuffercache.sgml | 37 ++++++++++-- src/backend/storage/buffer/bufmgr.c | 57 +++++++++++++++++++ src/include/storage/bufmgr.h | 2 + 8 files changed, 119 insertions(+), 8 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 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..0fb18ff786 --- /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_evict(IN int) +RETURNS bool +AS 'MODULE_PATHNAME', 'pg_buffercache_evict' +LANGUAGE C PARALLEL SAFE VOLATILE 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..fc33dffeeb 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_evict); Datum pg_buffercache_pages(PG_FUNCTION_ARGS) @@ -347,3 +348,22 @@ pg_buffercache_usage_counts(PG_FUNCTION_ARGS) return (Datum) 0; } + +/* + * Try to evict a block from a shared buffer. + */ +Datum +pg_buffercache_evict(PG_FUNCTION_ARGS) +{ + Buffer buf = PG_GETARG_INT32(0); + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use pg_buffercache_evict function"))); + + if (buf > NBuffers || buf < -NLocBuffer || BufferIsInvalid(buf)) + elog(ERROR, "bad buffer ID: %d", buf); + + PG_RETURN_BOOL(EvictBuffer(buf)); +} diff --git a/doc/src/sgml/pgbuffercache.sgml b/doc/src/sgml/pgbuffercache.sgml index afe2d97834..0aaffd5786 100644 --- a/doc/src/sgml/pgbuffercache.sgml +++ b/doc/src/sgml/pgbuffercache.sgml @@ -21,11 +21,16 @@ pg_buffercache_summary + + pg_buffercache_evict + + This module provides the pg_buffercache_pages() function (wrapped in the pg_buffercache view), - the pg_buffercache_summary() function, and the - pg_buffercache_usage_counts() function. + the pg_buffercache_summary() function, the + pg_buffercache_usage_counts() function and + the pg_buffercache_evict() function. @@ -47,9 +52,15 @@ - By default, use is restricted to superusers and roles with privileges of the - pg_monitor role. Access may be granted to others - using GRANT. + By default, use of the above functions is restricted to superusers and roles + with privileges of the pg_monitor role. Access may be + granted to others using GRANT. + + + + The pg_buffercache_evict() function allows a block to + be evicted from the buffer pool given a buffer identifier. Use of this + function is restricted to superusers only. @@ -351,7 +362,21 @@ - + + The <structname>pg_buffercache_evict</structname> Function + + The pg_buffercache_evict() takes a buffer identifier + from the bufferid of the + pg_buffercache view. It returns true on success, and + false if the buffer wasn't valid, couldn't be evicted because it was pinned, or + became dirty again after an attempt to write it out. The result is + immediately out of date upon return, as the buffer might become valid again at + any time due to concurrent activity. The function is intended for developer + testing. + + + + Sample Output diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 06e9ffd2b0..673d596b26 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -6003,3 +6003,60 @@ ResOwnerPrintBufferPin(Datum res) { return DebugPrintBufferRefcount(DatumGetInt32(res)); } + +/* + * Try to evict a shared a buffer. + * + * This function is intended for testing/development use only. By the time it + * returns, the buffer may again be occupied, perhaps even by the same block. + * + * Returns true if the buffer was valid and it has now been made invalid. + * Returns false if the wasn't valid, or it couldn't be evicted due to a pin, + * or if the buffer becomes dirty again while we're trying to write it out. + */ +bool +EvictBuffer(Buffer buf) +{ + BufferDesc *desc; + uint32 buf_state; + bool result; + + /* Make sure we can pin the buffer if it turned out require writing. */ + ReservePrivateRefCountEntry(); + + Assert(!BufferIsLocal(buf)); + desc = GetBufferDescriptor(buf - 1); + + /* Lock the header and check if it's valid. */ + buf_state = LockBufHdr(desc); + if ((buf_state & BM_VALID) == 0) + { + UnlockBufHdr(desc, buf_state); + return false; + } + + /* Check that it's not pinned by someone else. */ + if (BUF_STATE_GET_REFCOUNT(buf_state) > 0) + { + UnlockBufHdr(desc, buf_state); + return false; + } + + PinBuffer_Locked(desc); /* releases spinlock */ + + /* If it was dirty, try to clean it once. */ + if (buf_state & BM_DIRTY) + { + ResourceOwnerEnlarge(CurrentResourceOwner); + LWLockAcquire(BufferDescriptorGetContentLock(desc), LW_SHARED); + FlushBuffer(desc, NULL, IOOBJECT_RELATION, IOCONTEXT_NORMAL); + LWLockRelease(BufferDescriptorGetContentLock(desc)); + } + + /* This will return false if it becomes dirty or someone else pins it. */ + result = InvalidateVictimBuffer(desc); + + UnpinBuffer(desc); + + return result; +} diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h index f380f9d9a6..f278a697c3 100644 --- a/src/include/storage/bufmgr.h +++ b/src/include/storage/bufmgr.h @@ -305,6 +305,8 @@ extern bool BgBufferSync(struct WritebackContext *wb_context); extern void LimitAdditionalPins(uint32 *additional_pins); extern void LimitAdditionalLocalPins(uint32 *additional_pins); +extern bool EvictBuffer(Buffer buf); + /* in buf_init.c */ extern void InitBufferPool(void); extern Size BufferShmemSize(void); -- 2.39.3 (Apple Git-146)