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-2024, 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
739 andres@anarazel.de 40 :CBC 432923 : AtEOXact_PgStat(bool isCommit, bool parallel)
41 : : {
42 : : PgStat_SubXactStatus *xact_state;
43 : :
44 : 432923 : AtEOXact_PgStat_Database(isCommit, parallel);
45 : :
46 : : /* handle transactional stats information */
47 : 432923 : xact_state = pgStatXactStack;
48 [ + + ]: 432923 : if (xact_state != NULL)
49 : : {
50 [ - + ]: 110118 : Assert(xact_state->nest_level == 1);
51 [ - + ]: 110118 : Assert(xact_state->prev == NULL);
52 : :
53 : 110118 : AtEOXact_PgStat_Relations(xact_state, isCommit);
54 : 110118 : AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
55 : : }
56 : 432923 : pgStatXactStack = NULL;
57 : :
58 : : /* Make sure any stats snapshot is thrown away */
59 : 432923 : pgstat_clear_snapshot();
60 : 432923 : }
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 : 110118 : AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit)
68 : : {
69 : : dlist_mutable_iter iter;
70 : 110118 : int not_freed_count = 0;
71 : :
529 drowley@postgresql.o 72 [ + + ]: 110118 : if (dclist_count(&xact_state->pending_drops) == 0)
739 andres@anarazel.de 73 : 71383 : return;
74 : :
529 drowley@postgresql.o 75 [ + - + + ]: 140503 : dclist_foreach_modify(iter, &xact_state->pending_drops)
76 : : {
739 andres@anarazel.de 77 : 101768 : PgStat_PendingDroppedStatsItem *pending =
331 tgl@sss.pgh.pa.us 78 : 101768 : dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
739 andres@anarazel.de 79 : 101768 : xl_xact_stats_item *it = &pending->item;
80 : :
81 [ + + + + ]: 101768 : if (isCommit && !pending->is_create)
82 : : {
83 : : /*
84 : : * Transaction that dropped an object committed. Drop the stats
85 : : * too.
86 : : */
87 [ + + ]: 40117 : if (!pgstat_drop_entry(it->kind, it->dboid, it->objoid))
88 : 4813 : not_freed_count++;
89 : : }
90 [ + + + + ]: 66464 : else if (!isCommit && pending->is_create)
91 : : {
92 : : /*
93 : : * Transaction that created an object aborted. Drop the stats
94 : : * associated with the object.
95 : : */
96 [ - + ]: 2058 : if (!pgstat_drop_entry(it->kind, it->dboid, it->objoid))
739 andres@anarazel.de 97 :UBC 0 : not_freed_count++;
98 : : }
99 : :
529 drowley@postgresql.o 100 :CBC 101768 : dclist_delete_from(&xact_state->pending_drops, &pending->node);
739 andres@anarazel.de 101 : 101768 : pfree(pending);
102 : : }
103 : :
104 [ + + ]: 38735 : if (not_freed_count > 0)
105 : 1827 : pgstat_request_entry_refs_gc();
106 : : }
107 : :
108 : : /*
109 : : * Called from access/transam/xact.c at subtransaction commit/abort.
110 : : */
111 : : void
112 : 9927 : AtEOSubXact_PgStat(bool isCommit, int nestDepth)
113 : : {
114 : : PgStat_SubXactStatus *xact_state;
115 : :
116 : : /* merge the sub-transaction's transactional stats into the parent */
117 : 9927 : xact_state = pgStatXactStack;
118 [ + + ]: 9927 : if (xact_state != NULL &&
119 [ + + ]: 4677 : xact_state->nest_level >= nestDepth)
120 : : {
121 : : /* delink xact_state from stack immediately to simplify reuse case */
122 : 4248 : pgStatXactStack = xact_state->prev;
123 : :
124 : 4248 : AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
125 : 4248 : AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
126 : :
127 : 4248 : pfree(xact_state);
128 : : }
129 : 9927 : }
130 : :
131 : : /*
132 : : * Like AtEOXact_PgStat_DroppedStats(), but for subtransactions.
133 : : */
134 : : static void
135 : 4248 : AtEOSubXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state,
136 : : bool isCommit, int nestDepth)
137 : : {
138 : : PgStat_SubXactStatus *parent_xact_state;
139 : : dlist_mutable_iter iter;
140 : 4248 : int not_freed_count = 0;
141 : :
529 drowley@postgresql.o 142 [ + + ]: 4248 : if (dclist_count(&xact_state->pending_drops) == 0)
739 andres@anarazel.de 143 : 4176 : return;
144 : :
145 : 72 : parent_xact_state = pgstat_get_xact_stack_level(nestDepth - 1);
146 : :
529 drowley@postgresql.o 147 [ + - + + ]: 211 : dclist_foreach_modify(iter, &xact_state->pending_drops)
148 : : {
739 andres@anarazel.de 149 : 139 : PgStat_PendingDroppedStatsItem *pending =
331 tgl@sss.pgh.pa.us 150 : 139 : dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
739 andres@anarazel.de 151 : 139 : xl_xact_stats_item *it = &pending->item;
152 : :
529 drowley@postgresql.o 153 : 139 : dclist_delete_from(&xact_state->pending_drops, &pending->node);
154 : :
739 andres@anarazel.de 155 [ + + + + ]: 139 : if (!isCommit && pending->is_create)
156 : : {
157 : : /*
158 : : * Subtransaction creating a new stats object aborted. Drop the
159 : : * stats object.
160 : : */
161 [ - + ]: 69 : if (!pgstat_drop_entry(it->kind, it->dboid, it->objoid))
739 andres@anarazel.de 162 :UBC 0 : not_freed_count++;
739 andres@anarazel.de 163 :CBC 69 : pfree(pending);
164 : : }
165 [ + + ]: 70 : else if (isCommit)
166 : : {
167 : : /*
168 : : * Subtransaction dropping a stats object committed. Can't yet
169 : : * remove the stats object, the surrounding transaction might
170 : : * still abort. Pass it on to the parent.
171 : : */
529 drowley@postgresql.o 172 : 49 : dclist_push_tail(&parent_xact_state->pending_drops, &pending->node);
173 : : }
174 : : else
175 : : {
739 andres@anarazel.de 176 : 21 : pfree(pending);
177 : : }
178 : : }
179 : :
529 drowley@postgresql.o 180 [ - + ]: 72 : Assert(dclist_count(&xact_state->pending_drops) == 0);
739 andres@anarazel.de 181 [ - + ]: 72 : if (not_freed_count > 0)
739 andres@anarazel.de 182 :UBC 0 : pgstat_request_entry_refs_gc();
183 : : }
184 : :
185 : : /*
186 : : * Save the transactional stats state at 2PC transaction prepare.
187 : : */
188 : : void
739 andres@anarazel.de 189 :CBC 391 : AtPrepare_PgStat(void)
190 : : {
191 : : PgStat_SubXactStatus *xact_state;
192 : :
193 : 391 : xact_state = pgStatXactStack;
194 [ + + ]: 391 : if (xact_state != NULL)
195 : : {
196 [ - + ]: 384 : Assert(xact_state->nest_level == 1);
197 [ - + ]: 384 : Assert(xact_state->prev == NULL);
198 : :
199 : 384 : AtPrepare_PgStat_Relations(xact_state);
200 : : }
201 : 391 : }
202 : :
203 : : /*
204 : : * Clean up after successful PREPARE.
205 : : *
206 : : * Note: AtEOXact_PgStat is not called during PREPARE.
207 : : */
208 : : void
209 : 391 : PostPrepare_PgStat(void)
210 : : {
211 : : PgStat_SubXactStatus *xact_state;
212 : :
213 : : /*
214 : : * We don't bother to free any of the transactional state, since it's all
215 : : * in TopTransactionContext and will go away anyway.
216 : : */
217 : 391 : xact_state = pgStatXactStack;
218 [ + + ]: 391 : if (xact_state != NULL)
219 : : {
220 [ - + ]: 384 : Assert(xact_state->nest_level == 1);
221 [ - + ]: 384 : Assert(xact_state->prev == NULL);
222 : :
223 : 384 : PostPrepare_PgStat_Relations(xact_state);
224 : : }
225 : 391 : pgStatXactStack = NULL;
226 : :
227 : : /* Make sure any stats snapshot is thrown away */
228 : 391 : pgstat_clear_snapshot();
229 : 391 : }
230 : :
231 : : /*
232 : : * Ensure (sub)transaction stack entry for the given nest_level exists, adding
233 : : * it if needed.
234 : : */
235 : : PgStat_SubXactStatus *
236 : 410055 : pgstat_get_xact_stack_level(int nest_level)
237 : : {
238 : : PgStat_SubXactStatus *xact_state;
239 : :
240 : 410055 : xact_state = pgStatXactStack;
241 [ + + + + ]: 410055 : if (xact_state == NULL || xact_state->nest_level != nest_level)
242 : : {
243 : : xact_state = (PgStat_SubXactStatus *)
244 : 114755 : MemoryContextAlloc(TopTransactionContext,
245 : : sizeof(PgStat_SubXactStatus));
529 drowley@postgresql.o 246 : 114755 : dclist_init(&xact_state->pending_drops);
739 andres@anarazel.de 247 : 114755 : xact_state->nest_level = nest_level;
248 : 114755 : xact_state->prev = pgStatXactStack;
249 : 114755 : xact_state->first = NULL;
250 : 114755 : pgStatXactStack = xact_state;
251 : : }
252 : 410055 : return xact_state;
253 : : }
254 : :
255 : : /*
256 : : * Get stat items that need to be dropped at commit / abort.
257 : : *
258 : : * When committing, stats for objects that have been dropped in the
259 : : * transaction are returned. When aborting, stats for newly created objects are
260 : : * returned.
261 : : *
262 : : * Used by COMMIT / ABORT and 2PC PREPARE processing when building their
263 : : * respective WAL records, to ensure stats are dropped in case of a crash / on
264 : : * standbys.
265 : : *
266 : : * The list of items is allocated in CurrentMemoryContext and must be freed by
267 : : * the caller (directly or via memory context reset).
268 : : */
269 : : int
270 : 414534 : pgstat_get_transactional_drops(bool isCommit, xl_xact_stats_item **items)
271 : : {
272 : 414534 : PgStat_SubXactStatus *xact_state = pgStatXactStack;
273 : 414534 : int nitems = 0;
274 : : dlist_iter iter;
275 : :
276 [ + + ]: 414534 : if (xact_state == NULL)
277 : 303006 : return 0;
278 : :
279 : : /*
280 : : * We expect to be called for subtransaction abort (which logs a WAL
281 : : * record), but not for subtransaction commit (which doesn't).
282 : : */
283 [ + + - + ]: 111528 : Assert(!isCommit || xact_state->nest_level == 1);
284 [ + + - + ]: 111528 : Assert(!isCommit || xact_state->prev == NULL);
285 : :
529 drowley@postgresql.o 286 : 111528 : *items = palloc(dclist_count(&xact_state->pending_drops)
287 : : * sizeof(xl_xact_stats_item));
288 : :
289 [ + - + + ]: 214107 : dclist_foreach(iter, &xact_state->pending_drops)
290 : : {
739 andres@anarazel.de 291 : 102579 : PgStat_PendingDroppedStatsItem *pending =
331 tgl@sss.pgh.pa.us 292 : 102579 : dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
293 : :
739 andres@anarazel.de 294 [ + + + + ]: 102579 : if (isCommit && pending->is_create)
295 : 64026 : continue;
296 [ + + + + ]: 38553 : if (!isCommit && !pending->is_create)
297 : 442 : continue;
298 : :
529 drowley@postgresql.o 299 [ - + ]: 38111 : Assert(nitems < dclist_count(&xact_state->pending_drops));
739 andres@anarazel.de 300 : 38111 : (*items)[nitems++] = pending->item;
301 : : }
302 : :
303 : 111528 : return nitems;
304 : : }
305 : :
306 : : /*
307 : : * Execute scheduled drops post-commit. Called from xact_redo_commit() /
308 : : * xact_redo_abort() during recovery, and from FinishPreparedTransaction()
309 : : * during normal 2PC COMMIT/ABORT PREPARED processing.
310 : : */
311 : : void
312 : 3196 : pgstat_execute_transactional_drops(int ndrops, struct xl_xact_stats_item *items, bool is_redo)
313 : : {
314 : 3196 : int not_freed_count = 0;
315 : :
316 [ + + ]: 3196 : if (ndrops == 0)
317 : 388 : return;
318 : :
319 [ + + ]: 11488 : for (int i = 0; i < ndrops; i++)
320 : : {
321 : 8680 : xl_xact_stats_item *it = &items[i];
322 : :
323 [ + + ]: 8680 : if (!pgstat_drop_entry(it->kind, it->dboid, it->objoid))
324 : 2 : not_freed_count++;
325 : : }
326 : :
327 [ + + ]: 2808 : if (not_freed_count > 0)
328 : 2 : pgstat_request_entry_refs_gc();
329 : : }
330 : :
331 : : static void
332 : 101901 : create_drop_transactional_internal(PgStat_Kind kind, Oid dboid, Oid objoid, bool is_create)
333 : : {
334 : 101901 : int nest_level = GetCurrentTransactionNestLevel();
335 : : PgStat_SubXactStatus *xact_state;
336 : : PgStat_PendingDroppedStatsItem *drop = (PgStat_PendingDroppedStatsItem *)
331 tgl@sss.pgh.pa.us 337 : 101901 : MemoryContextAlloc(TopTransactionContext, sizeof(PgStat_PendingDroppedStatsItem));
338 : :
739 andres@anarazel.de 339 : 101901 : xact_state = pgstat_get_xact_stack_level(nest_level);
340 : :
341 : 101901 : drop->is_create = is_create;
342 : 101901 : drop->item.kind = kind;
343 : 101901 : drop->item.dboid = dboid;
344 : 101901 : drop->item.objoid = objoid;
345 : :
529 drowley@postgresql.o 346 : 101901 : dclist_push_tail(&xact_state->pending_drops, &drop->node);
739 andres@anarazel.de 347 : 101901 : }
348 : :
349 : : /*
350 : : * Create a stats entry for a newly created database object in a transactional
351 : : * manner.
352 : : *
353 : : * I.e. if the current (sub-)transaction aborts, the stats entry will also be
354 : : * dropped.
355 : : */
356 : : void
357 : 66153 : pgstat_create_transactional(PgStat_Kind kind, Oid dboid, Oid objoid)
358 : : {
359 [ - + ]: 66153 : if (pgstat_get_entry_ref(kind, dboid, objoid, false, NULL))
360 : : {
739 andres@anarazel.de 361 [ # # ]:UBC 0 : ereport(WARNING,
362 : : errmsg("resetting existing statistics for kind %s, db=%u, oid=%u",
363 : : (pgstat_get_kind_info(kind))->name, dboid, objoid));
364 : :
365 : 0 : pgstat_reset(kind, dboid, objoid);
366 : : }
367 : :
739 andres@anarazel.de 368 :CBC 66153 : create_drop_transactional_internal(kind, dboid, objoid, /* create */ true);
369 : 66153 : }
370 : :
371 : : /*
372 : : * Drop a stats entry for a just dropped database object in a transactional
373 : : * manner.
374 : : *
375 : : * I.e. if the current (sub-)transaction aborts, the stats entry will stay
376 : : * alive.
377 : : */
378 : : void
379 : 35748 : pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, Oid objoid)
380 : : {
381 : 35748 : create_drop_transactional_internal(kind, dboid, objoid, /* create */ false);
382 : 35748 : }
|