pgstat: Split out relation stats handling from AtEO[Sub]Xact_PgStat() etc.
authorAndres Freund <andres@anarazel.de>
Mon, 20 Sep 2021 20:56:16 +0000 (13:56 -0700)
committerAndres Freund <andres@anarazel.de>
Mon, 20 Sep 2021 20:56:16 +0000 (13:56 -0700)
An upcoming patch will add additional work to these functions. To avoid the
functions getting too complicated / doing too many things at once, split out
sub-tasks into their own functions.

Author: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/20210405092914.mmxqe7j56lsjfsej@alap3.anarazel.de

src/backend/postmaster/pgstat.c

index ab9c37c64f0d6e4f741e61224e6457cc43142ce3..69e89e4dec58633a53363b05accf4da4bb9c6434 100644 (file)
@@ -2374,18 +2374,68 @@ pgstat_update_heap_dead_tuples(Relation rel, int delta)
        pgstat_info->t_counts.t_delta_dead_tuples -= delta;
 }
 
-
-/* ----------
- * AtEOXact_PgStat
+/*
+ * Perform relation stats specific end-of-transaction work. Helper for
+ * AtEOXact_PgStat.
  *
- * Called from access/transam/xact.c at top-level transaction commit/abort.
- * ----------
+ * Transfer transactional insert/update counts into the base tabstat entries.
+ * We don't bother to free any of the transactional state, since it's all in
+ * TopTransactionContext and will go away anyway.
  */
-void
-AtEOXact_PgStat(bool isCommit, bool parallel)
+static void
+AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
 {
-   PgStat_SubXactStatus *xact_state;
+   PgStat_TableXactStatus *trans;
+
+   for (trans = xact_state->first; trans != NULL; trans = trans->next)
+   {
+       PgStat_TableStatus *tabstat;
+
+       Assert(trans->nest_level == 1);
+       Assert(trans->upper == NULL);
+       tabstat = trans->parent;
+       Assert(tabstat->trans == trans);
+       /* restore pre-truncate stats (if any) in case of aborted xact */
+       if (!isCommit)
+           pgstat_truncate_restore_counters(trans);
+       /* count attempted actions regardless of commit/abort */
+       tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
+       tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
+       tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
+       if (isCommit)
+       {
+           tabstat->t_counts.t_truncated = trans->truncated;
+           if (trans->truncated)
+           {
+               /* forget live/dead stats seen by backend thus far */
+               tabstat->t_counts.t_delta_live_tuples = 0;
+               tabstat->t_counts.t_delta_dead_tuples = 0;
+           }
+           /* insert adds a live tuple, delete removes one */
+           tabstat->t_counts.t_delta_live_tuples +=
+               trans->tuples_inserted - trans->tuples_deleted;
+           /* update and delete each create a dead tuple */
+           tabstat->t_counts.t_delta_dead_tuples +=
+               trans->tuples_updated + trans->tuples_deleted;
+           /* insert, update, delete each count as one change event */
+           tabstat->t_counts.t_changed_tuples +=
+               trans->tuples_inserted + trans->tuples_updated +
+               trans->tuples_deleted;
+       }
+       else
+       {
+           /* inserted tuples are dead, deleted tuples are unaffected */
+           tabstat->t_counts.t_delta_dead_tuples +=
+               trans->tuples_inserted + trans->tuples_updated;
+           /* an aborted xact generates no changed_tuple events */
+       }
+       tabstat->trans = NULL;
+   }
+}
 
+static void
+AtEOXact_PgStat_Database(bool isCommit, bool parallel)
+{
    /* Don't count parallel worker transaction stats */
    if (!parallel)
    {
@@ -2398,68 +2448,118 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
        else
            pgStatXactRollback++;
    }
+}
 
-   /*
-    * Transfer transactional insert/update counts into the base tabstat
-    * entries.  We don't bother to free any of the transactional state, since
-    * it's all in TopTransactionContext and will go away anyway.
-    */
+/* ----------
+ * AtEOXact_PgStat
+ *
+ * Called from access/transam/xact.c at top-level transaction commit/abort.
+ * ----------
+ */
+void
+AtEOXact_PgStat(bool isCommit, bool parallel)
+{
+   PgStat_SubXactStatus *xact_state;
+
+   AtEOXact_PgStat_Database(isCommit, parallel);
+
+   /* handle transactional stats information */
    xact_state = pgStatXactStack;
    if (xact_state != NULL)
    {
-       PgStat_TableXactStatus *trans;
-
        Assert(xact_state->nest_level == 1);
        Assert(xact_state->prev == NULL);
-       for (trans = xact_state->first; trans != NULL; trans = trans->next)
+
+       AtEOXact_PgStat_Relations(xact_state, isCommit);
+   }
+   pgStatXactStack = NULL;
+
+   /* Make sure any stats snapshot is thrown away */
+   pgstat_clear_snapshot();
+}
+
+/*
+ * Perform relation stats specific end-of-sub-transaction work. Helper for
+ * AtEOSubXact_PgStat.
+ *
+ * Transfer transactional insert/update counts into the next higher
+ * subtransaction state.
+ */
+static void
+AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
+{
+   PgStat_TableXactStatus *trans;
+   PgStat_TableXactStatus *next_trans;
+
+   for (trans = xact_state->first; trans != NULL; trans = next_trans)
+   {
+       PgStat_TableStatus *tabstat;
+
+       next_trans = trans->next;
+       Assert(trans->nest_level == nestDepth);
+       tabstat = trans->parent;
+       Assert(tabstat->trans == trans);
+
+       if (isCommit)
        {
-           PgStat_TableStatus *tabstat;
-
-           Assert(trans->nest_level == 1);
-           Assert(trans->upper == NULL);
-           tabstat = trans->parent;
-           Assert(tabstat->trans == trans);
-           /* restore pre-truncate stats (if any) in case of aborted xact */
-           if (!isCommit)
-               pgstat_truncate_restore_counters(trans);
-           /* count attempted actions regardless of commit/abort */
-           tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
-           tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
-           tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
-           if (isCommit)
+           if (trans->upper && trans->upper->nest_level == nestDepth - 1)
            {
-               tabstat->t_counts.t_truncated = trans->truncated;
                if (trans->truncated)
                {
-                   /* forget live/dead stats seen by backend thus far */
-                   tabstat->t_counts.t_delta_live_tuples = 0;
-                   tabstat->t_counts.t_delta_dead_tuples = 0;
+                   /* propagate the truncate status one level up */
+                   pgstat_truncate_save_counters(trans->upper);
+                   /* replace upper xact stats with ours */
+                   trans->upper->tuples_inserted = trans->tuples_inserted;
+                   trans->upper->tuples_updated = trans->tuples_updated;
+                   trans->upper->tuples_deleted = trans->tuples_deleted;
+               }
+               else
+               {
+                   trans->upper->tuples_inserted += trans->tuples_inserted;
+                   trans->upper->tuples_updated += trans->tuples_updated;
+                   trans->upper->tuples_deleted += trans->tuples_deleted;
                }
-               /* insert adds a live tuple, delete removes one */
-               tabstat->t_counts.t_delta_live_tuples +=
-                   trans->tuples_inserted - trans->tuples_deleted;
-               /* update and delete each create a dead tuple */
-               tabstat->t_counts.t_delta_dead_tuples +=
-                   trans->tuples_updated + trans->tuples_deleted;
-               /* insert, update, delete each count as one change event */
-               tabstat->t_counts.t_changed_tuples +=
-                   trans->tuples_inserted + trans->tuples_updated +
-                   trans->tuples_deleted;
+               tabstat->trans = trans->upper;
+               pfree(trans);
            }
            else
            {
-               /* inserted tuples are dead, deleted tuples are unaffected */
-               tabstat->t_counts.t_delta_dead_tuples +=
-                   trans->tuples_inserted + trans->tuples_updated;
-               /* an aborted xact generates no changed_tuple events */
+               /*
+                * When there isn't an immediate parent state, we can just
+                * reuse the record instead of going through a
+                * palloc/pfree pushup (this works since it's all in
+                * TopTransactionContext anyway).  We have to re-link it
+                * into the parent level, though, and that might mean
+                * pushing a new entry into the pgStatXactStack.
+                */
+               PgStat_SubXactStatus *upper_xact_state;
+
+               upper_xact_state = get_tabstat_stack_level(nestDepth - 1);
+               trans->next = upper_xact_state->first;
+               upper_xact_state->first = trans;
+               trans->nest_level = nestDepth - 1;
            }
-           tabstat->trans = NULL;
        }
-   }
-   pgStatXactStack = NULL;
+       else
+       {
+           /*
+            * On abort, update top-level tabstat counts, then forget the
+            * subtransaction
+            */
 
-   /* Make sure any stats snapshot is thrown away */
-   pgstat_clear_snapshot();
+           /* first restore values obliterated by truncate */
+           pgstat_truncate_restore_counters(trans);
+           /* count attempted actions regardless of commit/abort */
+           tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
+           tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
+           tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
+           /* inserted tuples are dead, deleted tuples are unaffected */
+           tabstat->t_counts.t_delta_dead_tuples +=
+               trans->tuples_inserted + trans->tuples_updated;
+           tabstat->trans = trans->upper;
+           pfree(trans);
+       }
+   }
 }
 
 /* ----------
@@ -2473,99 +2573,57 @@ AtEOSubXact_PgStat(bool isCommit, int nestDepth)
 {
    PgStat_SubXactStatus *xact_state;
 
-   /*
-    * Transfer transactional insert/update counts into the next higher
-    * subtransaction state.
-    */
+   /* merge the sub-transaction's transactional stats into the parent */
    xact_state = pgStatXactStack;
    if (xact_state != NULL &&
        xact_state->nest_level >= nestDepth)
    {
-       PgStat_TableXactStatus *trans;
-       PgStat_TableXactStatus *next_trans;
-
        /* delink xact_state from stack immediately to simplify reuse case */
        pgStatXactStack = xact_state->prev;
 
-       for (trans = xact_state->first; trans != NULL; trans = next_trans)
-       {
-           PgStat_TableStatus *tabstat;
-
-           next_trans = trans->next;
-           Assert(trans->nest_level == nestDepth);
-           tabstat = trans->parent;
-           Assert(tabstat->trans == trans);
-           if (isCommit)
-           {
-               if (trans->upper && trans->upper->nest_level == nestDepth - 1)
-               {
-                   if (trans->truncated)
-                   {
-                       /* propagate the truncate status one level up */
-                       pgstat_truncate_save_counters(trans->upper);
-                       /* replace upper xact stats with ours */
-                       trans->upper->tuples_inserted = trans->tuples_inserted;
-                       trans->upper->tuples_updated = trans->tuples_updated;
-                       trans->upper->tuples_deleted = trans->tuples_deleted;
-                   }
-                   else
-                   {
-                       trans->upper->tuples_inserted += trans->tuples_inserted;
-                       trans->upper->tuples_updated += trans->tuples_updated;
-                       trans->upper->tuples_deleted += trans->tuples_deleted;
-                   }
-                   tabstat->trans = trans->upper;
-                   pfree(trans);
-               }
-               else
-               {
-                   /*
-                    * When there isn't an immediate parent state, we can just
-                    * reuse the record instead of going through a
-                    * palloc/pfree pushup (this works since it's all in
-                    * TopTransactionContext anyway).  We have to re-link it
-                    * into the parent level, though, and that might mean
-                    * pushing a new entry into the pgStatXactStack.
-                    */
-                   PgStat_SubXactStatus *upper_xact_state;
-
-                   upper_xact_state = get_tabstat_stack_level(nestDepth - 1);
-                   trans->next = upper_xact_state->first;
-                   upper_xact_state->first = trans;
-                   trans->nest_level = nestDepth - 1;
-               }
-           }
-           else
-           {
-               /*
-                * On abort, update top-level tabstat counts, then forget the
-                * subtransaction
-                */
+       AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
 
-               /* first restore values obliterated by truncate */
-               pgstat_truncate_restore_counters(trans);
-               /* count attempted actions regardless of commit/abort */
-               tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
-               tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
-               tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
-               /* inserted tuples are dead, deleted tuples are unaffected */
-               tabstat->t_counts.t_delta_dead_tuples +=
-                   trans->tuples_inserted + trans->tuples_updated;
-               tabstat->trans = trans->upper;
-               pfree(trans);
-           }
-       }
        pfree(xact_state);
    }
 }
 
+/*
+ * Generate 2PC records for all the pending transaction-dependent relation
+ * stats.
+ */
+static void
+AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+{
+   PgStat_TableXactStatus *trans;
+
+   for (trans = xact_state->first; trans != NULL; trans = trans->next)
+   {
+       PgStat_TableStatus *tabstat;
+       TwoPhasePgStatRecord record;
+
+       Assert(trans->nest_level == 1);
+       Assert(trans->upper == NULL);
+       tabstat = trans->parent;
+       Assert(tabstat->trans == trans);
+
+       record.tuples_inserted = trans->tuples_inserted;
+       record.tuples_updated = trans->tuples_updated;
+       record.tuples_deleted = trans->tuples_deleted;
+       record.inserted_pre_trunc = trans->inserted_pre_trunc;
+       record.updated_pre_trunc = trans->updated_pre_trunc;
+       record.deleted_pre_trunc = trans->deleted_pre_trunc;
+       record.t_id = tabstat->t_id;
+       record.t_shared = tabstat->t_shared;
+       record.t_truncated = trans->truncated;
+
+       RegisterTwoPhaseRecord(TWOPHASE_RM_PGSTAT_ID, 0,
+                              &record, sizeof(TwoPhasePgStatRecord));
+   }
+}
 
 /*
  * AtPrepare_PgStat
  *     Save the transactional stats state at 2PC transaction prepare.
- *
- * In this phase we just generate 2PC records for all the pending
- * transaction-dependent stats work.
  */
 void
 AtPrepare_PgStat(void)
@@ -2575,44 +2633,38 @@ AtPrepare_PgStat(void)
    xact_state = pgStatXactStack;
    if (xact_state != NULL)
    {
-       PgStat_TableXactStatus *trans;
-
        Assert(xact_state->nest_level == 1);
        Assert(xact_state->prev == NULL);
-       for (trans = xact_state->first; trans != NULL; trans = trans->next)
-       {
-           PgStat_TableStatus *tabstat;
-           TwoPhasePgStatRecord record;
-
-           Assert(trans->nest_level == 1);
-           Assert(trans->upper == NULL);
-           tabstat = trans->parent;
-           Assert(tabstat->trans == trans);
-
-           record.tuples_inserted = trans->tuples_inserted;
-           record.tuples_updated = trans->tuples_updated;
-           record.tuples_deleted = trans->tuples_deleted;
-           record.inserted_pre_trunc = trans->inserted_pre_trunc;
-           record.updated_pre_trunc = trans->updated_pre_trunc;
-           record.deleted_pre_trunc = trans->deleted_pre_trunc;
-           record.t_id = tabstat->t_id;
-           record.t_shared = tabstat->t_shared;
-           record.t_truncated = trans->truncated;
-
-           RegisterTwoPhaseRecord(TWOPHASE_RM_PGSTAT_ID, 0,
-                                  &record, sizeof(TwoPhasePgStatRecord));
-       }
+
+       AtPrepare_PgStat_Relations(xact_state);
    }
 }
 
 /*
- * PostPrepare_PgStat
- *     Clean up after successful PREPARE.
- *
  * All we need do here is unlink the transaction stats state from the
  * nontransactional state.  The nontransactional action counts will be
- * reported to the stats collector immediately, while the effects on live
- * and dead tuple counts are preserved in the 2PC state file.
+ * reported to the stats collector immediately, while the effects on
+ * live and dead tuple counts are preserved in the 2PC state file.
+ *
+ * Note: AtEOXact_PgStat_Relations is not called during PREPARE.
+ */
+static void
+PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+{
+   PgStat_TableXactStatus *trans;
+
+   for (trans = xact_state->first; trans != NULL; trans = trans->next)
+   {
+       PgStat_TableStatus *tabstat;
+
+       tabstat = trans->parent;
+       tabstat->trans = NULL;
+   }
+}
+
+/*
+ * PostPrepare_PgStat
+ *     Clean up after successful PREPARE.
  *
  * Note: AtEOXact_PgStat is not called during PREPARE.
  */
@@ -2628,15 +2680,10 @@ PostPrepare_PgStat(void)
    xact_state = pgStatXactStack;
    if (xact_state != NULL)
    {
-       PgStat_TableXactStatus *trans;
-
-       for (trans = xact_state->first; trans != NULL; trans = trans->next)
-       {
-           PgStat_TableStatus *tabstat;
+       Assert(xact_state->nest_level == 1);
+       Assert(xact_state->prev == NULL);
 
-           tabstat = trans->parent;
-           tabstat->trans = NULL;
-       }
+       PostPrepare_PgStat_Relations(xact_state);
    }
    pgStatXactStack = NULL;