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: 2023-12-11 14:10:49 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-2023, 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/transam.h"
      16             : #include "access/xact.h"
      17             : #include "pgstat.h"
      18             : #include "utils/memutils.h"
      19             : #include "utils/pgstat_internal.h"
      20             : 
      21             : 
      22             : typedef struct PgStat_PendingDroppedStatsItem
      23             : {
      24             :     xl_xact_stats_item item;
      25             :     bool        is_create;
      26             :     dlist_node  node;
      27             : } PgStat_PendingDroppedStatsItem;
      28             : 
      29             : 
      30             : static void AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit);
      31             : static void AtEOSubXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state,
      32             :                                             bool isCommit, int nestDepth);
      33             : 
      34             : static PgStat_SubXactStatus *pgStatXactStack = NULL;
      35             : 
      36             : 
      37             : /*
      38             :  * Called from access/transam/xact.c at top-level transaction commit/abort.
      39             :  */
      40             : void
      41      515204 : AtEOXact_PgStat(bool isCommit, bool parallel)
      42             : {
      43             :     PgStat_SubXactStatus *xact_state;
      44             : 
      45      515204 :     AtEOXact_PgStat_Database(isCommit, parallel);
      46             : 
      47             :     /* handle transactional stats information */
      48      515204 :     xact_state = pgStatXactStack;
      49      515204 :     if (xact_state != NULL)
      50             :     {
      51             :         Assert(xact_state->nest_level == 1);
      52             :         Assert(xact_state->prev == NULL);
      53             : 
      54      195584 :         AtEOXact_PgStat_Relations(xact_state, isCommit);
      55      195584 :         AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
      56             :     }
      57      515204 :     pgStatXactStack = NULL;
      58             : 
      59             :     /* Make sure any stats snapshot is thrown away */
      60      515204 :     pgstat_clear_snapshot();
      61      515204 : }
      62             : 
      63             : /*
      64             :  * When committing, drop stats for objects dropped in the transaction. When
      65             :  * aborting, drop stats for objects created in the transaction.
      66             :  */
      67             : static void
      68      195584 : AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit)
      69             : {
      70             :     dlist_mutable_iter iter;
      71      195584 :     int         not_freed_count = 0;
      72             : 
      73      195584 :     if (dclist_count(&xact_state->pending_drops) == 0)
      74      121436 :         return;
      75             : 
      76      259152 :     dclist_foreach_modify(iter, &xact_state->pending_drops)
      77             :     {
      78      185004 :         PgStat_PendingDroppedStatsItem *pending =
      79      185004 :             dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
      80      185004 :         xl_xact_stats_item *it = &pending->item;
      81             : 
      82      185004 :         if (isCommit && !pending->is_create)
      83             :         {
      84             :             /*
      85             :              * Transaction that dropped an object committed. Drop the stats
      86             :              * too.
      87             :              */
      88       65232 :             if (!pgstat_drop_entry(it->kind, it->dboid, it->objoid))
      89        9188 :                 not_freed_count++;
      90             :         }
      91      119772 :         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        3566 :             if (!pgstat_drop_entry(it->kind, it->dboid, it->objoid))
      98           0 :                 not_freed_count++;
      99             :         }
     100             : 
     101      185004 :         dclist_delete_from(&xact_state->pending_drops, &pending->node);
     102      185004 :         pfree(pending);
     103             :     }
     104             : 
     105       74148 :     if (not_freed_count > 0)
     106        3528 :         pgstat_request_entry_refs_gc();
     107             : }
     108             : 
     109             : /*
     110             :  * Called from access/transam/xact.c at subtransaction commit/abort.
     111             :  */
     112             : void
     113       17988 : 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       17988 :     xact_state = pgStatXactStack;
     119       17988 :     if (xact_state != NULL &&
     120        7516 :         xact_state->nest_level >= nestDepth)
     121             :     {
     122             :         /* delink xact_state from stack immediately to simplify reuse case */
     123        6658 :         pgStatXactStack = xact_state->prev;
     124             : 
     125        6658 :         AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
     126        6658 :         AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
     127             : 
     128        6658 :         pfree(xact_state);
     129             :     }
     130       17988 : }
     131             : 
     132             : /*
     133             :  * Like AtEOXact_PgStat_DroppedStats(), but for subtransactions.
     134             :  */
     135             : static void
     136        6658 : 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        6658 :     int         not_freed_count = 0;
     142             : 
     143        6658 :     if (dclist_count(&xact_state->pending_drops) == 0)
     144        6514 :         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             : 
     154         278 :         dclist_delete_from(&xact_state->pending_drops, &pending->node);
     155             : 
     156         278 :         if (!isCommit && pending->is_create)
     157             :         {
     158             :             /*
     159             :              * Subtransaction creating a new stats object aborted. Drop the
     160             :              * stats object.
     161             :              */
     162         138 :             if (!pgstat_drop_entry(it->kind, it->dboid, it->objoid))
     163           0 :                 not_freed_count++;
     164         138 :             pfree(pending);
     165             :         }
     166         140 :         else if (isCommit)
     167             :         {
     168             :             /*
     169             :              * Subtransaction dropping a stats object committed. Can't yet
     170             :              * remove the stats object, the surrounding transaction might
     171             :              * still abort. Pass it on to the parent.
     172             :              */
     173          98 :             dclist_push_tail(&parent_xact_state->pending_drops, &pending->node);
     174             :         }
     175             :         else
     176             :         {
     177          42 :             pfree(pending);
     178             :         }
     179             :     }
     180             : 
     181             :     Assert(dclist_count(&xact_state->pending_drops) == 0);
     182         144 :     if (not_freed_count > 0)
     183           0 :         pgstat_request_entry_refs_gc();
     184             : }
     185             : 
     186             : /*
     187             :  * Save the transactional stats state at 2PC transaction prepare.
     188             :  */
     189             : void
     190         762 : AtPrepare_PgStat(void)
     191             : {
     192             :     PgStat_SubXactStatus *xact_state;
     193             : 
     194         762 :     xact_state = pgStatXactStack;
     195         762 :     if (xact_state != NULL)
     196             :     {
     197             :         Assert(xact_state->nest_level == 1);
     198             :         Assert(xact_state->prev == NULL);
     199             : 
     200         748 :         AtPrepare_PgStat_Relations(xact_state);
     201             :     }
     202         762 : }
     203             : 
     204             : /*
     205             :  * Clean up after successful PREPARE.
     206             :  *
     207             :  * Note: AtEOXact_PgStat is not called during PREPARE.
     208             :  */
     209             : void
     210         762 : PostPrepare_PgStat(void)
     211             : {
     212             :     PgStat_SubXactStatus *xact_state;
     213             : 
     214             :     /*
     215             :      * We don't bother to free any of the transactional state, since it's all
     216             :      * in TopTransactionContext and will go away anyway.
     217             :      */
     218         762 :     xact_state = pgStatXactStack;
     219         762 :     if (xact_state != NULL)
     220             :     {
     221             :         Assert(xact_state->nest_level == 1);
     222             :         Assert(xact_state->prev == NULL);
     223             : 
     224         748 :         PostPrepare_PgStat_Relations(xact_state);
     225             :     }
     226         762 :     pgStatXactStack = NULL;
     227             : 
     228             :     /* Make sure any stats snapshot is thrown away */
     229         762 :     pgstat_clear_snapshot();
     230         762 : }
     231             : 
     232             : /*
     233             :  * Ensure (sub)transaction stack entry for the given nest_level exists, adding
     234             :  * it if needed.
     235             :  */
     236             : PgStat_SubXactStatus *
     237      753278 : pgstat_get_xact_stack_level(int nest_level)
     238             : {
     239             :     PgStat_SubXactStatus *xact_state;
     240             : 
     241      753278 :     xact_state = pgStatXactStack;
     242      753278 :     if (xact_state == NULL || xact_state->nest_level != nest_level)
     243             :     {
     244             :         xact_state = (PgStat_SubXactStatus *)
     245      202990 :             MemoryContextAlloc(TopTransactionContext,
     246             :                                sizeof(PgStat_SubXactStatus));
     247      202990 :         dclist_init(&xact_state->pending_drops);
     248      202990 :         xact_state->nest_level = nest_level;
     249      202990 :         xact_state->prev = pgStatXactStack;
     250      202990 :         xact_state->first = NULL;
     251      202990 :         pgStatXactStack = xact_state;
     252             :     }
     253      753278 :     return xact_state;
     254             : }
     255             : 
     256             : /*
     257             :  * Get stat items that need to be dropped at commit / abort.
     258             :  *
     259             :  * When committing, stats for objects that have been dropped in the
     260             :  * transaction are returned. When aborting, stats for newly created objects are
     261             :  * returned.
     262             :  *
     263             :  * Used by COMMIT / ABORT and 2PC PREPARE processing when building their
     264             :  * respective WAL records, to ensure stats are dropped in case of a crash / on
     265             :  * standbys.
     266             :  *
     267             :  * The list of items is allocated in CurrentMemoryContext and must be freed by
     268             :  * the caller (directly or via memory context reset).
     269             :  */
     270             : int
     271      481934 : pgstat_get_transactional_drops(bool isCommit, xl_xact_stats_item **items)
     272             : {
     273      481934 :     PgStat_SubXactStatus *xact_state = pgStatXactStack;
     274      481934 :     int         nitems = 0;
     275             :     dlist_iter  iter;
     276             : 
     277      481934 :     if (xact_state == NULL)
     278      283574 :         return 0;
     279             : 
     280             :     /*
     281             :      * We expect to be called for subtransaction abort (which logs a WAL
     282             :      * record), but not for subtransaction commit (which doesn't).
     283             :      */
     284             :     Assert(!isCommit || xact_state->nest_level == 1);
     285             :     Assert(!isCommit || xact_state->prev == NULL);
     286             : 
     287      198360 :     *items = palloc(dclist_count(&xact_state->pending_drops)
     288             :                     * sizeof(xl_xact_stats_item));
     289             : 
     290      385006 :     dclist_foreach(iter, &xact_state->pending_drops)
     291             :     {
     292      186646 :         PgStat_PendingDroppedStatsItem *pending =
     293      186646 :             dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
     294             : 
     295      186646 :         if (isCommit && pending->is_create)
     296      115458 :             continue;
     297       71188 :         if (!isCommit && !pending->is_create)
     298         882 :             continue;
     299             : 
     300             :         Assert(nitems < dclist_count(&xact_state->pending_drops));
     301       70306 :         (*items)[nitems++] = pending->item;
     302             :     }
     303             : 
     304      198360 :     return nitems;
     305             : }
     306             : 
     307             : /*
     308             :  * Execute scheduled drops post-commit. Called from xact_redo_commit() /
     309             :  * xact_redo_abort() during recovery, and from FinishPreparedTransaction()
     310             :  * during normal 2PC COMMIT/ABORT PREPARED processing.
     311             :  */
     312             : void
     313        5942 : pgstat_execute_transactional_drops(int ndrops, struct xl_xact_stats_item *items, bool is_redo)
     314             : {
     315        5942 :     int         not_freed_count = 0;
     316             : 
     317        5942 :     if (ndrops == 0)
     318         752 :         return;
     319             : 
     320       20678 :     for (int i = 0; i < ndrops; i++)
     321             :     {
     322       15488 :         xl_xact_stats_item *it = &items[i];
     323             : 
     324       15488 :         if (!pgstat_drop_entry(it->kind, it->dboid, it->objoid))
     325           4 :             not_freed_count++;
     326             :     }
     327             : 
     328        5190 :     if (not_freed_count > 0)
     329           4 :         pgstat_request_entry_refs_gc();
     330             : }
     331             : 
     332             : static void
     333      185270 : create_drop_transactional_internal(PgStat_Kind kind, Oid dboid, Oid objoid, bool is_create)
     334             : {
     335      185270 :     int         nest_level = GetCurrentTransactionNestLevel();
     336             :     PgStat_SubXactStatus *xact_state;
     337             :     PgStat_PendingDroppedStatsItem *drop = (PgStat_PendingDroppedStatsItem *)
     338      185270 :         MemoryContextAlloc(TopTransactionContext, sizeof(PgStat_PendingDroppedStatsItem));
     339             : 
     340      185270 :     xact_state = pgstat_get_xact_stack_level(nest_level);
     341             : 
     342      185270 :     drop->is_create = is_create;
     343      185270 :     drop->item.kind = kind;
     344      185270 :     drop->item.dboid = dboid;
     345      185270 :     drop->item.objoid = objoid;
     346             : 
     347      185270 :     dclist_push_tail(&xact_state->pending_drops, &drop->node);
     348      185270 : }
     349             : 
     350             : /*
     351             :  * Create a stats entry for a newly created database object in a transactional
     352             :  * manner.
     353             :  *
     354             :  * I.e. if the current (sub-)transaction aborts, the stats entry will also be
     355             :  * dropped.
     356             :  */
     357             : void
     358      119162 : pgstat_create_transactional(PgStat_Kind kind, Oid dboid, Oid objoid)
     359             : {
     360      119162 :     if (pgstat_get_entry_ref(kind, dboid, objoid, false, NULL))
     361             :     {
     362           0 :         ereport(WARNING,
     363             :                 errmsg("resetting existing statistics for kind %s, db=%u, oid=%u",
     364             :                        (pgstat_get_kind_info(kind))->name, dboid, objoid));
     365             : 
     366           0 :         pgstat_reset(kind, dboid, objoid);
     367             :     }
     368             : 
     369      119162 :     create_drop_transactional_internal(kind, dboid, objoid, /* create */ true);
     370      119162 : }
     371             : 
     372             : /*
     373             :  * Drop a stats entry for a just dropped database object in a transactional
     374             :  * manner.
     375             :  *
     376             :  * I.e. if the current (sub-)transaction aborts, the stats entry will stay
     377             :  * alive.
     378             :  */
     379             : void
     380       66108 : pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, Oid objoid)
     381             : {
     382       66108 :     create_drop_transactional_internal(kind, dboid, objoid, /* create */ false);
     383       66108 : }

Generated by: LCOV version 1.14