LCOV - code coverage report
Current view: top level - src/backend/utils/activity - pgstat_xact.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 120 125 96.0 %
Date: 2024-10-10 03:14:42 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      764490 : AtEOXact_PgStat(bool isCommit, bool parallel)
      41             : {
      42             :     PgStat_SubXactStatus *xact_state;
      43             : 
      44      764490 :     AtEOXact_PgStat_Database(isCommit, parallel);
      45             : 
      46             :     /* handle transactional stats information */
      47      764490 :     xact_state = pgStatXactStack;
      48      764490 :     if (xact_state != NULL)
      49             :     {
      50             :         Assert(xact_state->nest_level == 1);
      51             :         Assert(xact_state->prev == NULL);
      52             : 
      53      224236 :         AtEOXact_PgStat_Relations(xact_state, isCommit);
      54      224236 :         AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
      55             :     }
      56      764490 :     pgStatXactStack = NULL;
      57             : 
      58             :     /* Make sure any stats snapshot is thrown away */
      59      764490 :     pgstat_clear_snapshot();
      60      764490 : }
      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      224236 : AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit)
      68             : {
      69             :     dlist_mutable_iter iter;
      70      224236 :     int         not_freed_count = 0;
      71             : 
      72      224236 :     if (dclist_count(&xact_state->pending_drops) == 0)
      73      146966 :         return;
      74             : 
      75      280424 :     dclist_foreach_modify(iter, &xact_state->pending_drops)
      76             :     {
      77      203154 :         PgStat_PendingDroppedStatsItem *pending =
      78      203154 :             dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
      79      203154 :         xl_xact_stats_item *it = &pending->item;
      80      203154 :         uint64      objid = ((uint64) it->objid_hi) << 32 | it->objid_lo;
      81             : 
      82      203154 :         if (isCommit && !pending->is_create)
      83             :         {
      84             :             /*
      85             :              * Transaction that dropped an object committed. Drop the stats
      86             :              * too.
      87             :              */
      88       69608 :             if (!pgstat_drop_entry(it->kind, it->dboid, objid))
      89        9206 :                 not_freed_count++;
      90             :         }
      91      133546 :         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        4290 :             if (!pgstat_drop_entry(it->kind, it->dboid, objid))
      98           0 :                 not_freed_count++;
      99             :         }
     100             : 
     101      203154 :         dclist_delete_from(&xact_state->pending_drops, &pending->node);
     102      203154 :         pfree(pending);
     103             :     }
     104             : 
     105       77270 :     if (not_freed_count > 0)
     106        3552 :         pgstat_request_entry_refs_gc();
     107             : }
     108             : 
     109             : /*
     110             :  * Called from access/transam/xact.c at subtransaction commit/abort.
     111             :  */
     112             : void
     113       18140 : 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       18140 :     xact_state = pgStatXactStack;
     119       18140 :     if (xact_state != NULL &&
     120        7562 :         xact_state->nest_level >= nestDepth)
     121             :     {
     122             :         /* delink xact_state from stack immediately to simplify reuse case */
     123        6654 :         pgStatXactStack = xact_state->prev;
     124             : 
     125        6654 :         AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
     126        6654 :         AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
     127             : 
     128        6654 :         pfree(xact_state);
     129             :     }
     130       18140 : }
     131             : 
     132             : /*
     133             :  * Like AtEOXact_PgStat_DroppedStats(), but for subtransactions.
     134             :  */
     135             : static void
     136        6654 : 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        6654 :     int         not_freed_count = 0;
     142             : 
     143        6654 :     if (dclist_count(&xact_state->pending_drops) == 0)
     144        6510 :         return;
     145             : 
     146         144 :     parent_xact_state = pgstat_get_xact_stack_level(nestDepth - 1);
     147             : 
     148         422 :     dclist_foreach_modify(iter, &xact_state->pending_drops)
     149             :     {
     150         278 :         PgStat_PendingDroppedStatsItem *pending =
     151         278 :             dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
     152         278 :         xl_xact_stats_item *it = &pending->item;
     153         278 :         uint64      objid = ((uint64) it->objid_hi) << 32 | it->objid_lo;
     154             : 
     155         278 :         dclist_delete_from(&xact_state->pending_drops, &pending->node);
     156             : 
     157         278 :         if (!isCommit && pending->is_create)
     158             :         {
     159             :             /*
     160             :              * Subtransaction creating a new stats object aborted. Drop the
     161             :              * stats object.
     162             :              */
     163         138 :             if (!pgstat_drop_entry(it->kind, it->dboid, objid))
     164           0 :                 not_freed_count++;
     165         138 :             pfree(pending);
     166             :         }
     167         140 :         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          98 :             dclist_push_tail(&parent_xact_state->pending_drops, &pending->node);
     175             :         }
     176             :         else
     177             :         {
     178          42 :             pfree(pending);
     179             :         }
     180             :     }
     181             : 
     182             :     Assert(dclist_count(&xact_state->pending_drops) == 0);
     183         144 :     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         758 : AtPrepare_PgStat(void)
     192             : {
     193             :     PgStat_SubXactStatus *xact_state;
     194             : 
     195         758 :     xact_state = pgStatXactStack;
     196         758 :     if (xact_state != NULL)
     197             :     {
     198             :         Assert(xact_state->nest_level == 1);
     199             :         Assert(xact_state->prev == NULL);
     200             : 
     201         744 :         AtPrepare_PgStat_Relations(xact_state);
     202             :     }
     203         758 : }
     204             : 
     205             : /*
     206             :  * Clean up after successful PREPARE.
     207             :  *
     208             :  * Note: AtEOXact_PgStat is not called during PREPARE.
     209             :  */
     210             : void
     211         758 : 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         758 :     xact_state = pgStatXactStack;
     220         758 :     if (xact_state != NULL)
     221             :     {
     222             :         Assert(xact_state->nest_level == 1);
     223             :         Assert(xact_state->prev == NULL);
     224             : 
     225         744 :         PostPrepare_PgStat_Relations(xact_state);
     226             :     }
     227         758 :     pgStatXactStack = NULL;
     228             : 
     229             :     /* Make sure any stats snapshot is thrown away */
     230         758 :     pgstat_clear_snapshot();
     231         758 : }
     232             : 
     233             : /*
     234             :  * Ensure (sub)transaction stack entry for the given nest_level exists, adding
     235             :  * it if needed.
     236             :  */
     237             : PgStat_SubXactStatus *
     238      817134 : pgstat_get_xact_stack_level(int nest_level)
     239             : {
     240             :     PgStat_SubXactStatus *xact_state;
     241             : 
     242      817134 :     xact_state = pgStatXactStack;
     243      817134 :     if (xact_state == NULL || xact_state->nest_level != nest_level)
     244             :     {
     245             :         xact_state = (PgStat_SubXactStatus *)
     246      231634 :             MemoryContextAlloc(TopTransactionContext,
     247             :                                sizeof(PgStat_SubXactStatus));
     248      231634 :         dclist_init(&xact_state->pending_drops);
     249      231634 :         xact_state->nest_level = nest_level;
     250      231634 :         xact_state->prev = pgStatXactStack;
     251      231634 :         xact_state->first = NULL;
     252      231634 :         pgStatXactStack = xact_state;
     253             :     }
     254      817134 :     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      727410 : pgstat_get_transactional_drops(bool isCommit, xl_xact_stats_item **items)
     273             : {
     274      727410 :     PgStat_SubXactStatus *xact_state = pgStatXactStack;
     275      727410 :     int         nitems = 0;
     276             :     dlist_iter  iter;
     277             : 
     278      727410 :     if (xact_state == NULL)
     279      500404 :         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      227006 :     *items = palloc(dclist_count(&xact_state->pending_drops)
     289             :                     * sizeof(xl_xact_stats_item));
     290             : 
     291      431806 :     dclist_foreach(iter, &xact_state->pending_drops)
     292             :     {
     293      204800 :         PgStat_PendingDroppedStatsItem *pending =
     294      204800 :             dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
     295             : 
     296      204800 :         if (isCommit && pending->is_create)
     297      128450 :             continue;
     298       76350 :         if (!isCommit && !pending->is_create)
     299         942 :             continue;
     300             : 
     301             :         Assert(nitems < dclist_count(&xact_state->pending_drops));
     302       75408 :         (*items)[nitems++] = pending->item;
     303             :     }
     304             : 
     305      227006 :     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        6366 : pgstat_execute_transactional_drops(int ndrops, struct xl_xact_stats_item *items, bool is_redo)
     315             : {
     316        6366 :     int         not_freed_count = 0;
     317             : 
     318        6366 :     if (ndrops == 0)
     319         756 :         return;
     320             : 
     321       22656 :     for (int i = 0; i < ndrops; i++)
     322             :     {
     323       17046 :         xl_xact_stats_item *it = &items[i];
     324       17046 :         uint64      objid = ((uint64) it->objid_hi) << 32 | it->objid_lo;
     325             : 
     326       17046 :         if (!pgstat_drop_entry(it->kind, it->dboid, objid))
     327           4 :             not_freed_count++;
     328             :     }
     329             : 
     330        5610 :     if (not_freed_count > 0)
     331           4 :         pgstat_request_entry_refs_gc();
     332             : }
     333             : 
     334             : static void
     335      203422 : create_drop_transactional_internal(PgStat_Kind kind, Oid dboid, uint64 objid, bool is_create)
     336             : {
     337      203422 :     int         nest_level = GetCurrentTransactionNestLevel();
     338             :     PgStat_SubXactStatus *xact_state;
     339             :     PgStat_PendingDroppedStatsItem *drop = (PgStat_PendingDroppedStatsItem *)
     340      203422 :         MemoryContextAlloc(TopTransactionContext, sizeof(PgStat_PendingDroppedStatsItem));
     341             : 
     342      203422 :     xact_state = pgstat_get_xact_stack_level(nest_level);
     343             : 
     344      203422 :     drop->is_create = is_create;
     345      203422 :     drop->item.kind = kind;
     346      203422 :     drop->item.dboid = dboid;
     347      203422 :     drop->item.objid_lo = (uint32) objid;
     348      203422 :     drop->item.objid_hi = (uint32) (objid >> 32);
     349             : 
     350      203422 :     dclist_push_tail(&xact_state->pending_drops, &drop->node);
     351      203422 : }
     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      132878 : pgstat_create_transactional(PgStat_Kind kind, Oid dboid, uint64 objid)
     362             : {
     363      132878 :     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=%llu",
     367             :                        (pgstat_get_kind_info(kind))->name, dboid,
     368             :                        (unsigned long long) objid));
     369             : 
     370           0 :         pgstat_reset(kind, dboid, objid);
     371             :     }
     372             : 
     373      132878 :     create_drop_transactional_internal(kind, dboid, objid, /* create */ true);
     374      132878 : }
     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       70544 : pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, uint64 objid)
     385             : {
     386       70544 :     create_drop_transactional_internal(kind, dboid, objid, /* create */ false);
     387       70544 : }

Generated by: LCOV version 1.14