diff options
| author | Michael Paquier <michael@paquier.xyz> | 2025-11-24 14:29:15 +0900 |
|---|---|---|
| committer | Michael Paquier <michael@paquier.xyz> | 2025-11-24 14:29:15 +0900 |
| commit | 4b203d499c610160e9867e6add2366780429344c (patch) | |
| tree | 244d13f2d90e5e715c7caa6dce779bd94514b780 | |
| parent | 07d1dc3aebe4391a2711e951c69df2bb56447f8a (diff) | |
pg_buffercache: Add pg_buffercache_os_pages
ba2a3c2302f has added a way to check if a buffer is spread across
multiple pages with some NUMA information, via a new view
pg_buffercache_numa that depends on pg_buffercache_numa_pages(), a SQL
function. These can only be queried when support for libnuma exists,
generating an error if not.
However, it can be useful to know how shared buffers and OS pages map
when NUMA is not supported or not available. This commit expands the
capabilities around pg_buffercache_numa:
- pg_buffercache_numa_pages() is refactored as an internal function,
able to optionally process NUMA. Its SQL definition prior to this
commit is still around to ensure backward-compatibility with v1.6.
- A SQL function called pg_buffercache_os_pages() is added, able to work
with or without NUMA.
- The view pg_buffercache_numa is redefined to use
pg_buffercache_os_pages().
- A new view is added, called pg_buffercache_os_pages. This ignores
NUMA for its result processing, for a better efficiency.
The implementation is done so as there is no code duplication between
the NUMA and non-NUMA views/functions, relying on one internal function
that does the job for all of them. The module is bumped to v1.7.
Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Mircea Cadariu <cadariu.mircea@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/Z/fFA2heH6lpSLlt@ip-10-97-1-34.eu-west-3.compute.internal
| -rw-r--r-- | contrib/pg_buffercache/Makefile | 2 | ||||
| -rw-r--r-- | contrib/pg_buffercache/expected/pg_buffercache.out | 18 | ||||
| -rw-r--r-- | contrib/pg_buffercache/meson.build | 1 | ||||
| -rw-r--r-- | contrib/pg_buffercache/pg_buffercache--1.6--1.7.sql | 33 | ||||
| -rw-r--r-- | contrib/pg_buffercache/pg_buffercache.control | 2 | ||||
| -rw-r--r-- | contrib/pg_buffercache/pg_buffercache_pages.c | 198 | ||||
| -rw-r--r-- | contrib/pg_buffercache/sql/pg_buffercache.sql | 8 | ||||
| -rw-r--r-- | doc/src/sgml/pgbuffercache.sgml | 108 | ||||
| -rw-r--r-- | src/tools/pgindent/typedefs.list | 4 |
9 files changed, 289 insertions, 85 deletions
diff --git a/contrib/pg_buffercache/Makefile b/contrib/pg_buffercache/Makefile index 5f748543e2e..0e618f66aec 100644 --- a/contrib/pg_buffercache/Makefile +++ b/contrib/pg_buffercache/Makefile @@ -9,7 +9,7 @@ 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.4--1.5.sql \ - pg_buffercache--1.5--1.6.sql + pg_buffercache--1.5--1.6.sql pg_buffercache--1.6--1.7.sql PGFILEDESC = "pg_buffercache - monitoring of shared buffer cache in real-time" REGRESS = pg_buffercache pg_buffercache_numa diff --git a/contrib/pg_buffercache/expected/pg_buffercache.out b/contrib/pg_buffercache/expected/pg_buffercache.out index 9a9216dc7b1..26c2d5f5710 100644 --- a/contrib/pg_buffercache/expected/pg_buffercache.out +++ b/contrib/pg_buffercache/expected/pg_buffercache.out @@ -8,6 +8,16 @@ from pg_buffercache; t (1 row) +-- For pg_buffercache_os_pages, we expect at least one entry for each buffer +select count(*) >= (select setting::bigint + from pg_settings + where name = 'shared_buffers') +from pg_buffercache_os_pages; + ?column? +---------- + t +(1 row) + select buffers_used + buffers_unused > 0, buffers_dirty <= buffers_used, buffers_pinned <= buffers_used @@ -28,6 +38,8 @@ SELECT count(*) > 0 FROM pg_buffercache_usage_counts() WHERE buffers >= 0; SET ROLE pg_database_owner; SELECT * FROM pg_buffercache; ERROR: permission denied for view pg_buffercache +SELECT * FROM pg_buffercache_os_pages; +ERROR: permission denied for view pg_buffercache_os_pages SELECT * FROM pg_buffercache_pages() AS p (wrong int); ERROR: permission denied for function pg_buffercache_pages SELECT * FROM pg_buffercache_summary(); @@ -43,6 +55,12 @@ SELECT count(*) > 0 FROM pg_buffercache; t (1 row) +SELECT count(*) > 0 FROM pg_buffercache_os_pages; + ?column? +---------- + t +(1 row) + SELECT buffers_used + buffers_unused > 0 FROM pg_buffercache_summary(); ?column? ---------- diff --git a/contrib/pg_buffercache/meson.build b/contrib/pg_buffercache/meson.build index 7cd039a1df9..7c31141881f 100644 --- a/contrib/pg_buffercache/meson.build +++ b/contrib/pg_buffercache/meson.build @@ -24,6 +24,7 @@ install_data( 'pg_buffercache--1.3--1.4.sql', 'pg_buffercache--1.4--1.5.sql', 'pg_buffercache--1.5--1.6.sql', + 'pg_buffercache--1.6--1.7.sql', 'pg_buffercache.control', kwargs: contrib_data_args, ) diff --git a/contrib/pg_buffercache/pg_buffercache--1.6--1.7.sql b/contrib/pg_buffercache/pg_buffercache--1.6--1.7.sql new file mode 100644 index 00000000000..5ecc0a8708a --- /dev/null +++ b/contrib/pg_buffercache/pg_buffercache--1.6--1.7.sql @@ -0,0 +1,33 @@ +/* contrib/pg_buffercache/pg_buffercache--1.6--1.7.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pg_buffercache UPDATE TO '1.7'" to load this file. \quit + +-- Function to retrieve information about OS pages, with optional NUMA +-- information. +CREATE FUNCTION pg_buffercache_os_pages(IN include_numa boolean, + OUT bufferid integer, + OUT os_page_num bigint, + OUT numa_node integer) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'pg_buffercache_os_pages' +LANGUAGE C PARALLEL SAFE; + +-- View for OS page information, without NUMA. +CREATE VIEW pg_buffercache_os_pages AS + SELECT bufferid, os_page_num + FROM pg_buffercache_os_pages(false); + +-- Re-create view for OS page information, with NUMA. +DROP VIEW pg_buffercache_numa; +CREATE VIEW pg_buffercache_numa AS + SELECT bufferid, os_page_num, numa_node + FROM pg_buffercache_os_pages(true); + +REVOKE ALL ON FUNCTION pg_buffercache_os_pages(boolean) FROM PUBLIC; +REVOKE ALL ON pg_buffercache_os_pages FROM PUBLIC; +REVOKE ALL ON pg_buffercache_numa FROM PUBLIC; + +GRANT EXECUTE ON FUNCTION pg_buffercache_os_pages(boolean) TO pg_monitor; +GRANT SELECT ON pg_buffercache_os_pages TO pg_monitor; +GRANT SELECT ON pg_buffercache_numa TO pg_monitor; diff --git a/contrib/pg_buffercache/pg_buffercache.control b/contrib/pg_buffercache/pg_buffercache.control index b030ba3a6fa..11499550945 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.6' +default_version = '1.7' 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 7c9ff24fa83..ae1712fc93c 100644 --- a/contrib/pg_buffercache/pg_buffercache_pages.c +++ b/contrib/pg_buffercache/pg_buffercache_pages.c @@ -26,7 +26,7 @@ #define NUM_BUFFERCACHE_EVICT_RELATION_ELEM 3 #define NUM_BUFFERCACHE_EVICT_ALL_ELEM 3 -#define NUM_BUFFERCACHE_NUMA_ELEM 3 +#define NUM_BUFFERCACHE_OS_PAGES_ELEM 3 PG_MODULE_MAGIC_EXT( .name = "pg_buffercache", @@ -67,14 +67,16 @@ typedef struct } BufferCachePagesContext; /* - * Record structure holding the to be exposed cache data. + * Record structure holding the to be exposed cache data for OS pages. This + * structure is used by pg_buffercache_os_pages(), where NUMA information may + * or may not be included. */ typedef struct { uint32 bufferid; int64 page_num; int32 numa_node; -} BufferCacheNumaRec; +} BufferCacheOsPagesRec; /* * Function context for data persisting over repeated calls. @@ -82,8 +84,9 @@ typedef struct typedef struct { TupleDesc tupdesc; - BufferCacheNumaRec *record; -} BufferCacheNumaContext; + bool include_numa; + BufferCacheOsPagesRec *record; +} BufferCacheOsPagesContext; /* @@ -91,6 +94,7 @@ typedef struct * relation node/tablespace/database/blocknum and dirty indicator. */ PG_FUNCTION_INFO_V1(pg_buffercache_pages); +PG_FUNCTION_INFO_V1(pg_buffercache_os_pages); PG_FUNCTION_INFO_V1(pg_buffercache_numa_pages); PG_FUNCTION_INFO_V1(pg_buffercache_summary); PG_FUNCTION_INFO_V1(pg_buffercache_usage_counts); @@ -284,26 +288,32 @@ pg_buffercache_pages(PG_FUNCTION_ARGS) } /* - * Inquire about NUMA memory mappings for shared buffers. + * Inquire about OS pages mappings for shared buffers, with NUMA information, + * optionally. + * + * When "include_numa" is false, this routines ignores everything related + * to NUMA (returned as NULL values), returning mapping information between + * shared buffers and OS pages. + * + * When "include_numa" is true, NUMA is initialized and numa_node values + * are generated. In order to get reliable results we also need to touch + * memory pages, so that the inquiry about NUMA memory node does not return + * -2, indicating unmapped/unallocated pages. * - * Returns NUMA node ID for each memory page used by the buffer. Buffers may - * be smaller or larger than OS memory pages. For each buffer we return one - * entry for each memory page used by the buffer (if the buffer is smaller, - * it only uses a part of one memory page). + * Buffers may be smaller or larger than OS memory pages. For each buffer we + * return one entry for each memory page used by the buffer (if the buffer is + * smaller, it only uses a part of one memory page). * * We expect both sizes (for buffers and memory pages) to be a power-of-2, so * one is always a multiple of the other. * - * In order to get reliable results we also need to touch memory pages, so - * that the inquiry about NUMA memory node doesn't return -2 (which indicates - * unmapped/unallocated pages). */ -Datum -pg_buffercache_numa_pages(PG_FUNCTION_ARGS) +static Datum +pg_buffercache_os_pages_internal(FunctionCallInfo fcinfo, bool include_numa) { FuncCallContext *funcctx; MemoryContext oldcontext; - BufferCacheNumaContext *fctx; /* User function context. */ + BufferCacheOsPagesContext *fctx; /* User function context. */ TupleDesc tupledesc; TupleDesc expected_tupledesc; HeapTuple tuple; @@ -314,15 +324,15 @@ pg_buffercache_numa_pages(PG_FUNCTION_ARGS) int i, idx; Size os_page_size; - void **os_page_ptrs; - int *os_page_status; - uint64 os_page_count; int pages_per_buffer; + int *os_page_status = NULL; + uint64 os_page_count = 0; int max_entries; char *startptr, *endptr; - if (pg_numa_init() == -1) + /* If NUMA information is requested, initialize NUMA support. */ + if (include_numa && pg_numa_init() == -1) elog(ERROR, "libnuma initialization failed or NUMA is not supported on this platform"); /* @@ -350,51 +360,56 @@ pg_buffercache_numa_pages(PG_FUNCTION_ARGS) */ Assert((os_page_size % BLCKSZ == 0) || (BLCKSZ % os_page_size == 0)); - /* - * How many addresses we are going to query? Simply get the page for - * the first buffer, and first page after the last buffer, and count - * the pages from that. - */ - startptr = (char *) TYPEALIGN_DOWN(os_page_size, - BufferGetBlock(1)); - endptr = (char *) TYPEALIGN(os_page_size, - (char *) BufferGetBlock(NBuffers) + BLCKSZ); - os_page_count = (endptr - startptr) / os_page_size; - - /* Used to determine the NUMA node for all OS pages at once */ - os_page_ptrs = palloc0(sizeof(void *) * os_page_count); - os_page_status = palloc(sizeof(uint64) * os_page_count); - - /* - * Fill pointers for all the memory pages. This loop stores and - * touches (if needed) addresses into os_page_ptrs[] as input to one - * big move_pages(2) inquiry system call, as done in - * pg_numa_query_pages(). - */ - idx = 0; - for (char *ptr = startptr; ptr < endptr; ptr += os_page_size) + if (include_numa) { - os_page_ptrs[idx++] = ptr; + void **os_page_ptrs = NULL; + + /* + * How many addresses we are going to query? Simply get the page + * for the first buffer, and first page after the last buffer, and + * count the pages from that. + */ + startptr = (char *) TYPEALIGN_DOWN(os_page_size, + BufferGetBlock(1)); + endptr = (char *) TYPEALIGN(os_page_size, + (char *) BufferGetBlock(NBuffers) + BLCKSZ); + os_page_count = (endptr - startptr) / os_page_size; + + /* Used to determine the NUMA node for all OS pages at once */ + os_page_ptrs = palloc0(sizeof(void *) * os_page_count); + os_page_status = palloc(sizeof(uint64) * os_page_count); + + /* + * Fill pointers for all the memory pages. This loop stores and + * touches (if needed) addresses into os_page_ptrs[] as input to + * one big move_pages(2) inquiry system call, as done in + * pg_numa_query_pages(). + */ + idx = 0; + for (char *ptr = startptr; ptr < endptr; ptr += os_page_size) + { + os_page_ptrs[idx++] = ptr; - /* Only need to touch memory once per backend process lifetime */ - if (firstNumaTouch) - pg_numa_touch_mem_if_required(ptr); - } + /* Only need to touch memory once per backend process lifetime */ + if (firstNumaTouch) + pg_numa_touch_mem_if_required(ptr); + } - Assert(idx == os_page_count); + Assert(idx == os_page_count); - elog(DEBUG1, "NUMA: NBuffers=%d os_page_count=" UINT64_FORMAT " " - "os_page_size=%zu", NBuffers, os_page_count, os_page_size); + elog(DEBUG1, "NUMA: NBuffers=%d os_page_count=" UINT64_FORMAT " " + "os_page_size=%zu", NBuffers, os_page_count, os_page_size); - /* - * If we ever get 0xff back from kernel inquiry, then we probably have - * bug in our buffers to OS page mapping code here. - */ - memset(os_page_status, 0xff, sizeof(int) * os_page_count); + /* + * If we ever get 0xff back from kernel inquiry, then we probably + * have bug in our buffers to OS page mapping code here. + */ + memset(os_page_status, 0xff, sizeof(int) * os_page_count); - /* Query NUMA status for all the pointers */ - if (pg_numa_query_pages(0, os_page_count, os_page_ptrs, os_page_status) == -1) - elog(ERROR, "failed NUMA pages inquiry: %m"); + /* Query NUMA status for all the pointers */ + if (pg_numa_query_pages(0, os_page_count, os_page_ptrs, os_page_status) == -1) + elog(ERROR, "failed NUMA pages inquiry: %m"); + } /* Initialize the multi-call context, load entries about buffers */ @@ -404,12 +419,12 @@ pg_buffercache_numa_pages(PG_FUNCTION_ARGS) oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /* Create a user function context for cross-call persistence */ - fctx = (BufferCacheNumaContext *) palloc(sizeof(BufferCacheNumaContext)); + fctx = (BufferCacheOsPagesContext *) palloc(sizeof(BufferCacheOsPagesContext)); if (get_call_result_type(fcinfo, NULL, &expected_tupledesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); - if (expected_tupledesc->natts != NUM_BUFFERCACHE_NUMA_ELEM) + if (expected_tupledesc->natts != NUM_BUFFERCACHE_OS_PAGES_ELEM) elog(ERROR, "incorrect number of output arguments"); /* Construct a tuple descriptor for the result rows. */ @@ -422,6 +437,7 @@ pg_buffercache_numa_pages(PG_FUNCTION_ARGS) INT4OID, -1, 0); fctx->tupdesc = BlessTupleDesc(tupledesc); + fctx->include_numa = include_numa; /* * Each buffer needs at least one entry, but it might be offset in @@ -433,15 +449,15 @@ pg_buffercache_numa_pages(PG_FUNCTION_ARGS) pages_per_buffer = Max(1, BLCKSZ / os_page_size) + 1; max_entries = NBuffers * pages_per_buffer; - /* Allocate entries for BufferCachePagesRec records. */ - fctx->record = (BufferCacheNumaRec *) + /* Allocate entries for BufferCacheOsPagesRec records. */ + fctx->record = (BufferCacheOsPagesRec *) MemoryContextAllocHuge(CurrentMemoryContext, - sizeof(BufferCacheNumaRec) * max_entries); + sizeof(BufferCacheOsPagesRec) * max_entries); /* Return to original context when allocating transient memory */ MemoryContextSwitchTo(oldcontext); - if (firstNumaTouch) + if (include_numa && firstNumaTouch) elog(DEBUG1, "NUMA: page-faulting the buffercache for proper NUMA readouts"); /* @@ -488,7 +504,7 @@ pg_buffercache_numa_pages(PG_FUNCTION_ARGS) { fctx->record[idx].bufferid = bufferid; fctx->record[idx].page_num = page_num; - fctx->record[idx].numa_node = os_page_status[page_num]; + fctx->record[idx].numa_node = include_numa ? os_page_status[page_num] : -1; /* advance to the next entry/page */ ++idx; @@ -496,14 +512,18 @@ pg_buffercache_numa_pages(PG_FUNCTION_ARGS) } } - Assert((idx >= os_page_count) && (idx <= max_entries)); + Assert(idx <= max_entries); + + if (include_numa) + Assert(idx >= os_page_count); /* Set max calls and remember the user function context. */ funcctx->max_calls = idx; funcctx->user_fctx = fctx; - /* Remember this backend touched the pages */ - firstNumaTouch = false; + /* Remember this backend touched the pages (only relevant for NUMA) */ + if (include_numa) + firstNumaTouch = false; } funcctx = SRF_PERCALL_SETUP(); @@ -514,8 +534,8 @@ pg_buffercache_numa_pages(PG_FUNCTION_ARGS) if (funcctx->call_cntr < funcctx->max_calls) { uint32 i = funcctx->call_cntr; - Datum values[NUM_BUFFERCACHE_NUMA_ELEM]; - bool nulls[NUM_BUFFERCACHE_NUMA_ELEM]; + Datum values[NUM_BUFFERCACHE_OS_PAGES_ELEM]; + bool nulls[NUM_BUFFERCACHE_OS_PAGES_ELEM]; values[0] = Int32GetDatum(fctx->record[i].bufferid); nulls[0] = false; @@ -523,8 +543,16 @@ pg_buffercache_numa_pages(PG_FUNCTION_ARGS) values[1] = Int64GetDatum(fctx->record[i].page_num); nulls[1] = false; - values[2] = Int32GetDatum(fctx->record[i].numa_node); - nulls[2] = false; + if (fctx->include_numa) + { + values[2] = Int32GetDatum(fctx->record[i].numa_node); + nulls[2] = false; + } + else + { + values[2] = (Datum) 0; + nulls[2] = true; + } /* Build and return the tuple. */ tuple = heap_form_tuple(fctx->tupdesc, values, nulls); @@ -536,6 +564,30 @@ pg_buffercache_numa_pages(PG_FUNCTION_ARGS) SRF_RETURN_DONE(funcctx); } +/* + * pg_buffercache_os_pages + * + * Retrieve information about OS pages, with or without NUMA information. + */ +Datum +pg_buffercache_os_pages(PG_FUNCTION_ARGS) +{ + bool include_numa; + + /* Get the boolean parameter that controls the NUMA behavior. */ + include_numa = PG_GETARG_BOOL(0); + + return pg_buffercache_os_pages_internal(fcinfo, include_numa); +} + +/* Backward-compatible wrapper for v1.6. */ +Datum +pg_buffercache_numa_pages(PG_FUNCTION_ARGS) +{ + /* Call internal function with include_numa=true */ + return pg_buffercache_os_pages_internal(fcinfo, true); +} + Datum pg_buffercache_summary(PG_FUNCTION_ARGS) { diff --git a/contrib/pg_buffercache/sql/pg_buffercache.sql b/contrib/pg_buffercache/sql/pg_buffercache.sql index 47cca1907c7..3c70ee9ef4a 100644 --- a/contrib/pg_buffercache/sql/pg_buffercache.sql +++ b/contrib/pg_buffercache/sql/pg_buffercache.sql @@ -5,6 +5,12 @@ select count(*) = (select setting::bigint where name = 'shared_buffers') from pg_buffercache; +-- For pg_buffercache_os_pages, we expect at least one entry for each buffer +select count(*) >= (select setting::bigint + from pg_settings + where name = 'shared_buffers') +from pg_buffercache_os_pages; + select buffers_used + buffers_unused > 0, buffers_dirty <= buffers_used, buffers_pinned <= buffers_used @@ -16,6 +22,7 @@ SELECT count(*) > 0 FROM pg_buffercache_usage_counts() WHERE buffers >= 0; -- having to create a dedicated user, use the pg_database_owner pseudo-role. SET ROLE pg_database_owner; SELECT * FROM pg_buffercache; +SELECT * FROM pg_buffercache_os_pages; SELECT * FROM pg_buffercache_pages() AS p (wrong int); SELECT * FROM pg_buffercache_summary(); SELECT * FROM pg_buffercache_usage_counts(); @@ -24,6 +31,7 @@ RESET role; -- Check that pg_monitor is allowed to query view / function SET ROLE pg_monitor; SELECT count(*) > 0 FROM pg_buffercache; +SELECT count(*) > 0 FROM pg_buffercache_os_pages; SELECT buffers_used + buffers_unused > 0 FROM pg_buffercache_summary(); SELECT count(*) > 0 FROM pg_buffercache_usage_counts(); RESET role; diff --git a/doc/src/sgml/pgbuffercache.sgml b/doc/src/sgml/pgbuffercache.sgml index 99ad2e68785..91bbedff343 100644 --- a/doc/src/sgml/pgbuffercache.sgml +++ b/doc/src/sgml/pgbuffercache.sgml @@ -46,8 +46,9 @@ <para> This module provides the <function>pg_buffercache_pages()</function> function (wrapped in the <structname>pg_buffercache</structname> view), the - <function>pg_buffercache_numa_pages()</function> function (wrapped in the - <structname>pg_buffercache_numa</structname> view), the + <function>pg_buffercache_os_pages()</function> function (wrapped in the + <structname>pg_buffercache_os_pages</structname> and + <structname>pg_buffercache_numa</structname> views), the <function>pg_buffercache_summary()</function> function, the <function>pg_buffercache_usage_counts()</function> function, the <function>pg_buffercache_evict()</function> function, the @@ -63,12 +64,16 @@ </para> <para> - The <function>pg_buffercache_numa_pages()</function> function provides - <acronym>NUMA</acronym> node mappings for shared buffer entries. This - information is not part of <function>pg_buffercache_pages()</function> - itself, as it is much slower to retrieve. - The <structname>pg_buffercache_numa</structname> view wraps the function for - convenient use. + The <function>pg_buffercache_os_pages()</function> function provides OS + pages mappings for shared buffer entries. When its argument is + <literal>true</literal>, it also provides <acronym>NUMA</acronym> node + mappings for shared buffer entries (this information is not part of + <function>pg_buffercache_pages()</function> itself, as it is much + slower to retrieve). + The <structname>pg_buffercache_os_pages</structname> and + <structname>pg_buffercache_numa</structname> views wrap the function for + convenient use, with its argument set to <literal>false</literal> and + <literal>true</literal> respectively. </para> <para> @@ -242,6 +247,53 @@ </para> </sect2> + <sect2 id="pgbuffercache-pg-buffercache-os-pages"> + <title>The <structname>pg_buffercache_os_pages</structname> View</title> + + <para> + The definitions of the columns exposed by the view are shown in + <xref linkend="pgbuffercache-os-pages-columns"/>. + </para> + + <table id="pgbuffercache-os-pages-columns"> + <title><structname>pg_buffercache_os_pages</structname> Columns</title> + <tgroup cols="1"> + <thead> + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + Column Type + </para> + <para> + Description + </para></entry> + </row> + </thead> + + <tbody> + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>bufferid</structfield> <type>integer</type> + </para> + <para> + ID, in the range 1..<varname>shared_buffers</varname> + </para></entry> + </row> + + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>os_page_num</structfield> <type>bigint</type> + </para> + <para> + Number of OS memory page for this buffer + </para></entry> + </row> + + </tbody> + </tgroup> + </table> + + </sect2> + <sect2 id="pgbuffercache-pg-buffercache-numa"> <title>The <structname>pg_buffercache_numa</structname> View</title> @@ -558,6 +610,46 @@ regression=# SELECT n.nspname, c.relname, count(*) AS buffers public | spgist_text_tbl | 182 (10 rows) +regression=# SELECT pages_per_buffer, COUNT(*) as buffer_count + FROM ( + SELECT bufferid, COUNT(*) as pages_per_buffer + FROM pg_buffercache_os_pages + GROUP BY bufferid + ) + GROUP BY pages_per_buffer + ORDER BY pages_per_buffer; + + pages_per_buffer | buffer_count +------------------+-------------- + 1 | 261120 + 2 | 1024 +(2 rows) + +regression=# SELECT n.nspname, c.relname, count(*) AS buffers_on_multiple_pages + FROM pg_buffercache b JOIN pg_class c + ON b.relfilenode = pg_relation_filenode(c.oid) AND + b.reldatabase IN (0, (SELECT oid FROM pg_database + WHERE datname = current_database())) + JOIN pg_namespace n ON n.oid = c.relnamespace + JOIN (SELECT bufferid FROM pg_buffercache_os_pages + GROUP BY bufferid HAVING count(*) > 1) m on m.bufferid = b.bufferid + GROUP BY n.nspname, c.relname + ORDER BY 3 DESC + LIMIT 10; + + nspname | relname | buffers_on_multiple_pages +------------+------------------------------+--------------------------- + public | delete_test_table | 3 + public | gin_test_idx | 2 + pg_catalog | pg_depend | 2 + public | quad_poly_tbl | 2 + pg_catalog | pg_depend_reference_index | 1 + pg_catalog | pg_index_indexrelid_index | 1 + pg_catalog | pg_constraint_contypid_index | 1 + pg_catalog | pg_statistic | 1 + pg_catalog | pg_depend_depender_index | 1 + pg_catalog | pg_operator | 1 +(10 rows) regression=# SELECT * FROM pg_buffercache_summary(); buffers_used | buffers_unused | buffers_dirty | buffers_pinned | usagecount_avg diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 0d1ea4ec63d..57a8f0366a5 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -338,8 +338,8 @@ BufFile Buffer BufferAccessStrategy BufferAccessStrategyType -BufferCacheNumaContext -BufferCacheNumaRec +BufferCacheOsPagesContext +BufferCacheOsPagesRec BufferCachePagesContext BufferCachePagesRec BufferDesc |
