diff --git a/.github/workflows/build-cloudberry.yml b/.github/workflows/build-cloudberry.yml index 04d5e827b6e..4f4d4f3939a 100644 --- a/.github/workflows/build-cloudberry.yml +++ b/.github/workflows/build-cloudberry.yml @@ -298,6 +298,7 @@ jobs: "contrib/pgcrypto:installcheck", "contrib/tablefunc:installcheck", "contrib/passwordcheck:installcheck", + "contrib/pg_buffercache:installcheck", "contrib/sslinfo:installcheck"] }, {"test":"ic-gpcontrib", diff --git a/.github/workflows/build-deb-cloudberry.yml b/.github/workflows/build-deb-cloudberry.yml index 38c2391376e..6299c9500c6 100644 --- a/.github/workflows/build-deb-cloudberry.yml +++ b/.github/workflows/build-deb-cloudberry.yml @@ -237,6 +237,7 @@ jobs: "contrib/pgcrypto:installcheck", "contrib/tablefunc:installcheck", "contrib/passwordcheck:installcheck", + "contrib/pg_buffercache:installcheck", "contrib/sslinfo:installcheck"] }, {"test":"ic-deb-gpcontrib", diff --git a/contrib/pg_buffercache/expected/pg_buffercache.out b/contrib/pg_buffercache/expected/pg_buffercache.out index 7b41872634e..de77aac68b0 100644 --- a/contrib/pg_buffercache/expected/pg_buffercache.out +++ b/contrib/pg_buffercache/expected/pg_buffercache.out @@ -17,9 +17,24 @@ from pg_buffercache_summary(); t | t | t (1 row) +SELECT count(*) > 0 FROM pg_buffercache_usage_counts() WHERE buffers >= 0; + ?column? +---------- + t +(1 row) + -- Check that the functions / views can't be accessed by default. To avoid -- having to create a dedicated user, use the pg_database_owner pseudo-role. SET ROLE pg_database_owner; +SELECT * FROM pg_buffercache; +ERROR: permission denied for view pg_buffercache +SELECT * FROM pg_buffercache_pages() AS p (wrong int); +ERROR: permission denied for function pg_buffercache_pages +SELECT * FROM pg_buffercache_summary(); +ERROR: permission denied for function pg_buffercache_summary +SELECT * FROM pg_buffercache_usage_counts(); +ERROR: permission denied for function pg_buffercache_usage_counts +RESET role; SELECT count(*) > 0 FROM pg_buffercache_usage_counts() WHERE buffers >= 0; ?column? ---------- @@ -72,17 +87,7 @@ SELECT count(*) > 0 FROM gp_buffercache_usage_counts_aggregated WHERE buffers >= (1 row) -- Check that the functions / views can't be accessed by default. -CREATE ROLE buffercache_test; -SET ROLE buffercache_test; -SELECT * FROM pg_buffercache; -ERROR: permission denied for view pg_buffercache -SELECT * FROM pg_buffercache_pages() AS p (wrong int); -ERROR: permission denied for function pg_buffercache_pages -SELECT * FROM pg_buffercache_summary(); -ERROR: permission denied for function pg_buffercache_summary -RESET role; -SELECT * FROM pg_buffercache_usage_counts(); -ERROR: permission denied for function pg_buffercache_usage_counts +SET ROLE pg_database_owner; -- GPDB SELECT * FROM pg_buffercache_summary; ERROR: permission denied for view pg_buffercache_summary @@ -166,4 +171,3 @@ SELECT count(*) > 0 FROM gp_buffercache_usage_counts_aggregated; (1 row) RESET ROLE; -DROP ROLE buffercache_test; diff --git a/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql b/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql index 8f212dc5e93..d5aebf3ba39 100644 --- a/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql +++ b/contrib/pg_buffercache/pg_buffercache--1.3--1.4.sql @@ -12,6 +12,17 @@ CREATE FUNCTION pg_buffercache_summary( AS 'MODULE_PATHNAME', 'pg_buffercache_summary' LANGUAGE C PARALLEL SAFE; +CREATE FUNCTION pg_buffercache_usage_counts( + OUT usage_count int4, + OUT buffers int4, + OUT dirty int4, + OUT pinned int4) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'pg_buffercache_usage_counts' +LANGUAGE C PARALLEL SAFE; + -- Don't want these to be available to public. REVOKE ALL ON FUNCTION pg_buffercache_summary() FROM PUBLIC; GRANT EXECUTE ON FUNCTION pg_buffercache_summary() TO pg_monitor; +REVOKE ALL ON FUNCTION pg_buffercache_usage_counts() FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pg_buffercache_usage_counts() TO pg_monitor; diff --git a/contrib/pg_buffercache/pg_buffercache_pages.c b/contrib/pg_buffercache/pg_buffercache_pages.c index 8e0807d6265..e91f35599f0 100644 --- a/contrib/pg_buffercache/pg_buffercache_pages.c +++ b/contrib/pg_buffercache/pg_buffercache_pages.c @@ -18,6 +18,7 @@ #define NUM_BUFFERCACHE_PAGES_MIN_ELEM 8 #define NUM_BUFFERCACHE_PAGES_ELEM 9 #define NUM_BUFFERCACHE_SUMMARY_ELEM 5 +#define NUM_BUFFERCACHE_USAGE_COUNTS_ELEM 4 PG_MODULE_MAGIC; @@ -61,6 +62,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); Datum pg_buffercache_pages(PG_FUNCTION_ARGS) @@ -304,3 +306,44 @@ pg_buffercache_summary(PG_FUNCTION_ARGS) PG_RETURN_DATUM(result); } + +Datum +pg_buffercache_usage_counts(PG_FUNCTION_ARGS) +{ + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + int usage_counts[BM_MAX_USAGE_COUNT + 1] = {0}; + int dirty[BM_MAX_USAGE_COUNT + 1] = {0}; + int pinned[BM_MAX_USAGE_COUNT + 1] = {0}; + Datum values[NUM_BUFFERCACHE_USAGE_COUNTS_ELEM]; + bool nulls[NUM_BUFFERCACHE_USAGE_COUNTS_ELEM] = {0}; + + InitMaterializedSRF(fcinfo, 0); + + for (int i = 0; i < NBuffers; i++) + { + BufferDesc *bufHdr = GetBufferDescriptor(i); + uint32 buf_state = pg_atomic_read_u32(&bufHdr->state); + int usage_count; + + usage_count = BUF_STATE_GET_USAGECOUNT(buf_state); + usage_counts[usage_count]++; + + if (buf_state & BM_DIRTY) + dirty[usage_count]++; + + if (BUF_STATE_GET_REFCOUNT(buf_state) > 0) + pinned[usage_count]++; + } + + for (int i = 0; i < BM_MAX_USAGE_COUNT + 1; i++) + { + values[0] = Int32GetDatum(i); + values[1] = Int32GetDatum(usage_counts[i]); + values[2] = Int32GetDatum(dirty[i]); + values[3] = Int32GetDatum(pinned[i]); + + tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls); + } + + return (Datum) 0; +} diff --git a/contrib/pg_buffercache/sql/pg_buffercache.sql b/contrib/pg_buffercache/sql/pg_buffercache.sql index 5d2beecd707..e9091c9e38c 100644 --- a/contrib/pg_buffercache/sql/pg_buffercache.sql +++ b/contrib/pg_buffercache/sql/pg_buffercache.sql @@ -10,12 +10,15 @@ select buffers_used + buffers_unused > 0, buffers_pinned <= buffers_used from pg_buffercache_summary(); +SELECT count(*) > 0 FROM pg_buffercache_usage_counts() WHERE buffers >= 0; + -- Check that the functions / views can't be accessed by default. To avoid -- 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_pages() AS p (wrong int); SELECT * FROM pg_buffercache_summary(); +SELECT * FROM pg_buffercache_usage_counts(); RESET role; SELECT count(*) > 0 FROM pg_buffercache_usage_counts() WHERE buffers >= 0; @@ -42,12 +45,7 @@ SELECT count(*) > 0 FROM gp_buffercache_usage_counts WHERE buffers >= 0; SELECT count(*) > 0 FROM gp_buffercache_usage_counts_aggregated WHERE buffers >= 0; -- Check that the functions / views can't be accessed by default. -CREATE ROLE buffercache_test; -SET ROLE buffercache_test; -SELECT * FROM pg_buffercache; -SELECT * FROM pg_buffercache_pages() AS p (wrong int); -SELECT * FROM pg_buffercache_summary(); -SELECT * FROM pg_buffercache_usage_counts(); +SET ROLE pg_database_owner; -- GPDB SELECT * FROM pg_buffercache_summary; SELECT * FROM pg_buffercache_usage_counts; @@ -73,5 +71,3 @@ SELECT buffers_used + buffers_unused > 0 FROM gp_buffercache_summary_aggregated; SELECT count(*) > 0 FROM gp_buffercache_usage_counts; SELECT count(*) > 0 FROM gp_buffercache_usage_counts_aggregated; RESET ROLE; - -DROP ROLE buffercache_test; diff --git a/doc/src/sgml/pgbuffercache.sgml b/doc/src/sgml/pgbuffercache.sgml index 8f314ee8ff4..a05f010b2a5 100644 --- a/doc/src/sgml/pgbuffercache.sgml +++ b/doc/src/sgml/pgbuffercache.sgml @@ -21,9 +21,10 @@ - The module provides the pg_buffercache_pages() - function, wrapped in the pg_buffercache view, and - the pg_buffercache_summary() function. + 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. @@ -38,6 +39,12 @@ row summarizing the state of the shared buffer cache. + + The pg_buffercache_usage_counts() function returns a set + of records, each row describing the number of buffers with a given usage + count. + + By default, use is restricted to superusers and roles with privileges of the pg_monitor role. Access may be granted to others @@ -242,7 +249,7 @@ usagecount_avg float8 - Average usagecount of used shared buffers + Average usage count of used shared buffers @@ -266,6 +273,84 @@ + The <function>pg_buffercache_usage_counts()</function> Function + + + The definitions of the columns exposed by the function are shown in + . + + + + <function>pg_buffercache_usage_counts()</function> Output Columns + + + + + Column Type + + + Description + + + + + + + + usage_count int4 + + + A possible buffer usage count + + + + + + buffers int4 + + + Number of buffers with the usage count + + + + + + dirty int4 + + + Number of dirty buffers with the usage count + + + + + + pinned int4 + + + Number of pinned buffers with the usage count + + + + +
+ + + The pg_buffercache_usage_counts() function returns a + set of rows summarizing the states of all shared buffers, aggregated over + the possible usage count values. Similar and more detailed information is + provided by the pg_buffercache view, but + pg_buffercache_usage_counts() is significantly cheaper. + + + + Like the pg_buffercache view, + pg_buffercache_usage_counts() does not acquire buffer + manager locks. Therefore concurrent activity can lead to minor inaccuracies + in the result. + +
+ + Sample Output @@ -299,6 +384,18 @@ regression=# SELECT * FROM pg_buffercache_summary(); --------------+----------------+---------------+----------------+---------------- 248 | 2096904 | 39 | 0 | 3.141129 (1 row) + + +regression=# SELECT * FROM pg_buffercache_usage_counts(); + usage_count | buffers | dirty | pinned +-------------+---------+-------+-------- + 0 | 14650 | 0 | 0 + 1 | 1436 | 671 | 0 + 2 | 102 | 88 | 0 + 3 | 23 | 21 | 0 + 4 | 9 | 7 | 0 + 5 | 164 | 106 | 0 +(6 rows)