On Tue Jul 28, 2026 at 9:39 PM UTC, Sami Imseih wrote: > On Wed Jul 23, 2026 at 4:35 PM UTC, Tristan Partin wrote: >> Did you mean to say shared_size? pgstat_get_entry_len() returns >> PgStat_KindInfo::shared_data_len, so the patch already exposes this >> value. > > Sorry for the confusion. I was agreeing with you, using > pgstat_get_entry_len() is the right approach. My point was that we > should include it (as you already do) because it's a kind attribute, > while being clear in the docs that it cannot be used to accurately > calculate the size of shared memory due to other overhead.
Thanks for clarifying. > On Mon Jul 28, 2026 at 2:58 AM UTC, Michael Paquier wrote: >> Would it help the monitoring purpose of the change if we began using >> GetNamedDSHash() when setting up the pgstats shared hash table? >> That would give a way to monitor the sizing of the table through >> pg_dsm_registry_allocations, at least, tackling your concerns about >> the imprecision of the data if we put a memory sizing field in >> pg_stat_kind_info? > > I think using GetNamedDSHash() would improve things on the > monitoring side. pg_dsm_registry_allocations would give users the > total memory used by pgstats, and once my per-kind DSA proposal > [1] lands, kinds with a dedicated hash would each have their own > entry in pg_dsm_registry_allocations giving accurate per-kind > memory accounting. > > pg_stat_kind_info could also expose the shmem allocation name > as a column, so users can join back to > pg_dsm_registry_allocations. And once [1] lands, > pg_stat_kind_info would also expose whether a kind uses > the shared or dedicated hash. > > I think to switch to GetNamedDSHash(), we would need to > return the DSA area from GetNamedDSHash() so entry body > allocations can use the same DSA that backs the hash. I already > have a patch for this [2]. More thought is needed, but I can look > into it. > > That said, the doc warning for entry_size is still needed. > entry_count * entry_size cannot give you actual memory usage > since it excludes hash overhead, entry headers, etc. > pg_dsm_registry_allocations is where users should look for > real memory numbers, not pg_stat_kind_info. > > [1] > https://www.postgresql.org/message-id/CAA5RZ0supQBxSkh=CWB39=j+cl3hhclpki3tcbk0b1r4fes...@mail.gmail.com > [2] > https://www.postgresql.org/message-id/flat/caa5rz0tkfcvqfnmztavm42h63ha2haf_c4mbjnwqkaw30cp...@mail.gmail.com I think this is a good idea. Attached is a v2. I wonder if we should also expose PgStat_KindInfo::shared_size. It is another piece of metadata. -- Tristan Partin PostgreSQL Contributors Team AWS (https://aws.amazon.com)
From a1396113b73e418709451dc05b4ba2f53e95ac91 Mon Sep 17 00:00:00 2001 From: Tristan Partin <[email protected]> Date: Mon, 6 Jul 2026 22:30:12 +0000 Subject: [PATCH v2] Add entry_size column to pg_stat_kind_info pg_stat_kind_info lacked a way to inspect the size of the statistics data payload for each registered kind. The column represents PgStat_KindInfo::shared_data_len, which is the size of the serializable statistics data payload, excluding shared memory wrapper overhead such as locking structures and hash table bookkeeping. Signed-off-by: Tristan Partin <[email protected]> --- doc/src/sgml/monitoring.sgml | 22 ++++++++++++ src/backend/catalog/system_views.sql | 3 +- src/backend/utils/activity/pgstat_kind.c | 4 ++- src/include/catalog/pg_proc.dat | 6 ++-- .../test_custom_stats/t/001_custom_stats.pl | 8 ++--- src/test/regress/expected/rules.out | 5 +-- src/test/regress/expected/stats.out | 34 +++++++++---------- src/test/regress/sql/stats.sql | 4 +-- 8 files changed, 56 insertions(+), 30 deletions(-) diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index a209e891b18..d38afc19581 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -3589,9 +3589,31 @@ description | Waiting for a newly initialized WAL file to reach durable storage </para> </entry> </row> + + <row> + <entry role="catalog_table_entry"> + <para role="column_definition"> + <structfield>entry_size</structfield> <type>bigint</type> + </para> + <para> + Size of the statistics data for each entry of this kind in bytes. This + reflects the statistics payload only, and does not include any shared + memory overhead. + </para> + </entry> + </row> </tbody> </tgroup> </table> + + <warning> + <para> + <varname>entry_count</varname> multiplied by <varname>entry_size</varname> + is not an accurate measure of the total memory used by a statistics kind. + Refer to <xref linkend="view-pg-dsm-registry-allocations"/> for data on + dynamic shared memory allocations. + </para> + </warning> </sect2> <sect2 id="monitoring-pg-stat-lock-view"> diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 090281a03dd..2cdbcacaa91 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -1290,7 +1290,8 @@ CREATE VIEW pg_stat_kind_info AS k.fixed_amount, k.accessed_across_databases, k.write_to_file, - k.entry_count + k.entry_count, + k.entry_size FROM pg_stat_get_kind_info() k; CREATE VIEW pg_stat_wal AS diff --git a/src/backend/utils/activity/pgstat_kind.c b/src/backend/utils/activity/pgstat_kind.c index 6c53b7e49bf..e9e636b34aa 100644 --- a/src/backend/utils/activity/pgstat_kind.c +++ b/src/backend/utils/activity/pgstat_kind.c @@ -31,7 +31,7 @@ Datum pg_stat_get_kind_info(PG_FUNCTION_ARGS) { -#define PG_STAT_KIND_INFO_COLS 7 +#define PG_STAT_KIND_INFO_COLS 8 ReturnSetInfo *rsinfo; InitMaterializedSRF(fcinfo, 0); @@ -64,6 +64,8 @@ pg_stat_get_kind_info(PG_FUNCTION_ARGS) else nulls[6] = true; + values[7] = Int64GetDatum((int64) pgstat_get_entry_len(kind)); + tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls); } diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index f8a021987b5..79e74f9923b 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -6078,9 +6078,9 @@ { oid => '8683', descr => 'statistics: information about statistics kinds', proname => 'pg_stat_get_kind_info', prorows => '20', proretset => 't', provolatile => 'v', proparallel => 'r', prorettype => 'record', - proargtypes => '', proallargtypes => '{int4,text,bool,bool,bool,bool,int8}', - proargmodes => '{o,o,o,o,o,o,o}', - proargnames => '{id,name,builtin,fixed_amount,accessed_across_databases,write_to_file,entry_count}', + proargtypes => '', proallargtypes => '{int4,text,bool,bool,bool,bool,int8,int8}', + proargmodes => '{o,o,o,o,o,o,o,o}', + proargnames => '{id,name,builtin,fixed_amount,accessed_across_databases,write_to_file,entry_count,entry_size}', prosrc => 'pg_stat_get_kind_info' }, { oid => '1136', descr => 'statistics: information about WAL activity', diff --git a/src/test/modules/test_custom_stats/t/001_custom_stats.pl b/src/test/modules/test_custom_stats/t/001_custom_stats.pl index 69f2284229e..ee291f4ac13 100644 --- a/src/test/modules/test_custom_stats/t/001_custom_stats.pl +++ b/src/test/modules/test_custom_stats/t/001_custom_stats.pl @@ -31,12 +31,12 @@ my $result = $node->safe_psql( 'postgres', q(SELECT id, name, builtin, fixed_amount, accessed_across_databases, - write_to_file + write_to_file, entry_size > 0 FROM pg_stat_kind_info WHERE name LIKE 'test_custom%' ORDER BY id)); -is( $result, - qq{25|test_custom_var_stats|f|f|t|t -26|test_custom_fixed_stats|f|t|f|t}, +is($result, + qq{25|test_custom_var_stats|f|f|t|t|t +26|test_custom_fixed_stats|f|t|f|t|t}, "custom stats kinds visible in pg_stat_kind_info"); # Create entries for variable-sized stats. diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 6a3341356da..e403d1c8747 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1973,8 +1973,9 @@ pg_stat_kind_info| SELECT id, fixed_amount, accessed_across_databases, write_to_file, - entry_count - FROM pg_stat_get_kind_info() k(id, name, builtin, fixed_amount, accessed_across_databases, write_to_file, entry_count); + entry_count, + entry_size + FROM pg_stat_get_kind_info() k(id, name, builtin, fixed_amount, accessed_across_databases, write_to_file, entry_count, entry_size); pg_stat_lock| SELECT locktype, waits, wait_time, diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out index e230356de13..45936eccaf8 100644 --- a/src/test/regress/expected/stats.out +++ b/src/test/regress/expected/stats.out @@ -107,26 +107,26 @@ walwriter|wal|normal (88 rows) \a -- List of registered statistics kinds. -SELECT id, name, fixed_amount, - accessed_across_databases AS across_db, write_to_file +SELECT id, name, fixed_amount, accessed_across_databases AS across_db, + write_to_file, entry_size > 0 AS has_entry_size FROM pg_stat_kind_info WHERE builtin ORDER BY id; - id | name | fixed_amount | across_db | write_to_file -----+--------------+--------------+-----------+--------------- - 1 | database | f | t | t - 2 | relation | f | f | t - 3 | function | f | f | t - 4 | replslot | f | t | t - 5 | subscription | f | t | t - 6 | backend | f | t | f - 7 | archiver | t | f | t - 8 | bgwriter | t | f | t - 9 | checkpointer | t | f | t - 10 | io | t | f | t - 11 | lock | t | f | t - 12 | slru | t | f | t - 13 | wal | t | f | t + id | name | fixed_amount | across_db | write_to_file | has_entry_size +----+--------------+--------------+-----------+---------------+---------------- + 1 | database | f | t | t | t + 2 | relation | f | f | t | t + 3 | function | f | f | t | t + 4 | replslot | f | t | t | t + 5 | subscription | f | t | t | t + 6 | backend | f | t | f | t + 7 | archiver | t | f | t | t + 8 | bgwriter | t | f | t | t + 9 | checkpointer | t | f | t | t + 10 | io | t | f | t | t + 11 | lock | t | f | t | t + 12 | slru | t | f | t | t + 13 | wal | t | f | t | t (13 rows) -- ensure that both seqscan and indexscan plans are allowed diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql index 4c265d1245c..554e6068aa5 100644 --- a/src/test/regress/sql/stats.sql +++ b/src/test/regress/sql/stats.sql @@ -15,8 +15,8 @@ SELECT backend_type, object, context FROM pg_stat_io \a -- List of registered statistics kinds. -SELECT id, name, fixed_amount, - accessed_across_databases AS across_db, write_to_file +SELECT id, name, fixed_amount, accessed_across_databases AS across_db, + write_to_file, entry_size > 0 AS has_entry_size FROM pg_stat_kind_info WHERE builtin ORDER BY id; -- Tristan Partin https://tristan.partin.io
