Age Owner Branch data TLA Line data Source code
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"
18 : : #include "utils/pgstat_internal.h"
19 : :
20 : :
21 : : typedef struct PgStat_PendingDroppedStatsItem
22 : : {
23 : : xl_xact_stats_item item;
24 : : bool is_create;
25 : : dlist_node node;
26 : : } PgStat_PendingDroppedStatsItem;
27 : :
28 : :
29 : : static void AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit);
30 : : static void AtEOSubXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state,
31 : : bool isCommit, int nestDepth);
32 : :
33 : : static PgStat_SubXactStatus *pgStatXactStack = NULL;
34 : :
35 : :
36 : : /*
37 : : * Called from access/transam/xact.c at top-level transaction commit/abort.
38 : : */
39 : : void
1351 andres@anarazel.de 40 :CBC 331907 : AtEOXact_PgStat(bool isCommit, bool parallel)
41 : : {
42 : : PgStat_SubXactStatus *xact_state;
43 : :
44 : 331907 : AtEOXact_PgStat_Database(isCommit, parallel);
45 : :
46 : : /* handle transactional stats information */
47 : 331907 : xact_state = pgStatXactStack;
48 [ + + ]: 331907 : if (xact_state != NULL)
49 : : {
50 [ - + ]: 128485 : Assert(xact_state->nest_level == 1);
51 [ - + ]: 128485 : Assert(xact_state->prev == NULL);
52 : :
53 : 128485 : AtEOXact_PgStat_Relations(xact_state, isCommit);
54 : 128485 : AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
55 : : }
56 : 331907 : pgStatXactStack = NULL;
57 : :
58 : : /* Make sure any stats snapshot is thrown away */
59 : 331907 : pgstat_clear_snapshot();
60 : 331907 : }
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 : : */
66 : : static void
67 : 128485 : AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit)
68 : : {
69 : : dlist_mutable_iter iter;
70 : 128485 : int not_freed_count = 0;
71 : :
1141 drowley@postgresql.o 72 [ + + ]: 128485 : if (dclist_count(&xact_state->pending_drops) == 0)
1351 andres@anarazel.de 73 : 83756 : return;
74 : :
1141 drowley@postgresql.o 75 [ + - + + ]: 162684 : dclist_foreach_modify(iter, &xact_state->pending_drops)
76 : : {
1351 andres@anarazel.de 77 : 117955 : PgStat_PendingDroppedStatsItem *pending =
943 tgl@sss.pgh.pa.us 78 : 117955 : dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
1351 andres@anarazel.de 79 : 117955 : xl_xact_stats_item *it = &pending->item;
455 michael@paquier.xyz 80 : 117955 : uint64 objid = ((uint64) it->objid_hi) << 32 | it->objid_lo;
81 : :
1351 andres@anarazel.de 82 [ + + + + ]: 117955 : if (isCommit && !pending->is_create)
83 : : {
84 : : /*
85 : : * Transaction that dropped an object committed. Drop the stats
86 : : * too.
87 : : */
455 michael@paquier.xyz 88 [ + + ]: 44988 : if (!pgstat_drop_entry(it->kind, it->dboid, objid))
1351 andres@anarazel.de 89 : 4942 : not_freed_count++;
90 : : }
91 [ + + + + ]: 77909 : else if (!isCommit && pending->is_create)
92 : : {
93 : : /*
94 : : * Transaction that created an object aborted. Drop the stats
95 : : * associated with the object.
96 : : */
455 michael@paquier.xyz 97 [ - + ]: 2469 : if (!pgstat_drop_entry(it->kind, it->dboid, objid))
1351 andres@anarazel.de 98 :UBC 0 : not_freed_count++;
99 : : }
100 : :
1141 drowley@postgresql.o 101 :CBC 117955 : dclist_delete_from(&xact_state->pending_drops, &pending->node);
1351 andres@anarazel.de 102 : 117955 : pfree(pending);
103 : : }
104 : :
105 [ + + ]: 44729 : if (not_freed_count > 0)
106 : 1898 : pgstat_request_entry_refs_gc();
107 : : }
108 : :
109 : : /*
110 : : * Called from access/transam/xact.c at subtransaction commit/abort.
111 : : */
112 : : void
113 : 9140 : AtEOSubXact_PgStat(bool isCommit, int nestDepth)
114 : : {
115 : : PgStat_SubXactStatus *xact_state;
116 : :
117 : : /* merge the sub-transaction's transactional stats into the parent */
118 : 9140 : xact_state = pgStatXactStack;
119 [ + + ]: 9140 : if (xact_state != NULL &&
120 [ + + ]: 3791 : xact_state->nest_level >= nestDepth)
121 : : {
122 : : /* delink xact_state from stack immediately to simplify reuse case */
123 : 3318 : pgStatXactStack = xact_state->prev;
124 : :
125 : 3318 : AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
126 : 3318 : AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
127 : :
128 : 3318 : pfree(xact_state);
129 : : }
130 : 9140 : }
131 : :
132 : : /*
133 : : * Like AtEOXact_PgStat_DroppedStats(), but for subtransactions.
134 : : */
135 : : static void
136 : 3318 : AtEOSubXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state,
137 : : bool isCommit, int nestDepth)
138 : : {
139 : : PgStat_SubXactStatus *parent_xact_state;
140 : : dlist_mutable_iter iter;
141 : 3318 : int not_freed_count = 0;
142 : :
1141 drowley@postgresql.o 143 [ + + ]: 3318 : if (dclist_count(&xact_state->pending_drops) == 0)
1351 andres@anarazel.de 144 : 3234 : return;
145 : :
146 : 84 : parent_xact_state = pgstat_get_xact_stack_level(nestDepth - 1);
147 : :
1141 drowley@postgresql.o 148 [ + - + + ]: 247 : dclist_foreach_modify(iter, &xact_state->pending_drops)
149 : : {
1351 andres@anarazel.de 150 : 163 : PgStat_PendingDroppedStatsItem *pending =
943 tgl@sss.pgh.pa.us 151 : 163 : dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
1351 andres@anarazel.de 152 : 163 : xl_xact_stats_item *it = &pending->item;
455 michael@paquier.xyz 153 : 163 : uint64 objid = ((uint64) it->objid_hi) << 32 | it->objid_lo;
154 : :
1141 drowley@postgresql.o 155 : 163 : dclist_delete_from(&xact_state->pending_drops, &pending->node);
156 : :
1351 andres@anarazel.de 157 [ + + + + ]: 163 : if (!isCommit && pending->is_create)
158 : : {
159 : : /*
160 : : * Subtransaction creating a new stats object aborted. Drop the
161 : : * stats object.
162 : : */
455 michael@paquier.xyz 163 [ - + ]: 75 : if (!pgstat_drop_entry(it->kind, it->dboid, objid))
1351 andres@anarazel.de 164 :UBC 0 : not_freed_count++;
1351 andres@anarazel.de 165 :CBC 75 : pfree(pending);
166 : : }
167 [ + + ]: 88 : 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 : : */
1141 drowley@postgresql.o 174 : 61 : dclist_push_tail(&parent_xact_state->pending_drops, &pending->node);
175 : : }
176 : : else
177 : : {
1351 andres@anarazel.de 178 : 27 : pfree(pending);
179 : : }
180 : : }
181 : :
1141 drowley@postgresql.o 182 [ - + ]: 84 : Assert(dclist_count(&xact_state->pending_drops) == 0);
1351 andres@anarazel.de 183 [ - + ]: 84 : if (not_freed_count > 0)
1351 andres@anarazel.de 184 :UBC 0 : pgstat_request_entry_refs_gc();
185 : : }
186 : :
187 : : /*
188 : : * Save the transactional stats state at 2PC transaction prepare.
189 : : */
190 : : void
1351 andres@anarazel.de 191 :CBC 298 : AtPrepare_PgStat(void)
192 : : {
193 : : PgStat_SubXactStatus *xact_state;
194 : :
195 : 298 : xact_state = pgStatXactStack;
196 [ + + ]: 298 : if (xact_state != NULL)
197 : : {
198 [ - + ]: 291 : Assert(xact_state->nest_level == 1);
199 [ - + ]: 291 : Assert(xact_state->prev == NULL);
200 : :
201 : 291 : AtPrepare_PgStat_Relations(xact_state);
202 : : }
203 : 298 : }
204 : :
205 : : /*
206 : : * Clean up after successful PREPARE.
207 : : *
208 : : * Note: AtEOXact_PgStat is not called during PREPARE.
209 : : */
210 : : void
211 : 298 : PostPrepare_PgStat(void)
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 : 298 : xact_state = pgStatXactStack;
220 [ + + ]: 298 : if (xact_state != NULL)
221 : : {
222 [ - + ]: 291 : Assert(xact_state->nest_level == 1);
223 [ - + ]: 291 : Assert(xact_state->prev == NULL);
224 : :
225 : 291 : PostPrepare_PgStat_Relations(xact_state);
226 : : }
227 : 298 : pgStatXactStack = NULL;
228 : :
229 : : /* Make sure any stats snapshot is thrown away */
230 : 298 : pgstat_clear_snapshot();
231 : 298 : }
232 : :
233 : : /*
234 : : * Ensure (sub)transaction stack entry for the given nest_level exists, adding
235 : : * it if needed.
236 : : */
237 : : PgStat_SubXactStatus *
238 : 476111 : pgstat_get_xact_stack_level(int nest_level)
239 : : {
240 : : PgStat_SubXactStatus *xact_state;
241 : :
242 : 476111 : xact_state = pgStatXactStack;
243 [ + + + + ]: 476111 : if (xact_state == NULL || xact_state->nest_level != nest_level)
244 : : {
245 : : xact_state = (PgStat_SubXactStatus *)
246 : 132094 : MemoryContextAlloc(TopTransactionContext,
247 : : sizeof(PgStat_SubXactStatus));
1141 drowley@postgresql.o 248 : 132094 : dclist_init(&xact_state->pending_drops);
1351 andres@anarazel.de 249 : 132094 : xact_state->nest_level = nest_level;
250 : 132094 : xact_state->prev = pgStatXactStack;
251 : 132094 : xact_state->first = NULL;
252 : 132094 : pgStatXactStack = xact_state;
253 : : }
254 : 476111 : 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 : : */
271 : : int
272 : 311087 : pgstat_get_transactional_drops(bool isCommit, xl_xact_stats_item **items)
273 : : {
274 : 311087 : PgStat_SubXactStatus *xact_state = pgStatXactStack;
275 : 311087 : int nitems = 0;
276 : : dlist_iter iter;
277 : :
278 [ + + ]: 311087 : if (xact_state == NULL)
279 : 181361 : 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 [ + + - + ]: 129726 : Assert(!isCommit || xact_state->nest_level == 1);
286 [ + + - + ]: 129726 : Assert(!isCommit || xact_state->prev == NULL);
287 : :
1141 drowley@postgresql.o 288 : 129726 : *items = palloc(dclist_count(&xact_state->pending_drops)
289 : : * sizeof(xl_xact_stats_item));
290 : :
291 [ + - + + ]: 248560 : dclist_foreach(iter, &xact_state->pending_drops)
292 : : {
1351 andres@anarazel.de 293 : 118834 : PgStat_PendingDroppedStatsItem *pending =
943 tgl@sss.pgh.pa.us 294 : 118834 : dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
295 : :
1351 andres@anarazel.de 296 [ + + + + ]: 118834 : if (isCommit && pending->is_create)
297 : 74997 : continue;
298 [ + + + + ]: 43837 : if (!isCommit && !pending->is_create)
299 : 539 : continue;
300 : :
1141 drowley@postgresql.o 301 [ - + ]: 43298 : Assert(nitems < dclist_count(&xact_state->pending_drops));
1351 andres@anarazel.de 302 : 43298 : (*items)[nitems++] = pending->item;
303 : : }
304 : :
305 : 129726 : 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 : : */
313 : : void
314 : 3532 : pgstat_execute_transactional_drops(int ndrops, struct xl_xact_stats_item *items, bool is_redo)
315 : : {
316 : 3532 : int not_freed_count = 0;
317 : :
318 [ + + ]: 3532 : if (ndrops == 0)
319 : 287 : return;
320 : :
321 [ + + ]: 13039 : for (int i = 0; i < ndrops; i++)
322 : : {
323 : 9794 : xl_xact_stats_item *it = &items[i];
455 michael@paquier.xyz 324 : 9794 : uint64 objid = ((uint64) it->objid_hi) << 32 | it->objid_lo;
325 : :
326 [ + + ]: 9794 : if (!pgstat_drop_entry(it->kind, it->dboid, objid))
1351 andres@anarazel.de 327 : 2 : not_freed_count++;
328 : : }
329 : :
330 [ + + ]: 3245 : if (not_freed_count > 0)
331 : 2 : pgstat_request_entry_refs_gc();
332 : : }
333 : :
334 : : static void
455 michael@paquier.xyz 335 : 118123 : create_drop_transactional_internal(PgStat_Kind kind, Oid dboid, uint64 objid, bool is_create)
336 : : {
1351 andres@anarazel.de 337 : 118123 : int nest_level = GetCurrentTransactionNestLevel();
338 : : PgStat_SubXactStatus *xact_state;
339 : : PgStat_PendingDroppedStatsItem *drop = (PgStat_PendingDroppedStatsItem *)
943 tgl@sss.pgh.pa.us 340 : 118123 : MemoryContextAlloc(TopTransactionContext, sizeof(PgStat_PendingDroppedStatsItem));
341 : :
1351 andres@anarazel.de 342 : 118123 : xact_state = pgstat_get_xact_stack_level(nest_level);
343 : :
344 : 118123 : drop->is_create = is_create;
345 : 118123 : drop->item.kind = kind;
346 : 118123 : drop->item.dboid = dboid;
455 michael@paquier.xyz 347 : 118123 : drop->item.objid_lo = (uint32) objid;
348 : 118123 : drop->item.objid_hi = (uint32) (objid >> 32);
349 : :
1141 drowley@postgresql.o 350 : 118123 : dclist_push_tail(&xact_state->pending_drops, &drop->node);
1351 andres@anarazel.de 351 : 118123 : }
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 : : */
360 : : void
455 michael@paquier.xyz 361 : 77541 : pgstat_create_transactional(PgStat_Kind kind, Oid dboid, uint64 objid)
362 : : {
363 [ - + ]: 77541 : if (pgstat_get_entry_ref(kind, dboid, objid, false, NULL))
364 : : {
1351 andres@anarazel.de 365 [ # # ]:UBC 0 : ereport(WARNING,
366 : : errmsg("resetting existing statistics for kind %s, db=%u, oid=%" PRIu64,
367 : : (pgstat_get_kind_info(kind))->name, dboid,
368 : : objid));
369 : :
455 michael@paquier.xyz 370 : 0 : pgstat_reset(kind, dboid, objid);
371 : : }
372 : :
455 michael@paquier.xyz 373 :CBC 77541 : create_drop_transactional_internal(kind, dboid, objid, /* create */ true);
1351 andres@anarazel.de 374 : 77541 : }
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 : : */
383 : : void
455 michael@paquier.xyz 384 : 40582 : pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, uint64 objid)
385 : : {
386 : 40582 : create_drop_transactional_internal(kind, dboid, objid, /* create */ false);
1351 andres@anarazel.de 387 : 40582 : }
|