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
40 566166 : AtEOXact_PgStat(bool isCommit, bool parallel)
41 : {
42 : PgStat_SubXactStatus *xact_state;
43 :
44 566166 : AtEOXact_PgStat_Database(isCommit, parallel);
45 :
46 : /* handle transactional stats information */
47 566166 : xact_state = pgStatXactStack;
48 566166 : if (xact_state != NULL)
49 : {
50 : Assert(xact_state->nest_level == 1);
51 : Assert(xact_state->prev == NULL);
52 :
53 223562 : AtEOXact_PgStat_Relations(xact_state, isCommit);
54 223562 : AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
55 : }
56 566166 : pgStatXactStack = NULL;
57 :
58 : /* Make sure any stats snapshot is thrown away */
59 566166 : pgstat_clear_snapshot();
60 566166 : }
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 223562 : AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit)
68 : {
69 : dlist_mutable_iter iter;
70 223562 : int not_freed_count = 0;
71 :
72 223562 : if (dclist_count(&xact_state->pending_drops) == 0)
73 144500 : return;
74 :
75 278260 : dclist_foreach_modify(iter, &xact_state->pending_drops)
76 : {
77 199198 : PgStat_PendingDroppedStatsItem *pending =
78 199198 : dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
79 199198 : xl_xact_stats_item *it = &pending->item;
80 :
81 199198 : if (isCommit && !pending->is_create)
82 : {
83 : /*
84 : * Transaction that dropped an object committed. Drop the stats
85 : * too.
86 : */
87 68090 : if (!pgstat_drop_entry(it->kind, it->dboid, it->objoid))
88 9208 : not_freed_count++;
89 : }
90 131108 : 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 4192 : if (!pgstat_drop_entry(it->kind, it->dboid, it->objoid))
97 0 : not_freed_count++;
98 : }
99 :
100 199198 : dclist_delete_from(&xact_state->pending_drops, &pending->node);
101 199198 : pfree(pending);
102 : }
103 :
104 79062 : if (not_freed_count > 0)
105 3560 : pgstat_request_entry_refs_gc();
106 : }
107 :
108 : /*
109 : * Called from access/transam/xact.c at subtransaction commit/abort.
110 : */
111 : void
112 17978 : 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 17978 : xact_state = pgStatXactStack;
118 17978 : if (xact_state != NULL &&
119 7510 : xact_state->nest_level >= nestDepth)
120 : {
121 : /* delink xact_state from stack immediately to simplify reuse case */
122 6652 : pgStatXactStack = xact_state->prev;
123 :
124 6652 : AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
125 6652 : AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
126 :
127 6652 : pfree(xact_state);
128 : }
129 17978 : }
130 :
131 : /*
132 : * Like AtEOXact_PgStat_DroppedStats(), but for subtransactions.
133 : */
134 : static void
135 6652 : 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 6652 : int not_freed_count = 0;
141 :
142 6652 : if (dclist_count(&xact_state->pending_drops) == 0)
143 6508 : return;
144 :
145 144 : parent_xact_state = pgstat_get_xact_stack_level(nestDepth - 1);
146 :
147 422 : dclist_foreach_modify(iter, &xact_state->pending_drops)
148 : {
149 278 : PgStat_PendingDroppedStatsItem *pending =
150 278 : dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
151 278 : xl_xact_stats_item *it = &pending->item;
152 :
153 278 : dclist_delete_from(&xact_state->pending_drops, &pending->node);
154 :
155 278 : if (!isCommit && pending->is_create)
156 : {
157 : /*
158 : * Subtransaction creating a new stats object aborted. Drop the
159 : * stats object.
160 : */
161 138 : if (!pgstat_drop_entry(it->kind, it->dboid, it->objoid))
162 0 : not_freed_count++;
163 138 : pfree(pending);
164 : }
165 140 : 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 : */
172 98 : dclist_push_tail(&parent_xact_state->pending_drops, &pending->node);
173 : }
174 : else
175 : {
176 42 : pfree(pending);
177 : }
178 : }
179 :
180 : Assert(dclist_count(&xact_state->pending_drops) == 0);
181 144 : if (not_freed_count > 0)
182 0 : pgstat_request_entry_refs_gc();
183 : }
184 :
185 : /*
186 : * Save the transactional stats state at 2PC transaction prepare.
187 : */
188 : void
189 796 : AtPrepare_PgStat(void)
190 : {
191 : PgStat_SubXactStatus *xact_state;
192 :
193 796 : xact_state = pgStatXactStack;
194 796 : if (xact_state != NULL)
195 : {
196 : Assert(xact_state->nest_level == 1);
197 : Assert(xact_state->prev == NULL);
198 :
199 782 : AtPrepare_PgStat_Relations(xact_state);
200 : }
201 796 : }
202 :
203 : /*
204 : * Clean up after successful PREPARE.
205 : *
206 : * Note: AtEOXact_PgStat is not called during PREPARE.
207 : */
208 : void
209 796 : 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 796 : xact_state = pgStatXactStack;
218 796 : if (xact_state != NULL)
219 : {
220 : Assert(xact_state->nest_level == 1);
221 : Assert(xact_state->prev == NULL);
222 :
223 782 : PostPrepare_PgStat_Relations(xact_state);
224 : }
225 796 : pgStatXactStack = NULL;
226 :
227 : /* Make sure any stats snapshot is thrown away */
228 796 : pgstat_clear_snapshot();
229 796 : }
230 :
231 : /*
232 : * Ensure (sub)transaction stack entry for the given nest_level exists, adding
233 : * it if needed.
234 : */
235 : PgStat_SubXactStatus *
236 821868 : pgstat_get_xact_stack_level(int nest_level)
237 : {
238 : PgStat_SubXactStatus *xact_state;
239 :
240 821868 : xact_state = pgStatXactStack;
241 821868 : if (xact_state == NULL || xact_state->nest_level != nest_level)
242 : {
243 : xact_state = (PgStat_SubXactStatus *)
244 230996 : MemoryContextAlloc(TopTransactionContext,
245 : sizeof(PgStat_SubXactStatus));
246 230996 : dclist_init(&xact_state->pending_drops);
247 230996 : xact_state->nest_level = nest_level;
248 230996 : xact_state->prev = pgStatXactStack;
249 230996 : xact_state->first = NULL;
250 230996 : pgStatXactStack = xact_state;
251 : }
252 821868 : 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 530308 : pgstat_get_transactional_drops(bool isCommit, xl_xact_stats_item **items)
271 : {
272 530308 : PgStat_SubXactStatus *xact_state = pgStatXactStack;
273 530308 : int nitems = 0;
274 : dlist_iter iter;
275 :
276 530308 : if (xact_state == NULL)
277 303898 : 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 : Assert(!isCommit || xact_state->nest_level == 1);
284 : Assert(!isCommit || xact_state->prev == NULL);
285 :
286 226410 : *items = palloc(dclist_count(&xact_state->pending_drops)
287 : * sizeof(xl_xact_stats_item));
288 :
289 427266 : dclist_foreach(iter, &xact_state->pending_drops)
290 : {
291 200856 : PgStat_PendingDroppedStatsItem *pending =
292 200856 : dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
293 :
294 200856 : if (isCommit && pending->is_create)
295 126140 : continue;
296 74716 : if (!isCommit && !pending->is_create)
297 918 : continue;
298 :
299 : Assert(nitems < dclist_count(&xact_state->pending_drops));
300 73798 : (*items)[nitems++] = pending->item;
301 : }
302 :
303 226410 : 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 6214 : pgstat_execute_transactional_drops(int ndrops, struct xl_xact_stats_item *items, bool is_redo)
313 : {
314 6214 : int not_freed_count = 0;
315 :
316 6214 : if (ndrops == 0)
317 786 : return;
318 :
319 22092 : for (int i = 0; i < ndrops; i++)
320 : {
321 16664 : xl_xact_stats_item *it = &items[i];
322 :
323 16664 : if (!pgstat_drop_entry(it->kind, it->dboid, it->objoid))
324 4 : not_freed_count++;
325 : }
326 :
327 5428 : if (not_freed_count > 0)
328 4 : pgstat_request_entry_refs_gc();
329 : }
330 :
331 : static void
332 199472 : create_drop_transactional_internal(PgStat_Kind kind, Oid dboid, Oid objoid, bool is_create)
333 : {
334 199472 : int nest_level = GetCurrentTransactionNestLevel();
335 : PgStat_SubXactStatus *xact_state;
336 : PgStat_PendingDroppedStatsItem *drop = (PgStat_PendingDroppedStatsItem *)
337 199472 : MemoryContextAlloc(TopTransactionContext, sizeof(PgStat_PendingDroppedStatsItem));
338 :
339 199472 : xact_state = pgstat_get_xact_stack_level(nest_level);
340 :
341 199472 : drop->is_create = is_create;
342 199472 : drop->item.kind = kind;
343 199472 : drop->item.dboid = dboid;
344 199472 : drop->item.objoid = objoid;
345 :
346 199472 : dclist_push_tail(&xact_state->pending_drops, &drop->node);
347 199472 : }
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 130470 : pgstat_create_transactional(PgStat_Kind kind, Oid dboid, Oid objoid)
358 : {
359 130470 : if (pgstat_get_entry_ref(kind, dboid, objoid, false, NULL))
360 : {
361 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 :
368 130470 : create_drop_transactional_internal(kind, dboid, objoid, /* create */ true);
369 130470 : }
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 69002 : pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, Oid objoid)
380 : {
381 69002 : create_drop_transactional_internal(kind, dboid, objoid, /* create */ false);
382 69002 : }
|