LCOV - code coverage report
Current view: top level - src/backend/utils/activity - pgstat_xact.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 116 121 95.9 %
Date: 2023-06-06 08:12:15 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      976450 : AtEOXact_PgStat(bool isCommit, bool parallel)
      42             : {
      43             :     PgStat_SubXactStatus *xact_state;
      44             : 
      45      976450 :     AtEOXact_PgStat_Database(isCommit, parallel);
      46             : 
      47             :     /* handle transactional stats information */
      48      976450 :     xact_state = pgStatXactStack;
      49      976450 :     if (xact_state != NULL)
      50             :     {
      51             :         Assert(xact_state->nest_level == 1);
      52             :         Assert(xact_state->prev == NULL);
      53             : 
      54      586574 :         AtEOXact_PgStat_Relations(xact_state, isCommit);
      55      586574 :         AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
      56             :     }
      57      976450 :     pgStatXactStack = NULL;
      58             : 
      59             :     /* Make sure any stats snapshot is thrown away */
      60      976450 :     pgstat_clear_snapshot();
      61      976450 : }
      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      586574 : AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *xact_state, bool isCommit)
      69             : {
      70             :     dlist_mutable_iter iter;
      71      586574 :     int         not_freed_count = 0;
      72             : 
      73      586574 :     if (dclist_count(&xact_state->pending_drops) == 0)
      74      428254 :         return;
      75             : 
      76      573006 :     dclist_foreach_modify(iter, &xact_state->pending_drops)
      77             :     {
      78      414686 :         PgStat_PendingDroppedStatsItem *pending =
      79      414686 :             dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
      80      414686 :         xl_xact_stats_item *it = &pending->item;
      81             : 
      82      414686 :         if (isCommit && !pending->is_create)
      83             :         {
      84             :             /*
      85             :              * Transaction that dropped an object committed. Drop the stats
      86             :              * too.
      87             :              */
      88       64218 :             if (!pgstat_drop_entry(it->kind, it->dboid, it->objoid))
      89        9176 :                 not_freed_count++;
      90             :         }
      91      350468 :         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        3478 :             if (!pgstat_drop_entry(it->kind, it->dboid, it->objoid))
      98           0 :                 not_freed_count++;
      99             :         }
     100             : 
     101      414686 :         dclist_delete_from(&xact_state->pending_drops, &pending->node);
     102      414686 :         pfree(pending);
     103             :     }
     104             : 
     105      158320 :     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       17580 : 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       17580 :     xact_state = pgStatXactStack;
     119       17580 :     if (xact_state != NULL &&
     120        7482 :         xact_state->nest_level >= nestDepth)
     121             :     {
     122             :         /* delink xact_state from stack immediately to simplify reuse case */
     123        6662 :         pgStatXactStack = xact_state->prev;
     124             : 
     125        6662 :         AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
     126        6662 :         AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
     127             : 
     128        6662 :         pfree(xact_state);
     129             :     }
     130       17580 : }
     131             : 
     132             : /*
     133             :  * Like AtEOXact_PgStat_DroppedStats(), but for subtransactions.
     134             :  */
     135             : static void
     136        6662 : 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        6662 :     int         not_freed_count = 0;
     142             : 
     143        6662 :     if (dclist_count(&xact_state->pending_drops) == 0)
     144        6518 :         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         738 : AtPrepare_PgStat(void)
     191             : {
     192             :     PgStat_SubXactStatus *xact_state;
     193             : 
     194         738 :     xact_state = pgStatXactStack;
     195         738 :     if (xact_state != NULL)
     196             :     {
     197             :         Assert(xact_state->nest_level == 1);
     198             :         Assert(xact_state->prev == NULL);
     199             : 
     200         728 :         AtPrepare_PgStat_Relations(xact_state);
     201             :     }
     202         738 : }
     203             : 
     204             : /*
     205             :  * Clean up after successful PREPARE.
     206             :  *
     207             :  * Note: AtEOXact_PgStat is not called during PREPARE.
     208             :  */
     209             : void
     210         738 : 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         738 :     xact_state = pgStatXactStack;
     219         738 :     if (xact_state != NULL)
     220             :     {
     221             :         Assert(xact_state->nest_level == 1);
     222             :         Assert(xact_state->prev == NULL);
     223             : 
     224         728 :         PostPrepare_PgStat_Relations(xact_state);
     225             :     }
     226         738 :     pgStatXactStack = NULL;
     227             : 
     228             :     /* Make sure any stats snapshot is thrown away */
     229         738 :     pgstat_clear_snapshot();
     230         738 : }
     231             : 
     232             : /*
     233             :  * Ensure (sub)transaction stack entry for the given nest_level exists, adding
     234             :  * it if needed.
     235             :  */
     236             : PgStat_SubXactStatus *
     237     1834494 : pgstat_get_xact_stack_level(int nest_level)
     238             : {
     239             :     PgStat_SubXactStatus *xact_state;
     240             : 
     241     1834494 :     xact_state = pgStatXactStack;
     242     1834494 :     if (xact_state == NULL || xact_state->nest_level != nest_level)
     243             :     {
     244             :         xact_state = (PgStat_SubXactStatus *)
     245      593964 :             MemoryContextAlloc(TopTransactionContext,
     246             :                                sizeof(PgStat_SubXactStatus));
     247      593964 :         dclist_init(&xact_state->pending_drops);
     248      593964 :         xact_state->nest_level = nest_level;
     249      593964 :         xact_state->prev = pgStatXactStack;
     250      593964 :         xact_state->first = NULL;
     251      593964 :         pgStatXactStack = xact_state;
     252             :     }
     253     1834494 :     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      944042 : pgstat_get_transactional_drops(bool isCommit, xl_xact_stats_item **items)
     272             : {
     273      944042 :     PgStat_SubXactStatus *xact_state = pgStatXactStack;
     274      944042 :     int         nitems = 0;
     275             :     dlist_iter  iter;
     276             : 
     277      944042 :     if (xact_state == NULL)
     278      354732 :         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      589310 :     *items = palloc(dclist_count(&xact_state->pending_drops)
     288             :                     * sizeof(xl_xact_stats_item));
     289             : 
     290     1005638 :     dclist_foreach(iter, &xact_state->pending_drops)
     291             :     {
     292      416328 :         PgStat_PendingDroppedStatsItem *pending =
     293      416328 :             dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
     294             : 
     295      416328 :         if (isCommit && pending->is_create)
     296      346224 :             continue;
     297       70104 :         if (!isCommit && !pending->is_create)
     298         900 :             continue;
     299             : 
     300             :         Assert(nitems < dclist_count(&xact_state->pending_drops));
     301       69204 :         (*items)[nitems++] = pending->item;
     302             :     }
     303             : 
     304      589310 :     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        5722 : pgstat_execute_transactional_drops(int ndrops, struct xl_xact_stats_item *items, bool is_redo)
     314             : {
     315        5722 :     int         not_freed_count = 0;
     316             : 
     317        5722 :     if (ndrops == 0)
     318         730 :         return;
     319             : 
     320       20270 :     for (int i = 0; i < ndrops; i++)
     321             :     {
     322       15278 :         xl_xact_stats_item *it = &items[i];
     323             : 
     324       15278 :         if (!pgstat_drop_entry(it->kind, it->dboid, it->objoid))
     325           4 :             not_freed_count++;
     326             :     }
     327             : 
     328        4992 :     if (not_freed_count > 0)
     329           4 :         pgstat_request_entry_refs_gc();
     330             : }
     331             : 
     332             : static void
     333      414952 : create_drop_transactional_internal(PgStat_Kind kind, Oid dboid, Oid objoid, bool is_create)
     334             : {
     335      414952 :     int         nest_level = GetCurrentTransactionNestLevel();
     336             :     PgStat_SubXactStatus *xact_state;
     337             :     PgStat_PendingDroppedStatsItem *drop = (PgStat_PendingDroppedStatsItem *)
     338      414952 :         MemoryContextAlloc(TopTransactionContext, sizeof(PgStat_PendingDroppedStatsItem));
     339             : 
     340      414952 :     xact_state = pgstat_get_xact_stack_level(nest_level);
     341             : 
     342      414952 :     drop->is_create = is_create;
     343      414952 :     drop->item.kind = kind;
     344      414952 :     drop->item.dboid = dboid;
     345      414952 :     drop->item.objoid = objoid;
     346             : 
     347      414952 :     dclist_push_tail(&xact_state->pending_drops, &drop->node);
     348      414952 : }
     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      349840 : pgstat_create_transactional(PgStat_Kind kind, Oid dboid, Oid objoid)
     359             : {
     360      349840 :     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      349840 :     create_drop_transactional_internal(kind, dboid, objoid, /* create */ true);
     370      349840 : }
     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       65112 : pgstat_drop_transactional(PgStat_Kind kind, Oid dboid, Oid objoid)
     381             : {
     382       65112 :     create_drop_transactional_internal(kind, dboid, objoid, /* create */ false);
     383       65112 : }

Generated by: LCOV version 1.14