PostgreSQL Source Code git master
pgstat_xact.c
Go to the documentation of this file.
1/* -------------------------------------------------------------------------
2 *
3 * pgstat_xact.c
4 * Transactional integration for the cumulative statistics system.
5 *
6 * Copyright (c) 2001-2025, PostgreSQL Global Development Group
7 *
8 * IDENTIFICATION
9 * src/backend/utils/activity/pgstat_xact.c
10 * -------------------------------------------------------------------------
11 */
12
13#include "postgres.h"
14
15#include "access/xact.h"
16#include "pgstat.h"
17#include "utils/memutils.h"
19
20
22{
27
28
29static void AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit);
31 bool isCommit, int nestDepth);
32
34
35
36/*
37 * Called from access/transam/xact.c at top-level transaction commit/abort.
38 */
39void
40AtEOXact_PgStat(bool isCommit, bool parallel)
41{
42 PgStat_SubXactStatus *xact_state;
43
44 AtEOXact_PgStat_Database(isCommit, parallel);
45
46 /* handle transactional stats information */
47 xact_state = pgStatXactStack;
48 if (xact_state != NULL)
49 {
50 Assert(xact_state->nest_level == 1);
51 Assert(xact_state->prev == NULL);
52
53 AtEOXact_PgStat_Relations(xact_state, isCommit);
54 AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
55 }
56 pgStatXactStack = NULL;
57
58 /* Make sure any stats snapshot is thrown away */
60}
61
62/*
63 * When committing, drop stats for objects dropped in the transaction. When
64 * aborting, drop stats for objects created in the transaction.
65 */
66static void
68{
70 int not_freed_count = 0;
71
72 if (dclist_count(&xact_state->pending_drops) == 0)
73 return;
74
75 dclist_foreach_modify(iter, &xact_state->pending_drops)
76 {
79 xl_xact_stats_item *it = &pending->item;
80 uint64 objid = ((uint64) it->objid_hi) << 32 | it->objid_lo;
81
82 if (isCommit && !pending->is_create)
83 {
84 /*
85 * Transaction that dropped an object committed. Drop the stats
86 * too.
87 */
88 if (!pgstat_drop_entry(it->kind, it->dboid, objid))
89 not_freed_count++;
90 }
91 else if (!isCommit && pending->is_create)
92 {
93 /*
94 * Transaction that created an object aborted. Drop the stats
95 * associated with the object.
96 */
97 if (!pgstat_drop_entry(it->kind, it->dboid, objid))
98 not_freed_count++;
99 }
100
101 dclist_delete_from(&xact_state->pending_drops, &pending->node);
102 pfree(pending);
103 }
104
105 if (not_freed_count > 0)
107}
108
109/*
110 * Called from access/transam/xact.c at subtransaction commit/abort.
111 */
112void
113AtEOSubXact_PgStat(bool isCommit, int nestDepth)
114{
115 PgStat_SubXactStatus *xact_state;
116
117 /* merge the sub-transaction's transactional stats into the parent */
118 xact_state = pgStatXactStack;
119 if (xact_state != NULL &&
120 xact_state->nest_level >= nestDepth)
121 {
122 /* delink xact_state from stack immediately to simplify reuse case */
123 pgStatXactStack = xact_state->prev;
124
125 AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
126 AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
127
128 pfree(xact_state);
129 }
130}
131
132/*
133 * Like AtEOXact_PgStat_DroppedStats(), but for subtransactions.
134 */
135static void
137 bool isCommit, int nestDepth)
138{
139 PgStat_SubXactStatus *parent_xact_state;
141 int not_freed_count = 0;
142
143 if (dclist_count(&xact_state->pending_drops) == 0)
144 return;
145
146 parent_xact_state = pgstat_get_xact_stack_level(nestDepth - 1);
147
148 dclist_foreach_modify(iter, &xact_state->pending_drops)
149 {
152 xl_xact_stats_item *it = &pending->item;
153 uint64 objid = ((uint64) it->objid_hi) << 32 | it->objid_lo;
154
155 dclist_delete_from(&xact_state->pending_drops, &pending->node);
156
157 if (!isCommit && pending->is_create)
158 {
159 /*
160 * Subtransaction creating a new stats object aborted. Drop the
161 * stats object.
162 */
163 if (!pgstat_drop_entry(it->kind, it->dboid, objid))
164 not_freed_count++;
165 pfree(pending);
166 }
167 else if (isCommit)
168 {
169 /*
170 * Subtransaction dropping a stats object committed. Can't yet
171 * remove the stats object, the surrounding transaction might
172 * still abort. Pass it on to the parent.
173 */
174 dclist_push_tail(&parent_xact_state->pending_drops, &pending->node);
175 }
176 else
177 {
178 pfree(pending);
179 }
180 }
181
182 Assert(dclist_count(&xact_state->pending_drops) == 0);
183 if (not_freed_count > 0)
185}
186
187/*
188 * Save the transactional stats state at 2PC transaction prepare.
189 */
190void
192{
193 PgStat_SubXactStatus *xact_state;
194
195 xact_state = pgStatXactStack;
196 if (xact_state != NULL)
197 {
198 Assert(xact_state->nest_level == 1);
199 Assert(xact_state->prev == NULL);
200
201 AtPrepare_PgStat_Relations(xact_state);
202 }
203}
204
205/*
206 * Clean up after successful PREPARE.
207 *
208 * Note: AtEOXact_PgStat is not called during PREPARE.
209 */
210void
212{
213 PgStat_SubXactStatus *xact_state;
214
215 /*
216 * We don't bother to free any of the transactional state, since it's all
217 * in TopTransactionContext and will go away anyway.
218 */
219 xact_state = pgStatXactStack;
220 if (xact_state != NULL)
221 {
222 Assert(xact_state->nest_level == 1);
223 Assert(xact_state->prev == NULL);
224
226 }
227 pgStatXactStack = NULL;
228
229 /* Make sure any stats snapshot is thrown away */
231}
232
233/*
234 * Ensure (sub)transaction stack entry for the given nest_level exists, adding
235 * it if needed.
236 */
239{
240 PgStat_SubXactStatus *xact_state;
241
242 xact_state = pgStatXactStack;
243 if (xact_state == NULL || xact_state->nest_level != nest_level)
244 {
245 xact_state = (PgStat_SubXactStatus *)
247 sizeof(PgStat_SubXactStatus));
248 dclist_init(&xact_state->pending_drops);
249 xact_state->nest_level = nest_level;
250 xact_state->prev = pgStatXactStack;
251 xact_state->first = NULL;
252 pgStatXactStack = xact_state;
253 }
254 return xact_state;
255}
256
257/*
258 * Get stat items that need to be dropped at commit / abort.
259 *
260 * When committing, stats for objects that have been dropped in the
261 * transaction are returned. When aborting, stats for newly created objects are
262 * returned.
263 *
264 * Used by COMMIT / ABORT and 2PC PREPARE processing when building their
265 * respective WAL records, to ensure stats are dropped in case of a crash / on
266 * standbys.
267 *
268 * The list of items is allocated in CurrentMemoryContext and must be freed by
269 * the caller (directly or via memory context reset).
270 */
271int
273{
275 int nitems = 0;
276 dlist_iter iter;
277
278 if (xact_state == NULL)
279 return 0;
280
281 /*
282 * We expect to be called for subtransaction abort (which logs a WAL
283 * record), but not for subtransaction commit (which doesn't).
284 */
285 Assert(!isCommit || xact_state->nest_level == 1);
286 Assert(!isCommit || xact_state->prev == NULL);
287
288 *items = palloc(dclist_count(&xact_state->pending_drops)
289 * sizeof(xl_xact_stats_item));
290
291 dclist_foreach(iter, &xact_state->pending_drops)
292 {
295
296 if (isCommit && pending->is_create)
297 continue;
298 if (!isCommit && !pending->is_create)
299 continue;
300
301 Assert(nitems < dclist_count(&xact_state->pending_drops));
302 (*items)[nitems++] = pending->item;
303 }
304
305 return nitems;
306}
307
308/*
309 * Execute scheduled drops post-commit. Called from xact_redo_commit() /
310 * xact_redo_abort() during recovery, and from FinishPreparedTransaction()
311 * during normal 2PC COMMIT/ABORT PREPARED processing.
312 */
313void
315{
316 int not_freed_count = 0;
317
318 if (ndrops == 0)
319 return;
320
321 for (int i = 0; i < ndrops; i++)
322 {
323 xl_xact_stats_item *it = &items[i];
324 uint64 objid = ((uint64) it->objid_hi) << 32 | it->objid_lo;
325
326 if (!pgstat_drop_entry(it->kind, it->dboid, objid))
327 not_freed_count++;
328 }
329
330 if (not_freed_count > 0)
332}
333
334static void
335create_drop_transactional_internal(PgStat_Kind kind, Oid dboid, uint64 objid, bool is_create)
336{
337 int nest_level = GetCurrentTransactionNestLevel();
338 PgStat_SubXactStatus *xact_state;
341
342 xact_state = pgstat_get_xact_stack_level(nest_level);
343
344 drop->is_create = is_create;
345 drop->item.kind = kind;
346 drop->item.dboid = dboid;
347 drop->item.objid_lo = (uint32) objid;
348 drop->item.objid_hi = (uint32) (objid >> 32);
349
350 dclist_push_tail(&xact_state->pending_drops, &drop->node);
351}
352
353/*
354 * Create a stats entry for a newly created database object in a transactional
355 * manner.
356 *
357 * I.e. if the current (sub-)transaction aborts, the stats entry will also be
358 * dropped.
359 */
360void
362{
363 if (pgstat_get_entry_ref(kind, dboid, objid, false, NULL))
364 {
366 errmsg("resetting existing statistics for kind %s, db=%u, oid=%llu",
367 (pgstat_get_kind_info(kind))->name, dboid,
368 (unsigned long long) objid));
369
370 pgstat_reset(kind, dboid, objid);
371 }
372
373 create_drop_transactional_internal(kind, dboid, objid, /* create */ true);
374}
375
376/*
377 * Drop a stats entry for a just dropped database object in a transactional
378 * manner.
379 *
380 * I.e. if the current (sub-)transaction aborts, the stats entry will stay
381 * alive.
382 */
383void
385{
386 create_drop_transactional_internal(kind, dboid, objid, /* create */ false);
387}
#define Assert(condition)
Definition: c.h:815
uint64_t uint64
Definition: c.h:489
uint32_t uint32
Definition: c.h:488
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define WARNING
Definition: elog.h:36
#define ereport(elevel,...)
Definition: elog.h:149
#define dclist_container(type, membername, ptr)
Definition: ilist.h:947
static void dclist_push_tail(dclist_head *head, dlist_node *node)
Definition: ilist.h:709
static uint32 dclist_count(const dclist_head *head)
Definition: ilist.h:932
static void dclist_delete_from(dclist_head *head, dlist_node *node)
Definition: ilist.h:763
static void dclist_init(dclist_head *head)
Definition: ilist.h:671
#define dclist_foreach_modify(iter, lhead)
Definition: ilist.h:973
#define dclist_foreach(iter, lhead)
Definition: ilist.h:970
#define nitems(x)
Definition: indent.h:31
int i
Definition: isn.c:72
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1181
MemoryContext TopTransactionContext
Definition: mcxt.c:154
void pfree(void *pointer)
Definition: mcxt.c:1521
void * palloc(Size size)
Definition: mcxt.c:1317
void pgstat_reset(PgStat_Kind kind, Oid dboid, uint64 objid)
Definition: pgstat.c:870
const PgStat_KindInfo * pgstat_get_kind_info(PgStat_Kind kind)
Definition: pgstat.c:1454
void pgstat_clear_snapshot(void)
Definition: pgstat.c:918
void AtEOXact_PgStat_Database(bool isCommit, bool parallel)
#define PgStat_Kind
Definition: pgstat_kind.h:17
void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
void AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
void AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
void pgstat_request_entry_refs_gc(void)
Definition: pgstat_shmem.c:700
PgStat_EntryRef * pgstat_get_entry_ref(PgStat_Kind kind, Oid dboid, uint64 objid, bool create, bool *created_entry)
Definition: pgstat_shmem.c:444
bool pgstat_drop_entry(PgStat_Kind kind, Oid dboid, uint64 objid)
Definition: pgstat_shmem.c:953
static PgStat_SubXactStatus * pgStatXactStack
Definition: pgstat_xact.c:33
void AtPrepare_PgStat(void)
Definition: pgstat_xact.c:191
void pgstat_execute_transactional_drops(int ndrops, struct xl_xact_stats_item *items, bool is_redo)
Definition: pgstat_xact.c:314
void AtEOXact_PgStat(bool isCommit, bool parallel)
Definition: pgstat_xact.c:40
static void AtEOSubXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
Definition: pgstat_xact.c:136
void pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, uint64 objid)
Definition: pgstat_xact.c:384
void pgstat_create_transactional(PgStat_Kind kind, Oid dboid, uint64 objid)
Definition: pgstat_xact.c:361
struct PgStat_PendingDroppedStatsItem PgStat_PendingDroppedStatsItem
void PostPrepare_PgStat(void)
Definition: pgstat_xact.c:211
PgStat_SubXactStatus * pgstat_get_xact_stack_level(int nest_level)
Definition: pgstat_xact.c:238
static void create_drop_transactional_internal(PgStat_Kind kind, Oid dboid, uint64 objid, bool is_create)
Definition: pgstat_xact.c:335
int pgstat_get_transactional_drops(bool isCommit, xl_xact_stats_item **items)
Definition: pgstat_xact.c:272
void AtEOSubXact_PgStat(bool isCommit, int nestDepth)
Definition: pgstat_xact.c:113
static void AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit)
Definition: pgstat_xact.c:67
unsigned int Oid
Definition: postgres_ext.h:32
xl_xact_stats_item item
Definition: pgstat_xact.c:23
PgStat_TableXactStatus * first
struct PgStat_SubXactStatus * prev
dlist_node * cur
Definition: ilist.h:179
dlist_node * cur
Definition: ilist.h:200
uint32 objid_lo
Definition: xact.h:291
uint32 objid_hi
Definition: xact.h:292
static ItemArray items
Definition: test_tidstore.c:48
const char * name
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:928