LCOV - code coverage report
Current view: top level - src/backend/utils/activity - pgstat_xact.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 116 121 95.9 %
Date: 2024-03-28 09:11:01 Functions: 12 12 100.0 %
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.14