Line data Source code
1 : /* -------------------------------------------------------------------------
2 : *
3 : * pgstat_xact.c
4 : * Transactional integration for the cumulative statistics system.
5 : *
6 : * Copyright (c) 2001-2026, 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
40 1081348 : AtEOXact_PgStat(bool isCommit, bool parallel)
41 : {
42 : PgStat_SubXactStatus *xact_state;
43 :
44 1081348 : AtEOXact_PgStat_Database(isCommit, parallel);
45 :
46 : /* handle transactional stats information */
47 1081348 : xact_state = pgStatXactStack;
48 1081348 : if (xact_state != NULL)
49 : {
50 : Assert(xact_state->nest_level == 1);
51 : Assert(xact_state->prev == NULL);
52 :
53 261466 : AtEOXact_PgStat_Relations(xact_state, isCommit);
54 261466 : AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
55 : }
56 1081348 : pgStatXactStack = NULL;
57 :
58 : /* Make sure any stats snapshot is thrown away */
59 1081348 : pgstat_clear_snapshot();
60 1081348 : }
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 261466 : AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit)
68 : {
69 : dlist_mutable_iter iter;
70 261466 : int not_freed_count = 0;
71 :
72 261466 : if (dclist_count(&xact_state->pending_drops) == 0)
73 171430 : return;
74 :
75 335530 : dclist_foreach_modify(iter, &xact_state->pending_drops)
76 : {
77 245494 : PgStat_PendingDroppedStatsItem *pending =
78 245494 : dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
79 245494 : xl_xact_stats_item *it = &pending->item;
80 245494 : uint64 objid = ((uint64) it->objid_hi) << 32 | it->objid_lo;
81 :
82 245494 : if (isCommit && !pending->is_create)
83 : {
84 : /*
85 : * Transaction that dropped an object committed. Drop the stats
86 : * too.
87 : */
88 84644 : if (!pgstat_drop_entry(it->kind, it->dboid, objid))
89 9946 : not_freed_count++;
90 : }
91 160850 : 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 5002 : if (!pgstat_drop_entry(it->kind, it->dboid, objid))
98 0 : not_freed_count++;
99 : }
100 :
101 245494 : dclist_delete_from(&xact_state->pending_drops, &pending->node);
102 245494 : pfree(pending);
103 : }
104 :
105 90036 : if (not_freed_count > 0)
106 3852 : pgstat_request_entry_refs_gc();
107 : }
108 :
109 : /*
110 : * Called from access/transam/xact.c at subtransaction commit/abort.
111 : */
112 : void
113 20134 : 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 20134 : xact_state = pgStatXactStack;
119 20134 : if (xact_state != NULL &&
120 9392 : xact_state->nest_level >= nestDepth)
121 : {
122 : /* delink xact_state from stack immediately to simplify reuse case */
123 8446 : pgStatXactStack = xact_state->prev;
124 :
125 8446 : AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
126 8446 : AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
127 :
128 8446 : pfree(xact_state);
129 : }
130 20134 : }
131 :
132 : /*
133 : * Like AtEOXact_PgStat_DroppedStats(), but for subtransactions.
134 : */
135 : static void
136 8446 : 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 8446 : int not_freed_count = 0;
142 :
143 8446 : if (dclist_count(&xact_state->pending_drops) == 0)
144 8278 : return;
145 :
146 168 : parent_xact_state = pgstat_get_xact_stack_level(nestDepth - 1);
147 :
148 494 : dclist_foreach_modify(iter, &xact_state->pending_drops)
149 : {
150 326 : PgStat_PendingDroppedStatsItem *pending =
151 326 : dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
152 326 : xl_xact_stats_item *it = &pending->item;
153 326 : uint64 objid = ((uint64) it->objid_hi) << 32 | it->objid_lo;
154 :
155 326 : dclist_delete_from(&xact_state->pending_drops, &pending->node);
156 :
157 326 : if (!isCommit && pending->is_create)
158 : {
159 : /*
160 : * Subtransaction creating a new stats object aborted. Drop the
161 : * stats object.
162 : */
163 150 : if (!pgstat_drop_entry(it->kind, it->dboid, objid))
164 0 : not_freed_count++;
165 150 : pfree(pending);
166 : }
167 176 : 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 122 : dclist_push_tail(&parent_xact_state->pending_drops, &pending->node);
175 : }
176 : else
177 : {
178 54 : pfree(pending);
179 : }
180 : }
181 :
182 : Assert(dclist_count(&xact_state->pending_drops) == 0);
183 168 : if (not_freed_count > 0)
184 0 : pgstat_request_entry_refs_gc();
185 : }
186 :
187 : /*
188 : * Save the transactional stats state at 2PC transaction prepare.
189 : */
190 : void
191 620 : AtPrepare_PgStat(void)
192 : {
193 : PgStat_SubXactStatus *xact_state;
194 :
195 620 : xact_state = pgStatXactStack;
196 620 : if (xact_state != NULL)
197 : {
198 : Assert(xact_state->nest_level == 1);
199 : Assert(xact_state->prev == NULL);
200 :
201 606 : AtPrepare_PgStat_Relations(xact_state);
202 : }
203 620 : }
204 :
205 : /*
206 : * Clean up after successful PREPARE.
207 : *
208 : * Note: AtEOXact_PgStat is not called during PREPARE.
209 : */
210 : void
211 620 : 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 620 : xact_state = pgStatXactStack;
220 620 : if (xact_state != NULL)
221 : {
222 : Assert(xact_state->nest_level == 1);
223 : Assert(xact_state->prev == NULL);
224 :
225 606 : PostPrepare_PgStat_Relations(xact_state);
226 : }
227 620 : pgStatXactStack = NULL;
228 :
229 : /* Make sure any stats snapshot is thrown away */
230 620 : pgstat_clear_snapshot();
231 620 : }
232 :
233 : /*
234 : * Ensure (sub)transaction stack entry for the given nest_level exists, adding
235 : * it if needed.
236 : */
237 : PgStat_SubXactStatus *
238 971094 : pgstat_get_xact_stack_level(int nest_level)
239 : {
240 : PgStat_SubXactStatus *xact_state;
241 :
242 971094 : xact_state = pgStatXactStack;
243 971094 : if (xact_state == NULL || xact_state->nest_level != nest_level)
244 : {
245 : xact_state = (PgStat_SubXactStatus *)
246 270518 : MemoryContextAlloc(TopTransactionContext,
247 : sizeof(PgStat_SubXactStatus));
248 270518 : dclist_init(&xact_state->pending_drops);
249 270518 : xact_state->nest_level = nest_level;
250 270518 : xact_state->prev = pgStatXactStack;
251 270518 : xact_state->first = NULL;
252 270518 : pgStatXactStack = xact_state;
253 : }
254 971094 : 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 1040816 : pgstat_get_transactional_drops(bool isCommit, xl_xact_stats_item **items)
273 : {
274 1040816 : PgStat_SubXactStatus *xact_state = pgStatXactStack;
275 1040816 : int nitems = 0;
276 : dlist_iter iter;
277 :
278 1040816 : if (xact_state == NULL)
279 776816 : 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 264000 : *items = palloc(dclist_count(&xact_state->pending_drops)
289 : * sizeof(xl_xact_stats_item));
290 :
291 511252 : dclist_foreach(iter, &xact_state->pending_drops)
292 : {
293 247252 : PgStat_PendingDroppedStatsItem *pending =
294 247252 : dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
295 :
296 247252 : if (isCommit && pending->is_create)
297 154928 : continue;
298 92324 : if (!isCommit && !pending->is_create)
299 1112 : continue;
300 :
301 : Assert(nitems < dclist_count(&xact_state->pending_drops));
302 91212 : (*items)[nitems++] = pending->item;
303 : }
304 :
305 264000 : 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 7110 : pgstat_execute_transactional_drops(int ndrops, struct xl_xact_stats_item *items, bool is_redo)
315 : {
316 7110 : int not_freed_count = 0;
317 :
318 7110 : if (ndrops == 0)
319 598 : return;
320 :
321 26158 : for (int i = 0; i < ndrops; i++)
322 : {
323 19646 : xl_xact_stats_item *it = &items[i];
324 19646 : uint64 objid = ((uint64) it->objid_hi) << 32 | it->objid_lo;
325 :
326 19646 : if (!pgstat_drop_entry(it->kind, it->dboid, objid))
327 4 : not_freed_count++;
328 : }
329 :
330 6512 : if (not_freed_count > 0)
331 4 : pgstat_request_entry_refs_gc();
332 : }
333 :
334 : static void
335 245830 : create_drop_transactional_internal(PgStat_Kind kind, Oid dboid, uint64 objid, bool is_create)
336 : {
337 245830 : int nest_level = GetCurrentTransactionNestLevel();
338 : PgStat_SubXactStatus *xact_state;
339 : PgStat_PendingDroppedStatsItem *drop = (PgStat_PendingDroppedStatsItem *)
340 245830 : MemoryContextAlloc(TopTransactionContext, sizeof(PgStat_PendingDroppedStatsItem));
341 :
342 245830 : xact_state = pgstat_get_xact_stack_level(nest_level);
343 :
344 245830 : drop->is_create = is_create;
345 245830 : drop->item.kind = kind;
346 245830 : drop->item.dboid = dboid;
347 245830 : drop->item.objid_lo = (uint32) objid;
348 245830 : drop->item.objid_hi = (uint32) (objid >> 32);
349 :
350 245830 : dclist_push_tail(&xact_state->pending_drops, &drop->node);
351 245830 : }
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
361 160080 : pgstat_create_transactional(PgStat_Kind kind, Oid dboid, uint64 objid)
362 : {
363 160080 : if (pgstat_get_entry_ref(kind, dboid, objid, false, NULL))
364 : {
365 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 :
370 0 : pgstat_reset(kind, dboid, objid);
371 : }
372 :
373 160080 : create_drop_transactional_internal(kind, dboid, objid, /* create */ true);
374 160080 : }
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
384 85750 : pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, uint64 objid)
385 : {
386 85750 : create_drop_transactional_internal(kind, dboid, objid, /* create */ false);
387 85750 : }
|