Skip to content

Commit 4185838

Browse files
andrew-j-metronomeCommitfest Bot
authored andcommitted
Adding pg_stat_muiltixact view to allow membership usage telemetry
1 parent 6551a05 commit 4185838

File tree

16 files changed

+435
-4
lines changed

16 files changed

+435
-4
lines changed

doc/src/sgml/monitoring.sgml

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,8 +278,9 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
278278
shared memory statistics) in the views
279279
<structname>pg_stat_xact_all_tables</structname>,
280280
<structname>pg_stat_xact_sys_tables</structname>,
281-
<structname>pg_stat_xact_user_tables</structname>, and
282-
<structname>pg_stat_xact_user_functions</structname>. These numbers do not act as
281+
<structname>pg_stat_xact_user_tables</structname>,
282+
<structname>pg_stat_xact_user_functions</structname> and
283+
<structname>pg_stat_multixact</structname>. These numbers do not act as
283284
stated above; instead they update continuously throughout the transaction.
284285
</para>
285286

@@ -493,6 +494,14 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
493494
</entry>
494495
</row>
495496

497+
<row>
498+
<entry><structname>pg_stat_multixact</structname><indexterm><primary>pg_stat_multixact</primary></indexterm></entry>
499+
<entry>One row only, showing statistics about multixact membership consumption. See
500+
<link linkend="monitoring-pg-stat-multixact-view">
501+
<structname>pg_stat_multixact</structname></link> for details.
502+
</entry>
503+
</row>
504+
496505
<row>
497506
<entry><structname>pg_stat_replication_slots</structname><indexterm><primary>pg_stat_replication_slots</primary></indexterm></entry>
498507
<entry>One row per replication slot, showing statistics about the
@@ -3254,6 +3263,56 @@ description | Waiting for a newly initialized WAL file to reach durable storage
32543263
</para>
32553264
</sect2>
32563265

3266+
<sect2 id="monitoring-pg-stat-multixact-view">
3267+
<title><structname>pg_stat_multixact</structname></title>
3268+
3269+
<indexterm>
3270+
<primary>pg_stat_multixact</primary>
3271+
</indexterm>
3272+
3273+
<para>
3274+
The <structname>pg_stat_multixact</structname> view will always have
3275+
a single row, containing data about multixact membership consumption
3276+
of the cluster.
3277+
</para>
3278+
3279+
<table id="pg-stat-multixact-view" xreflabel="pg_stat_multixact">
3280+
<title><structname>pg_stat_multixact</structname> View</title>
3281+
<tgroup cols="1">
3282+
<thead>
3283+
<row>
3284+
<entry role="catalog_table_entry"><para role="column_definition">
3285+
Column Type
3286+
</para>
3287+
<para>
3288+
Description
3289+
</para></entry>
3290+
</row>
3291+
</thead>
3292+
3293+
<tbody>
3294+
<row>
3295+
<entry role="catalog_table_entry"><para role="column_definition">
3296+
<structfield>update_timestamp</structfield> <type>timestamp with time zone</type>
3297+
</para>
3298+
<para>
3299+
Time at which the statistic was last updated.
3300+
</para></entry>
3301+
</row>
3302+
3303+
<row>
3304+
<entry role="catalog_table_entry"><para role="column_definition">
3305+
<structfield>members</structfield> <type>bigint</type>
3306+
</para>
3307+
<para>
3308+
Number of multixact members in use.
3309+
</para></entry>
3310+
</row>
3311+
</tbody>
3312+
</tgroup>
3313+
</table>
3314+
</sect2>
3315+
32573316
<sect2 id="monitoring-pg-stat-wal-view">
32583317
<title><structname>pg_stat_wal</structname></title>
32593318

src/backend/access/transam/multixact.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,12 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
12591259

12601260
MultiXactState->nextOffset += nmembers;
12611261

1262+
/*
1263+
* Record multixact membership space telemetry while we have the lock. We
1264+
* do not use the saved variables above because they are stale.
1265+
*/
1266+
pgstat_update_multixact_stats(MultiXactState->nextOffset - MultiXactState->oldestOffset);
1267+
12621268
LWLockRelease(MultiXactGenLock);
12631269

12641270
debug_elog4(DEBUG2, "GetNew: returning %u offset %u", result, *offset);
@@ -2927,6 +2933,9 @@ MultiXactMemberFreezeThreshold(void)
29272933
if (!ReadMultiXactCounts(&multixacts, &members))
29282934
return 0;
29292935

2936+
/* Record the number of multixact members. */
2937+
pgstat_update_multixact_stats(members);
2938+
29302939
/* If member space utilization is low, no special action is required. */
29312940
if (members <= MULTIXACT_MEMBER_SAFE_THRESHOLD)
29322941
return autovacuum_multixact_freeze_max_age;

src/backend/catalog/system_views.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,6 +1209,11 @@ CREATE VIEW pg_stat_wal AS
12091209
w.stats_reset
12101210
FROM pg_stat_get_wal() w;
12111211

1212+
CREATE VIEW pg_stat_multixact AS
1213+
SELECT
1214+
pg_stat_get_multixact_update_timestamp() AS update_timestamp,
1215+
pg_stat_get_multixact_members() AS members;
1216+
12121217
CREATE VIEW pg_stat_progress_analyze AS
12131218
SELECT
12141219
S.pid AS pid, S.datid AS datid, D.datname AS datname,

src/backend/utils/activity/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ OBJS = \
2626
pgstat_database.o \
2727
pgstat_function.o \
2828
pgstat_io.o \
29+
pgstat_multixact.o \
2930
pgstat_relation.o \
3031
pgstat_replslot.o \
3132
pgstat_shmem.o \

src/backend/utils/activity/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ backend_sources += files(
1111
'pgstat_database.c',
1212
'pgstat_function.c',
1313
'pgstat_io.c',
14+
'pgstat_multixact.c',
1415
'pgstat_relation.c',
1516
'pgstat_replslot.c',
1617
'pgstat_shmem.c',

src/backend/utils/activity/pgstat.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,24 @@ static const PgStat_KindInfo pgstat_kind_builtin_infos[PGSTAT_KIND_BUILTIN_SIZE]
480480
.reset_all_cb = pgstat_wal_reset_all_cb,
481481
.snapshot_cb = pgstat_wal_snapshot_cb,
482482
},
483+
484+
[PGSTAT_KIND_MULTIXACT] = {
485+
.name = "multixact",
486+
487+
.fixed_amount = true,
488+
.write_to_file = true,
489+
490+
.snapshot_ctl_off = offsetof(PgStat_Snapshot, multixact),
491+
.shared_ctl_off = offsetof(PgStat_ShmemControl, multixact),
492+
.shared_data_off = offsetof(PgStatShared_MultiXact, stats),
493+
.shared_data_len = sizeof(((PgStatShared_MultiXact *) 0)->stats),
494+
495+
.init_shmem_cb = pgstat_multixact_init_shmem_cb,
496+
.flush_static_cb = pgstat_flush_multixact_cb,
497+
.have_static_pending_cb = pgstat_multixact_have_pending_cb,
498+
.reset_all_cb = pgstat_multixact_reset_all_cb,
499+
.snapshot_cb = pgstat_multixact_snapshot_cb,
500+
},
483501
};
484502

485503
/*
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/* -------------------------------------------------------------------------
2+
*
3+
* pgstat_multixact.c
4+
* Implementation of multixact statistics.
5+
*
6+
* This file contains the implementation of multixact statistics. It is kept
7+
* separate from pgstat.c to enforce the line between the statistics access /
8+
* storage implementation and the details about individual types of
9+
* statistics.
10+
*
11+
* Copyright (c) 2001-2025, PostgreSQL Global Development Group
12+
*
13+
* IDENTIFICATION
14+
* src/backend/utils/activity/pgstat_multixact.c
15+
* -------------------------------------------------------------------------
16+
*/
17+
18+
#include "postgres.h"
19+
#include "utils/pgstat_internal.h"
20+
#include "utils/timestamp.h"
21+
22+
static bool have_multixact_stats = false;
23+
24+
/*
25+
* pgstat_fetch_stat_multixact()
26+
*
27+
* Support function for the SQL-callable pgstat* functions. Returns
28+
* a pointer to the multixact statistics struct.
29+
*/
30+
PgStat_MultiXactStats *
31+
pgstat_fetch_stat_multixact(void)
32+
{
33+
pgstat_snapshot_fixed(PGSTAT_KIND_MULTIXACT);
34+
35+
return &pgStatLocal.snapshot.multixact;
36+
}
37+
38+
bool
39+
pgstat_multixact_have_pending_cb(void)
40+
{
41+
return have_multixact_stats;
42+
}
43+
44+
void
45+
pgstat_update_multixact_stats(uint32 nmembers)
46+
{
47+
PgStat_MultiXactStats * local_stats;
48+
TimestampTz now;
49+
50+
now = GetCurrentTimestamp();
51+
local_stats = &pgStatLocal.snapshot.multixact;
52+
local_stats->num_members_used = nmembers;
53+
local_stats->stat_update_timestamp = now;
54+
have_multixact_stats = true;
55+
}
56+
57+
/*
58+
* Report multixact statistics. If nowait is true, then this function
59+
* will return if the lock is currently being held.
60+
*
61+
* This function returns true if the lock could not be acquired. Otherwise, false.
62+
*/
63+
bool
64+
pgstat_flush_multixact_cb(bool nowait)
65+
{
66+
PgStatShared_MultiXact *stats_shmem;
67+
LWLock *shared_lock;
68+
69+
stats_shmem = &pgStatLocal.shmem->multixact;
70+
shared_lock = &pgStatLocal.shmem->multixact.lock;
71+
72+
// Since this statistic is a gauge, we have to check timestamps
73+
// of the shared statistic and the local statistic. If ours is
74+
// larger, then we can overwrite the shared statistic.
75+
if (!nowait)
76+
LWLockAcquire(shared_lock, LW_EXCLUSIVE);
77+
else if (!LWLockConditionalAcquire(shared_lock, LW_EXCLUSIVE))
78+
return true;
79+
80+
if (pgStatLocal.snapshot.multixact.stat_update_timestamp <= stats_shmem->stats.stat_update_timestamp)
81+
{
82+
// Return if our stats are <= the latest update.
83+
LWLockRelease(shared_lock);
84+
have_multixact_stats = false;
85+
return false;
86+
}
87+
88+
// Update multixact member usage and latest timestamp.
89+
stats_shmem->stats.num_members_used = pgStatLocal.snapshot.multixact.num_members_used;
90+
stats_shmem->stats.stat_update_timestamp = pgStatLocal.snapshot.multixact.stat_update_timestamp;
91+
have_multixact_stats = false;
92+
93+
LWLockRelease(shared_lock);
94+
return false;
95+
}
96+
97+
void
98+
pgstat_multixact_init_shmem_cb(void *stats)
99+
{
100+
PgStatShared_MultiXact *stats_shmem;
101+
102+
stats_shmem = (PgStatShared_MultiXact *) stats;
103+
LWLockInitialize(&stats_shmem->lock, LWTRANCHE_PGSTATS_DATA);
104+
}
105+
106+
void
107+
pgstat_multixact_snapshot_cb(void)
108+
{
109+
PgStat_MultiXactStats *local_snapshot;
110+
PgStatShared_MultiXact *stats_shmem;
111+
112+
local_snapshot = &pgStatLocal.snapshot.multixact;
113+
stats_shmem = &pgStatLocal.shmem->multixact;
114+
115+
LWLockAcquire(&stats_shmem->lock, LW_SHARED);
116+
if (local_snapshot->stat_update_timestamp < stats_shmem->stats.stat_update_timestamp)
117+
{
118+
local_snapshot->stat_update_timestamp = stats_shmem->stats.stat_update_timestamp;
119+
local_snapshot->num_members_used = stats_shmem->stats.num_members_used;
120+
}
121+
LWLockRelease(&stats_shmem->lock);
122+
}
123+
124+
void
125+
pgstat_multixact_reset_all_cb(TimestampTz ts)
126+
{
127+
PgStatShared_MultiXact *stats_shmem;
128+
129+
stats_shmem = &pgStatLocal.shmem->multixact;
130+
LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE);
131+
memset(&stats_shmem->stats, 0, sizeof(stats_shmem->stats));
132+
LWLockRelease(&stats_shmem->lock);
133+
}

src/backend/utils/adt/pgstatfuncs.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,6 +1304,18 @@ pg_stat_get_buf_alloc(PG_FUNCTION_ARGS)
13041304
PG_RETURN_INT64(pgstat_fetch_stat_bgwriter()->buf_alloc);
13051305
}
13061306

1307+
Datum
1308+
pg_stat_get_multixact_members(PG_FUNCTION_ARGS)
1309+
{
1310+
PG_RETURN_INT64(pgstat_fetch_stat_multixact()->num_members_used);
1311+
}
1312+
1313+
Datum
1314+
pg_stat_get_multixact_update_timestamp(PG_FUNCTION_ARGS)
1315+
{
1316+
PG_RETURN_TIMESTAMPTZ(pgstat_fetch_stat_multixact()->stat_update_timestamp);
1317+
}
1318+
13071319
/*
13081320
* When adding a new column to the pg_stat_io view and the
13091321
* pg_stat_get_backend_io() function, add a new enum value here above
@@ -1883,6 +1895,7 @@ pg_stat_reset_shared(PG_FUNCTION_ARGS)
18831895
XLogPrefetchResetStats();
18841896
pgstat_reset_of_kind(PGSTAT_KIND_SLRU);
18851897
pgstat_reset_of_kind(PGSTAT_KIND_WAL);
1898+
pgstat_reset_of_kind(PGSTAT_KIND_MULTIXACT);
18861899

18871900
PG_RETURN_VOID();
18881901
}
@@ -1903,6 +1916,8 @@ pg_stat_reset_shared(PG_FUNCTION_ARGS)
19031916
pgstat_reset_of_kind(PGSTAT_KIND_SLRU);
19041917
else if (strcmp(target, "wal") == 0)
19051918
pgstat_reset_of_kind(PGSTAT_KIND_WAL);
1919+
else if (strcmp(target, "multixact") == 0)
1920+
pgstat_reset_of_kind(PGSTAT_KIND_MULTIXACT);
19061921
else
19071922
ereport(ERROR,
19081923
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),

src/include/catalog/pg_proc.dat

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5992,6 +5992,14 @@
59925992
proname => 'pg_stat_get_buf_alloc', provolatile => 's', proparallel => 'r',
59935993
prorettype => 'int8', proargtypes => '', prosrc => 'pg_stat_get_buf_alloc' },
59945994

5995+
{ oid => '9999', descr => 'statistics: number of multixact members in use',
5996+
proname => 'pg_stat_get_multixact_members', provolatile => 's', proparallel => 'r',
5997+
prorettype => 'int8', proargtypes => '', prosrc => 'pg_stat_get_multixact_members' },
5998+
{ oid => '9998', descr => 'statistics: timestamp of the last time the multixact members count was updated',
5999+
proname => 'pg_stat_get_multixact_update_timestamp', provolatile => 's',
6000+
proparallel => 'r', prorettype => 'timestamptz', proargtypes => '',
6001+
prosrc => 'pg_stat_get_multixact_update_timestamp' },
6002+
59956003
{ oid => '6214', descr => 'statistics: per backend type IO statistics',
59966004
proname => 'pg_stat_get_io', prorows => '30', proretset => 't',
59976005
provolatile => 'v', proparallel => 'r', prorettype => 'record',

src/include/pgstat.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,19 @@ typedef struct PgStat_CheckpointerStats
265265
TimestampTz stat_reset_timestamp;
266266
} PgStat_CheckpointerStats;
267267

268+
/* --------
269+
* PgStat_MultiXactStats MultiXact stats
270+
*
271+
* This struct should contain only actual event counters, because we make use
272+
* of pg_memory_is_all_zeros() to detect whether there are any stats updates
273+
* to apply.
274+
* ---------
275+
*/
276+
typedef struct PgStat_MultiXactStats
277+
{
278+
PgStat_Counter num_members_used;
279+
TimestampTz stat_update_timestamp;
280+
} PgStat_MultiXactStats;
268281

269282
/*
270283
* Types related to counting IO operations
@@ -579,6 +592,12 @@ extern void pgstat_report_checkpointer(void);
579592
extern PgStat_CheckpointerStats *pgstat_fetch_stat_checkpointer(void);
580593

581594

595+
/*
596+
* Functions in pgstat_multixact.c
597+
*/
598+
extern PgStat_MultiXactStats *pgstat_fetch_stat_multixact(void);
599+
extern void pgstat_update_multixact_stats(uint32 nmembers);
600+
582601
/*
583602
* Functions in pgstat_io.c
584603
*/

0 commit comments

Comments
 (0)