From 1a13e00fd8b069d653f08132a3d35c7c17fdf5c9 Mon Sep 17 00:00:00 2001 From: Ashutosh Bapat Date: Mon, 25 Aug 2025 19:23:50 +0530 Subject: [PATCH 01/19] Add system view for shared buffer lookup table The view exposes the contents of the shared buffer lookup table for debugging, testing and investigation. TODO: It is better to place this view in pg_buffercache. But it's added as a system view since BufHashTable is not exposed outside buf_table.c. To move it to pg_buffercache, we should move the function pg_get_buffer_lookup_table() to pg_buffercache which invokes BufTableGetContent() by passing it the tuple store and tuple descriptor. BufTableGetContent fills the tuple store. The partitions are locked by pg_get_buffer_lookup_table(). Author: Ashutosh Bapat --- doc/src/sgml/system-views.sgml | 89 ++++++++++++++++++++++++++ src/backend/catalog/system_views.sql | 7 ++ src/backend/storage/buffer/buf_table.c | 61 ++++++++++++++++++ src/include/catalog/pg_proc.dat | 11 ++++ src/test/regress/expected/rules.out | 7 ++ 5 files changed, 175 insertions(+) diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml index 7971498fe75..8f3e2741051 100644 --- a/doc/src/sgml/system-views.sgml +++ b/doc/src/sgml/system-views.sgml @@ -71,6 +71,11 @@ backend memory contexts + + pg_buffer_lookup_table + shared buffer lookup table + + pg_config compile-time configuration parameters @@ -901,6 +906,90 @@ AND c1.path[c2.level] = c2.path[c2.level]; + + <structname>pg_buffer_lookup_table</structname> + + pg_buffer_lookup_table + + + The pg_buffer_lookup_table view exposes the current + contents of the shared buffer lookup table. Each row represents an entry in + the lookup table mapping a relation page to the ID of buffer in which it is + cached. The shared buffer lookup table is locked for a short duration while + reading so as to ensure consistency. This may affect performance if this view + is queried very frequently. + + + <structname>pg_buffer_lookup_table</structname> View + + + + + Column Type + + + Description + + + + + + + tablespace oid + + + OID of the tablespace containing the relation + + + + + database oid + + + OID of the database containing the relation (zero for shared relations) + + + + + relfilenode oid + + + relfilenode identifying the relation + + + + + forknum int2 + + + Fork number within the relation (see ) + + + + + blocknum int8 + + + Block number within the relation + + + + + bufferid int4 + + + ID of the buffer caching the page + + + + +
+ + Access to this view is restricted to members of the + pg_read_all_stats role by default. + +
+ <structname>pg_config</structname> diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 823776c1498..c7240250c07 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -1436,3 +1436,10 @@ REVOKE ALL ON pg_aios FROM PUBLIC; GRANT SELECT ON pg_aios TO pg_read_all_stats; REVOKE EXECUTE ON FUNCTION pg_get_aios() FROM PUBLIC; GRANT EXECUTE ON FUNCTION pg_get_aios() TO pg_read_all_stats; + +CREATE VIEW pg_buffer_lookup_table AS + SELECT * FROM pg_get_buffer_lookup_table(); +REVOKE ALL ON pg_buffer_lookup_table FROM PUBLIC; +GRANT SELECT ON pg_buffer_lookup_table TO pg_read_all_stats; +REVOKE EXECUTE ON FUNCTION pg_get_buffer_lookup_table() FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pg_get_buffer_lookup_table() TO pg_read_all_stats; diff --git a/src/backend/storage/buffer/buf_table.c b/src/backend/storage/buffer/buf_table.c index 9d256559bab..1f6e215a2ca 100644 --- a/src/backend/storage/buffer/buf_table.c +++ b/src/backend/storage/buffer/buf_table.c @@ -21,7 +21,12 @@ */ #include "postgres.h" +#include "fmgr.h" +#include "funcapi.h" #include "storage/buf_internals.h" +#include "storage/lwlock.h" +#include "utils/rel.h" +#include "utils/builtins.h" /* entry for buffer lookup hashtable */ typedef struct @@ -159,3 +164,59 @@ BufTableDelete(BufferTag *tagPtr, uint32 hashcode) if (!result) /* shouldn't happen */ elog(ERROR, "shared buffer hash table corrupted"); } + +/* + * SQL callable function to report contents of the shared buffer lookup table. + */ +Datum +pg_get_buffer_lookup_table(PG_FUNCTION_ARGS) +{ +#define PG_GET_BUFFER_LOOKUP_TABLE_COLS 6 + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + HASH_SEQ_STATUS hstat; + BufferLookupEnt *ent; + Datum values[PG_GET_BUFFER_LOOKUP_TABLE_COLS]; + bool nulls[PG_GET_BUFFER_LOOKUP_TABLE_COLS]; + int i; + + memset(nulls, 0, sizeof(nulls)); + + /* + * We put all the tuples into a tuplestore in one scan of the hashtable. + * This avoids any issue of the hashtable possibly changing between calls. + */ + InitMaterializedSRF(fcinfo, 0); + + Assert(rsinfo->setDesc->natts == PG_GET_BUFFER_LOOKUP_TABLE_COLS); + + /* + * Lock all buffer mapping partitions to ensure a consistent view of the + * hash table during the scan. Must grab LWLocks in partition-number order + * to avoid LWLock deadlock. + */ + for (i = 0; i < NUM_BUFFER_PARTITIONS; i++) + LWLockAcquire(BufMappingPartitionLockByIndex(i), LW_SHARED); + + hash_seq_init(&hstat, SharedBufHash); + while ((ent = (BufferLookupEnt *) hash_seq_search(&hstat)) != NULL) + { + values[0] = ObjectIdGetDatum(ent->key.spcOid); + values[1] = ObjectIdGetDatum(ent->key.dbOid); + values[2] = ObjectIdGetDatum(ent->key.relNumber); + values[3] = ObjectIdGetDatum(ent->key.forkNum); + values[4] = UInt32GetDatum(ent->key.blockNum); + values[5] = Int32GetDatum(ent->id); + + tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, + values, nulls); + } + + /* + * Release all buffer mapping partition locks in the reverse order so as + * to avoid LWLock deadlock. + */ + for (i = NUM_BUFFER_PARTITIONS - 1; i >= 0; i--) + LWLockRelease(BufMappingPartitionLockByIndex(i)); + + return (Datum) 0; +} diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index b51d2b17379..e631323a325 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8600,6 +8600,17 @@ proargmodes => '{o,o,o}', proargnames => '{name,type,size}', prosrc => 'pg_get_dsm_registry_allocations' }, +# buffer lookup table +{ oid => '5102', + descr => 'shared buffer lookup table', + proname => 'pg_get_buffer_lookup_table', prorows => '6', proretset => 't', + provolatile => 'v', prorettype => 'record', + proargtypes => '', proallargtypes => '{oid,oid,oid,int2,int8,int4}', + proargmodes => '{o,o,o,o,o,o}', + proargnames => '{tablespace,database,relfilenode,forknum,blocknum,bufferid}', + prosrc => 'pg_get_buffer_lookup_table' +}, + # memory context of local backend { oid => '2282', descr => 'information about all memory contexts of local backend', diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 16753b2e4c0..83f566d3218 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1330,6 +1330,13 @@ pg_backend_memory_contexts| SELECT name, free_chunks, used_bytes FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, type, level, path, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); +pg_buffer_lookup_table| SELECT tablespace, + database, + relfilenode, + forknum, + blocknum, + bufferid + FROM pg_get_buffer_lookup_table() pg_get_buffer_lookup_table(tablespace, database, relfilenode, forknum, blocknum, bufferid); pg_config| SELECT name, setting FROM pg_config() pg_config(name, setting); base-commit: 7a662a46ebf74e9fa15cb62b592b4bf00c96fc94 -- 2.34.1