Add n_tup_newpage_upd to pg_stat table views

Started by Corey Huinkeralmost 3 years ago12 messages
#1Corey Huinker
corey.huinker@gmail.com
1 attachment(s)

This patch adds the n_tup_newpage_upd to all the table stat views.

Just as we currently track HOT updates, it should be beneficial to track
updates where the new tuple cannot fit on the existing page and must go to
a different one.

Hopefully this can give users some insight as to whether their current
fillfactor settings need to be adjusted.

My chosen implementation replaces the hot-update boolean with an
update_type which is currently a three-value enum. I favored that
only slightly over adding a separate newpage-update boolean because the two
events are mutually exclusive and fewer parameters is less overhead and one
less assertion check. The relative wisdom of this choice may not come to
light until we add a new measurement and see whether that new measurement
overlaps either is-hot or is-new-page.

Attachments:

v1-0001-Add-n_tup_newpage_upd-to-pg_stat_all_tables-and-p.patchtext/x-patch; charset=US-ASCII; name=v1-0001-Add-n_tup_newpage_upd-to-pg_stat_all_tables-and-p.patchDownload
From 55204b3d2f719f5dd8c308ea722606a40b3d09b8 Mon Sep 17 00:00:00 2001
From: coreyhuinker <corey.huinker@gmail.com>
Date: Fri, 27 Jan 2023 17:56:59 -0500
Subject: [PATCH v1] Add n_tup_newpage_upd to pg_stat_all_tables and
 pg_stat_xact_all_tables. This value reflects the number of tuples updated
 where the new tuple was placed on a different page than the previous version.

---
 doc/src/sgml/monitoring.sgml                 |  9 +++++++++
 src/backend/access/heap/heapam.c             | 10 ++++++----
 src/backend/catalog/system_views.sql         |  4 +++-
 src/backend/utils/activity/pgstat_relation.c |  8 ++++++--
 src/backend/utils/adt/pgstatfuncs.c          | 18 ++++++++++++++++++
 src/include/catalog/pg_proc.dat              |  9 +++++++++
 src/include/pgstat.h                         | 14 ++++++++++++--
 src/test/regress/expected/rules.out          | 12 +++++++++---
 8 files changed, 72 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 1756f1a4b6..f0291a21fb 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4523,6 +4523,15 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
       </para></entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>n_tup_newpage_upd</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of rows updated where the new row is on a different page than the previous version
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>n_live_tup</structfield> <type>bigint</type>
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index e6024a980b..f5aa429a9a 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -3155,7 +3155,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
 				pagefree;
 	bool		have_tuple_lock = false;
 	bool		iscombo;
-	bool		use_hot_update = false;
+	PgStat_HeapUpdateType update_type = PGSTAT_HEAPUPDATE_NON_HOT;
+
 	bool		key_intact;
 	bool		all_visible_cleared = false;
 	bool		all_visible_cleared_new = false;
@@ -3838,10 +3839,11 @@ l2:
 		 * changed.
 		 */
 		if (!bms_overlap(modified_attrs, hot_attrs))
-			use_hot_update = true;
+			update_type = PGSTAT_HEAPUPDATE_HOT;
 	}
 	else
 	{
+		update_type = PGSTAT_HEAPUPDATE_NEW_PAGE;
 		/* Set a hint that the old page could use prune/defrag */
 		PageSetFull(page);
 	}
@@ -3875,7 +3877,7 @@ l2:
 	 */
 	PageSetPrunable(page, xid);
 
-	if (use_hot_update)
+	if (update_type == PGSTAT_HEAPUPDATE_HOT)
 	{
 		/* Mark the old tuple as HOT-updated */
 		HeapTupleSetHotUpdated(&oldtup);
@@ -3986,7 +3988,7 @@ l2:
 	if (have_tuple_lock)
 		UnlockTupleTuplock(relation, &(oldtup.t_self), *lockmode);
 
-	pgstat_count_heap_update(relation, use_hot_update);
+	pgstat_count_heap_update(relation, update_type);
 
 	/*
 	 * If heaptup is a private copy, release it.  Don't forget to copy t_self
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 8608e3fa5b..292a9b88b3 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -665,6 +665,7 @@ CREATE VIEW pg_stat_all_tables AS
             pg_stat_get_tuples_updated(C.oid) AS n_tup_upd,
             pg_stat_get_tuples_deleted(C.oid) AS n_tup_del,
             pg_stat_get_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
+            pg_stat_get_tuples_newpage_updated(C.oid) AS n_tup_newpage_upd,
             pg_stat_get_live_tuples(C.oid) AS n_live_tup,
             pg_stat_get_dead_tuples(C.oid) AS n_dead_tup,
             pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
@@ -696,7 +697,8 @@ CREATE VIEW pg_stat_xact_all_tables AS
             pg_stat_get_xact_tuples_inserted(C.oid) AS n_tup_ins,
             pg_stat_get_xact_tuples_updated(C.oid) AS n_tup_upd,
             pg_stat_get_xact_tuples_deleted(C.oid) AS n_tup_del,
-            pg_stat_get_xact_tuples_hot_updated(C.oid) AS n_tup_hot_upd
+            pg_stat_get_xact_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
+            pg_stat_get_xact_tuples_newpage_updated(C.oid) AS n_tup_newpage_upd
     FROM pg_class C LEFT JOIN
          pg_index I ON C.oid = I.indrelid
          LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index 2e20b93c20..920ee71e35 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -362,7 +362,7 @@ pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
  * count a tuple update
  */
 void
-pgstat_count_heap_update(Relation rel, bool hot)
+pgstat_count_heap_update(Relation rel, PgStat_HeapUpdateType hut)
 {
 	if (pgstat_should_count_relation(rel))
 	{
@@ -372,8 +372,11 @@ pgstat_count_heap_update(Relation rel, bool hot)
 		pgstat_info->trans->tuples_updated++;
 
 		/* t_tuples_hot_updated is nontransactional, so just advance it */
-		if (hot)
+		if (hut == PGSTAT_HEAPUPDATE_HOT)
 			pgstat_info->t_counts.t_tuples_hot_updated++;
+		else if (hut == PGSTAT_HEAPUPDATE_NEW_PAGE)
+			pgstat_info->t_counts.t_tuples_newpage_updated++;
+
 	}
 }
 
@@ -793,6 +796,7 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 	tabentry->tuples_updated += lstats->t_counts.t_tuples_updated;
 	tabentry->tuples_deleted += lstats->t_counts.t_tuples_deleted;
 	tabentry->tuples_hot_updated += lstats->t_counts.t_tuples_hot_updated;
+	tabentry->tuples_newpage_updated += lstats->t_counts.t_tuples_newpage_updated;
 
 	/*
 	 * If table was truncated/dropped, first reset the live/dead counters.
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 6737493402..ace9a46b6e 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -92,6 +92,9 @@ PG_STAT_GET_RELENTRY_INT64(tuples_fetched)
 /* pg_stat_get_tuples_hot_updated */
 PG_STAT_GET_RELENTRY_INT64(tuples_hot_updated)
 
+/* pg_stat_get_tuples_newpage_updated */
+PG_STAT_GET_RELENTRY_INT64(tuples_newpage_updated)
+
 /* pg_stat_get_tuples_inserted */
 PG_STAT_GET_RELENTRY_INT64(tuples_inserted)
 
@@ -1479,6 +1482,21 @@ pg_stat_get_xact_tuples_hot_updated(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_xact_tuples_newpage_updated(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	int64		result;
+	PgStat_TableStatus *tabentry;
+
+	if ((tabentry = find_tabstat_entry(relid)) == NULL)
+		result = 0;
+	else
+		result = (int64) (tabentry->t_counts.t_tuples_newpage_updated);
+
+	PG_RETURN_INT64(result);
+}
+
 Datum
 pg_stat_get_xact_blocks_fetched(PG_FUNCTION_ARGS)
 {
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index c0f2a8a77c..4a7ca3e146 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5337,6 +5337,10 @@
   proname => 'pg_stat_get_tuples_hot_updated', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_tuples_hot_updated' },
+{ oid => '4642', descr => 'statistics: number of tuples non-hot newpage updated',
+  proname => 'pg_stat_get_tuples_newpage_updated', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tuples_newpage_updated' },
 { oid => '2878', descr => 'statistics: number of live tuples',
   proname => 'pg_stat_get_live_tuples', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
@@ -5791,6 +5795,11 @@
   proname => 'pg_stat_get_xact_tuples_hot_updated', provolatile => 'v',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_xact_tuples_hot_updated' },
+{ oid => '8704',
+  descr => 'statistics: number of tuples newpage updated in current transaction',
+  proname => 'pg_stat_get_xact_tuples_newpage_updated', provolatile => 'v',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_xact_tuples_newpage_updated' },
 { oid => '3044',
   descr => 'statistics: number of blocks fetched in current transaction',
   proname => 'pg_stat_get_xact_blocks_fetched', provolatile => 'v',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 5e3326a3b9..4b21f8ffe7 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -81,6 +81,13 @@ typedef enum SessionEndType
 	DISCONNECT_KILLED
 } SessionEndType;
 
+typedef enum PgStat_HeapUpdateType
+{
+	PGSTAT_HEAPUPDATE_NON_HOT, /* regular update, same page */
+	PGSTAT_HEAPUPDATE_HOT, /* HOT update */
+	PGSTAT_HEAPUPDATE_NEW_PAGE /* regular update, new page */
+} PgStat_HeapUpdateType;
+
 /* ----------
  * The data type used for counters.
  * ----------
@@ -159,7 +166,7 @@ typedef struct PgStat_BackendSubEntry
  * the index AM, while tuples_fetched is the number of tuples successfully
  * fetched by heap_fetch under the control of simple indexscans for this index.
  *
- * tuples_inserted/updated/deleted/hot_updated count attempted actions,
+ * tuples_inserted/updated/deleted/hot_updated/newpage_updated count attempted actions,
  * regardless of whether the transaction committed.  delta_live_tuples,
  * delta_dead_tuples, and changed_tuples are set depending on commit or abort.
  * Note that delta_live_tuples and delta_dead_tuples can be negative!
@@ -176,6 +183,7 @@ typedef struct PgStat_TableCounts
 	PgStat_Counter t_tuples_updated;
 	PgStat_Counter t_tuples_deleted;
 	PgStat_Counter t_tuples_hot_updated;
+	PgStat_Counter t_tuples_newpage_updated;
 	bool		t_truncdropped;
 
 	PgStat_Counter t_delta_live_tuples;
@@ -363,6 +371,7 @@ typedef struct PgStat_StatTabEntry
 	PgStat_Counter tuples_updated;
 	PgStat_Counter tuples_deleted;
 	PgStat_Counter tuples_hot_updated;
+	PgStat_Counter tuples_newpage_updated;
 
 	PgStat_Counter live_tuples;
 	PgStat_Counter dead_tuples;
@@ -560,7 +569,8 @@ extern void pgstat_report_analyze(Relation rel,
 	} while (0)
 
 extern void pgstat_count_heap_insert(Relation rel, PgStat_Counter n);
-extern void pgstat_count_heap_update(Relation rel, bool hot);
+extern void pgstat_count_heap_update(Relation rel, PgStat_HeapUpdateType hut);
+
 extern void pgstat_count_heap_delete(Relation rel);
 extern void pgstat_count_truncate(Relation rel);
 extern void pgstat_update_heap_dead_tuples(Relation rel, int delta);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index e7a2f5856a..bdd56d6283 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1789,6 +1789,7 @@ pg_stat_all_tables| SELECT c.oid AS relid,
     pg_stat_get_tuples_updated(c.oid) AS n_tup_upd,
     pg_stat_get_tuples_deleted(c.oid) AS n_tup_del,
     pg_stat_get_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
+    pg_stat_get_tuples_newpage_updated(c.oid) AS n_tup_newpage_upd,
     pg_stat_get_live_tuples(c.oid) AS n_live_tup,
     pg_stat_get_dead_tuples(c.oid) AS n_dead_tup,
     pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
@@ -2134,6 +2135,7 @@ pg_stat_sys_tables| SELECT relid,
     n_tup_upd,
     n_tup_del,
     n_tup_hot_upd,
+    n_tup_newpage_upd,
     n_live_tup,
     n_dead_tup,
     n_mod_since_analyze,
@@ -2181,6 +2183,7 @@ pg_stat_user_tables| SELECT relid,
     n_tup_upd,
     n_tup_del,
     n_tup_hot_upd,
+    n_tup_newpage_upd,
     n_live_tup,
     n_dead_tup,
     n_mod_since_analyze,
@@ -2232,7 +2235,8 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid,
     pg_stat_get_xact_tuples_inserted(c.oid) AS n_tup_ins,
     pg_stat_get_xact_tuples_updated(c.oid) AS n_tup_upd,
     pg_stat_get_xact_tuples_deleted(c.oid) AS n_tup_del,
-    pg_stat_get_xact_tuples_hot_updated(c.oid) AS n_tup_hot_upd
+    pg_stat_get_xact_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
+    pg_stat_get_xact_tuples_newpage_updated(c.oid) AS n_tup_newpage_upd
    FROM ((pg_class c
      LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))
      LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
@@ -2248,7 +2252,8 @@ pg_stat_xact_sys_tables| SELECT relid,
     n_tup_ins,
     n_tup_upd,
     n_tup_del,
-    n_tup_hot_upd
+    n_tup_hot_upd,
+    n_tup_newpage_upd
    FROM pg_stat_xact_all_tables
   WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text));
 pg_stat_xact_user_functions| SELECT p.oid AS funcid,
@@ -2270,7 +2275,8 @@ pg_stat_xact_user_tables| SELECT relid,
     n_tup_ins,
     n_tup_upd,
     n_tup_del,
-    n_tup_hot_upd
+    n_tup_hot_upd,
+    n_tup_newpage_upd
    FROM pg_stat_xact_all_tables
   WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text));
 pg_statio_all_indexes| SELECT c.oid AS relid,
-- 
2.39.1

#2Andres Freund
andres@anarazel.de
In reply to: Corey Huinker (#1)
Re: Add n_tup_newpage_upd to pg_stat table views

Hi,

On 2023-01-27 18:23:39 -0500, Corey Huinker wrote:

This patch adds the n_tup_newpage_upd to all the table stat views.

Just as we currently track HOT updates, it should be beneficial to track
updates where the new tuple cannot fit on the existing page and must go to
a different one.

I like that idea.

I wonder if it's quite detailed enough - we can be forced to do out-of-page
updates because we actually are out of space, or because we reach the max
number of line pointers we allow in a page. HOT pruning can't remove dead line
pointers, so that doesn't necessarily help.

Which e.g. means that:

Hopefully this can give users some insight as to whether their current
fillfactor settings need to be adjusted.

Isn't that easy, because you can have a page with just a visible single tuple
on, but still be unable to do a same-page update. The fix instead is to VACUUM
(more aggressively).

OTOH, just seeing that there's high percentage "out-of-page updates" provides
more information than we have right now. And the alternative would be to add
yet another counter.

Similarly, it's a bit sad that we can't distinguish between the number of
potential-HOT out-of-page updates and the other out-of-page updates. But
that'd mean even more counters.

I guess we could try to add tracepoints to allow to distinguish between those
cases instead? Not a lot of people use those though.

@@ -372,8 +372,11 @@ pgstat_count_heap_update(Relation rel, bool hot)
pgstat_info->trans->tuples_updated++;

/* t_tuples_hot_updated is nontransactional, so just advance it */
-		if (hot)
+		if (hut == PGSTAT_HEAPUPDATE_HOT)
pgstat_info->t_counts.t_tuples_hot_updated++;
+		else if (hut == PGSTAT_HEAPUPDATE_NEW_PAGE)
+			pgstat_info->t_counts.t_tuples_newpage_updated++;
+
}
}

I think this might cause some trouble for existing monitoring setups after an
upgrade. Suddenly the number of updates will appear way lower than
before... And if we end up eventually distinguishing between different reasons
for out-of-page updates, or hot/non-hot out-of-page that'll happen again.

I wish we'd included HOT updates in the total number of updates, and just kept
HOT updates a separate counter that'd always be less than updates in total.

From that angle: Perhaps it'd be better to have counter for how many times a
page is found to be full during an update?

--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -3155,7 +3155,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
pagefree;
bool		have_tuple_lock = false;
bool		iscombo;
-	bool		use_hot_update = false;
+	PgStat_HeapUpdateType update_type = PGSTAT_HEAPUPDATE_NON_HOT;
+
bool		key_intact;
bool		all_visible_cleared = false;
bool		all_visible_cleared_new = false;
@@ -3838,10 +3839,11 @@ l2:
* changed.
*/
if (!bms_overlap(modified_attrs, hot_attrs))
-			use_hot_update = true;
+			update_type = PGSTAT_HEAPUPDATE_HOT;
}
else
{
+		update_type = PGSTAT_HEAPUPDATE_NEW_PAGE;
/* Set a hint that the old page could use prune/defrag */
PageSetFull(page);
}
@@ -3875,7 +3877,7 @@ l2:
*/
PageSetPrunable(page, xid);
-	if (use_hot_update)
+	if (update_type == PGSTAT_HEAPUPDATE_HOT)

It's a bit weird that heap_update() uses a pgstat type to decide what to
do. But not sure there's a much better alternative.

Greetings,

Andres Freund

In reply to: Andres Freund (#2)
Re: Add n_tup_newpage_upd to pg_stat table views

On Fri, Jan 27, 2023 at 3:55 PM Andres Freund <andres@anarazel.de> wrote:

I wonder if it's quite detailed enough - we can be forced to do out-of-page
updates because we actually are out of space, or because we reach the max
number of line pointers we allow in a page. HOT pruning can't remove dead line
pointers, so that doesn't necessarily help.

It would be hard to apply that kind of information, I suspect. Maybe
it's still worth having, though.

Similarly, it's a bit sad that we can't distinguish between the number of
potential-HOT out-of-page updates and the other out-of-page updates. But
that'd mean even more counters.

ISTM that it would make more sense to do that at the index level
instead. It wouldn't be all that hard to teach ExecInsertIndexTuples()
to remember whether each index received the indexUnchanged hint used
by bottom-up deletion, which is approximately the same thing, but
works at the index level.

This is obviously more useful, because you have index-granularity
information that can guide users in how to index to maximize the
number of HOT updates. And, even if changing things around didn't lead
to the hoped-for improvement in the rate of HOT updates, it would at
least still allow the indexes on the table to use bottom-up deletion
more often, on average.

Admittedly this has some problems. The index_unchanged_by_update()
logic probably isn't as sophisticated as it ought to be because it's
driven by the statement-level extraUpdatedCols bitmap set, and not a
per-tuple test, like the HOT safety test in heap_update() is.
But...that should probably be fixed anyway.

I think this might cause some trouble for existing monitoring setups after an
upgrade. Suddenly the number of updates will appear way lower than
before... And if we end up eventually distinguishing between different reasons
for out-of-page updates, or hot/non-hot out-of-page that'll happen again.

Uh...no it won't? The new counter is totally independent of the existing
HOT counter, and the transactional all-updates counter. It's just that
there is an enum that encodes which of the two non-transactional "sub
counters" to use (either for HOT updates or new-page-migration
updates).

I wish we'd included HOT updates in the total number of updates, and just kept
HOT updates a separate counter that'd always be less than updates in total.

Uh...we did in fact do it that way to begin with?

From that angle: Perhaps it'd be better to have counter for how many times a
page is found to be full during an update?

Didn't Corey propose a patch to add just that? Do you mean something
more specific, like a tracker for when an UPDATE leaves a page full,
without needing to go to a new page itself?

If so, then that does require defining what that really means, because
it isn't trivial. Do you assume that all updates have a successor
version that is equal in size to that of the UPDATE that gets counted
by this hypothetical other counter of yours?

--
Peter Geoghegan

#4Andres Freund
andres@anarazel.de
In reply to: Peter Geoghegan (#3)
Re: Add n_tup_newpage_upd to pg_stat table views

Hi,

On 2023-01-27 17:59:32 -0800, Peter Geoghegan wrote:

I think this might cause some trouble for existing monitoring setups after an
upgrade. Suddenly the number of updates will appear way lower than
before... And if we end up eventually distinguishing between different reasons
for out-of-page updates, or hot/non-hot out-of-page that'll happen again.

Uh...no it won't? The new counter is totally independent of the existing
HOT counter, and the transactional all-updates counter. It's just that
there is an enum that encodes which of the two non-transactional "sub
counters" to use (either for HOT updates or new-page-migration
updates).

I wish we'd included HOT updates in the total number of updates, and just kept
HOT updates a separate counter that'd always be less than updates in total.

Uh...we did in fact do it that way to begin with?

Sorry, I misread the diff, and then misremembered some old issue.

From that angle: Perhaps it'd be better to have counter for how many times a
page is found to be full during an update?

Didn't Corey propose a patch to add just that? Do you mean something
more specific, like a tracker for when an UPDATE leaves a page full,
without needing to go to a new page itself?

Nope, I just had a brainfart.

Similarly, it's a bit sad that we can't distinguish between the number of
potential-HOT out-of-page updates and the other out-of-page updates. But
that'd mean even more counters.

ISTM that it would make more sense to do that at the index level
instead. It wouldn't be all that hard to teach ExecInsertIndexTuples()
to remember whether each index received the indexUnchanged hint used
by bottom-up deletion, which is approximately the same thing, but
works at the index level.

I don't think that'd make it particularly easy to figure out how often
out-of-space causes non-HOT updates to go out of page, and how often it causes
potential HOT updates to go out of page. If you just have a single index,
it's not too hard, but after that seems decidedly nontrival.

But I might just be missing what you're suggesting.

Greetings,

Andres Freund

In reply to: Andres Freund (#4)
Re: Add n_tup_newpage_upd to pg_stat table views

On Fri, Jan 27, 2023 at 6:44 PM Andres Freund <andres@anarazel.de> wrote:

I don't think that'd make it particularly easy to figure out how often
out-of-space causes non-HOT updates to go out of page, and how often it causes
potential HOT updates to go out of page. If you just have a single index,
it's not too hard, but after that seems decidedly nontrival.

But I might just be missing what you're suggesting.

It would be useless for that, of course. But it would be a good proxy
for what specific indexes force non-hot updates due to HOT safety
issues. This would work independently of the issue of what's going on
in the heap. That matters too, of course, but in practice the main
problem is likely the specific combination of indexes and updates.
(Maybe it would just be an issue with heap fill factor, at times, but
even then you'd still want to rule out basic HOT safety issues first.)

If you see one particular index that gets a far larger number of
non-hot updates that are reported as "logical changes to the indexed
columns", then dropping that index has the potential to make the HOT
update situation far better.

--
Peter Geoghegan

#6Corey Huinker
corey.huinker@gmail.com
In reply to: Andres Freund (#2)
Re: Add n_tup_newpage_upd to pg_stat table views

On Fri, Jan 27, 2023 at 6:55 PM Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2023-01-27 18:23:39 -0500, Corey Huinker wrote:

This patch adds the n_tup_newpage_upd to all the table stat views.

Just as we currently track HOT updates, it should be beneficial to track
updates where the new tuple cannot fit on the existing page and must go

to

a different one.

I like that idea.

I wonder if it's quite detailed enough - we can be forced to do out-of-page
updates because we actually are out of space, or because we reach the max
number of line pointers we allow in a page. HOT pruning can't remove dead
line
pointers, so that doesn't necessarily help.

I must be missing something, I only see the check for running out of space,
not the check for exhausting line pointers. I agree dividing them would be
interesting.

Similarly, it's a bit sad that we can't distinguish between the number of
potential-HOT out-of-page updates and the other out-of-page updates. But
that'd mean even more counters.

I wondered that too, but the combinations of "would have been HOT but not
no space" and "key update suggested not-HOT but it was id=id so today's
your lucky HOT" combinations started to get away from me.

I wondered if there was interest in knowing if the tuple had to get
TOASTed, and further wondered if we would be interested in the number of
updates that had to wait on a lock. Again, more counters.

#7Andres Freund
andres@anarazel.de
In reply to: Corey Huinker (#6)
Re: Add n_tup_newpage_upd to pg_stat table views

Hi,

On 2023-01-30 13:40:15 -0500, Corey Huinker wrote:

I must be missing something, I only see the check for running out of space,
not the check for exhausting line pointers. I agree dividing them would be
interesting.

See PageGetHeapFreeSpace(), particularly the header comment and the
MaxHeapTuplesPerPage check.

Similarly, it's a bit sad that we can't distinguish between the number of
potential-HOT out-of-page updates and the other out-of-page updates. But
that'd mean even more counters.

I wondered that too, but the combinations of "would have been HOT but not
no space" and "key update suggested not-HOT but it was id=id so today's
your lucky HOT" combinations started to get away from me.

Not sure I follow the second part. Are you just worried about explaining when
a HOT update is possible?

I wondered if there was interest in knowing if the tuple had to get
TOASTed, and further wondered if we would be interested in the number of
updates that had to wait on a lock. Again, more counters.

Those seem a lot less actionable / related to the topic at hand to me.

Greetings,

Andres Freund

In reply to: Corey Huinker (#1)
Re: Add n_tup_newpage_upd to pg_stat table views

On Fri, Jan 27, 2023 at 3:23 PM Corey Huinker <corey.huinker@gmail.com> wrote:

This patch adds the n_tup_newpage_upd to all the table stat views.

I think that this is pretty close to being committable already.

I'll move on that early next week, barring any objections.

--
Peter Geoghegan

In reply to: Peter Geoghegan (#8)
1 attachment(s)
Re: Add n_tup_newpage_upd to pg_stat table views

On Fri, Mar 17, 2023 at 3:22 PM Peter Geoghegan <pg@bowt.ie> wrote:

I think that this is pretty close to being committable already.

Attached revision has some small tweaks by me. Going to commit this
revised version tomorrow morning.

Changes:

* No more dedicated struct to carry around the type of an update.

We just use two boolean arguments to the pgstats function instead. The
struct didn't seem to be adding much, and it was distracting to track
the information this way within heap_update().

* Small adjustments to the documentation.

Nearby related items were tweaked slightly to make everything fit
together a bit better. For example, the description of n_tup_hot_upd
is revised to make it obvious that n_tup_hot_upd counts row updates
that can never get counted under the new n_tup_newpage_upd counter.

--
Peter Geoghegan

Attachments:

v2-0001-Count-the-number-of-new-page-updates-in-pgstats.patchapplication/x-patch; name=v2-0001-Count-the-number-of-new-page-updates-in-pgstats.patchDownload
From 23d768e87e95e421e583e2f0dc06bd36534081a6 Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@bowt.ie>
Date: Wed, 22 Mar 2023 11:58:18 -0700
Subject: [PATCH v2] Count the number of new page updates in pgstats.

Bump catalog and stats format versions.

Corey Huinker, with some tweaks by me.

Author: Corey Huinker <corey.huinker@gmail.com>
Reviewed-By: Peter Geoghegan <pg@bowt.ie>
Reviewed-By: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/CADkLM=ded21M9iZ36hHm-vj2rE2d=zcKpUQMds__Xm2pxLfHKA@mail.gmail.com
---
 src/include/catalog/catversion.h             |  2 +-
 src/include/catalog/pg_proc.dat              | 10 ++++++++
 src/include/pgstat.h                         | 10 ++++----
 src/backend/access/heap/heapam.c             |  2 +-
 src/backend/catalog/system_views.sql         |  4 +++-
 src/backend/utils/activity/pgstat_relation.c | 12 ++++++++--
 src/backend/utils/adt/pgstatfuncs.c          | 18 ++++++++++++++
 doc/src/sgml/monitoring.sgml                 | 25 ++++++++++++++++----
 src/test/regress/expected/rules.out          | 12 +++++++---
 9 files changed, 78 insertions(+), 17 deletions(-)

diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index e94528a7c..0c0915885 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -57,6 +57,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	202303181
+#define CATALOG_VERSION_NO	202303221
 
 #endif
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 5cf87aeb2..7c358cff1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5360,6 +5360,11 @@
   proname => 'pg_stat_get_tuples_hot_updated', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_tuples_hot_updated' },
+{ oid => '8614',
+  descr => 'statistics: number of tuples updated onto a new page',
+  proname => 'pg_stat_get_tuples_newpage_updated', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tuples_newpage_updated' },
 { oid => '2878', descr => 'statistics: number of live tuples',
   proname => 'pg_stat_get_live_tuples', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
@@ -5823,6 +5828,11 @@
   proname => 'pg_stat_get_xact_tuples_hot_updated', provolatile => 'v',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_xact_tuples_hot_updated' },
+{ oid => '8615',
+  descr => 'statistics: number of tuples updated onto a new page in current transaction',
+  proname => 'pg_stat_get_xact_tuples_newpage_updated', provolatile => 'v',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_xact_tuples_newpage_updated' },
 { oid => '3044',
   descr => 'statistics: number of blocks fetched in current transaction',
   proname => 'pg_stat_get_xact_blocks_fetched', provolatile => 'v',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 1e418b682..46d053422 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -151,8 +151,8 @@ typedef struct PgStat_BackendSubEntry
  * the index AM, while tuples_fetched is the number of tuples successfully
  * fetched by heap_fetch under the control of simple indexscans for this index.
  *
- * tuples_inserted/updated/deleted/hot_updated count attempted actions,
- * regardless of whether the transaction committed.  delta_live_tuples,
+ * tuples_inserted/updated/deleted/hot_updated/newpage_updated count attempted
+ * actions, regardless of whether the transaction committed.  delta_live_tuples,
  * delta_dead_tuples, and changed_tuples are set depending on commit or abort.
  * Note that delta_live_tuples and delta_dead_tuples can be negative!
  * ----------
@@ -168,6 +168,7 @@ typedef struct PgStat_TableCounts
 	PgStat_Counter t_tuples_updated;
 	PgStat_Counter t_tuples_deleted;
 	PgStat_Counter t_tuples_hot_updated;
+	PgStat_Counter t_tuples_newpage_updated;
 	bool		t_truncdropped;
 
 	PgStat_Counter t_delta_live_tuples;
@@ -234,7 +235,7 @@ typedef struct PgStat_TableXactStatus
  * ------------------------------------------------------------
  */
 
-#define PGSTAT_FILE_FORMAT_ID	0x01A5BCAA
+#define PGSTAT_FILE_FORMAT_ID	0x01A5BCAB
 
 typedef struct PgStat_ArchiverStats
 {
@@ -401,6 +402,7 @@ typedef struct PgStat_StatTabEntry
 	PgStat_Counter tuples_updated;
 	PgStat_Counter tuples_deleted;
 	PgStat_Counter tuples_hot_updated;
+	PgStat_Counter tuples_newpage_updated;
 
 	PgStat_Counter live_tuples;
 	PgStat_Counter dead_tuples;
@@ -616,7 +618,7 @@ extern void pgstat_report_analyze(Relation rel,
 	} while (0)
 
 extern void pgstat_count_heap_insert(Relation rel, PgStat_Counter n);
-extern void pgstat_count_heap_update(Relation rel, bool hot);
+extern void pgstat_count_heap_update(Relation rel, bool hot, bool newpage);
 extern void pgstat_count_heap_delete(Relation rel);
 extern void pgstat_count_truncate(Relation rel);
 extern void pgstat_update_heap_dead_tuples(Relation rel, int delta);
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index cf4b917eb..8abc101c8 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -3803,7 +3803,7 @@ l2:
 	if (have_tuple_lock)
 		UnlockTupleTuplock(relation, &(oldtup.t_self), *lockmode);
 
-	pgstat_count_heap_update(relation, use_hot_update);
+	pgstat_count_heap_update(relation, use_hot_update, newbuf != buffer);
 
 	/*
 	 * If heaptup is a private copy, release it.  Don't forget to copy t_self
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 34ca0e739..8ea159dbd 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -665,6 +665,7 @@ CREATE VIEW pg_stat_all_tables AS
             pg_stat_get_tuples_updated(C.oid) AS n_tup_upd,
             pg_stat_get_tuples_deleted(C.oid) AS n_tup_del,
             pg_stat_get_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
+            pg_stat_get_tuples_newpage_updated(C.oid) AS n_tup_newpage_upd,
             pg_stat_get_live_tuples(C.oid) AS n_live_tup,
             pg_stat_get_dead_tuples(C.oid) AS n_dead_tup,
             pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
@@ -696,7 +697,8 @@ CREATE VIEW pg_stat_xact_all_tables AS
             pg_stat_get_xact_tuples_inserted(C.oid) AS n_tup_ins,
             pg_stat_get_xact_tuples_updated(C.oid) AS n_tup_upd,
             pg_stat_get_xact_tuples_deleted(C.oid) AS n_tup_del,
-            pg_stat_get_xact_tuples_hot_updated(C.oid) AS n_tup_hot_upd
+            pg_stat_get_xact_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
+            pg_stat_get_xact_tuples_newpage_updated(C.oid) AS n_tup_newpage_upd
     FROM pg_class C LEFT JOIN
          pg_index I ON C.oid = I.indrelid
          LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index f793ac151..b57643379 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -373,8 +373,10 @@ pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
  * count a tuple update
  */
 void
-pgstat_count_heap_update(Relation rel, bool hot)
+pgstat_count_heap_update(Relation rel, bool hot, bool newpage)
 {
+	Assert(!(hot && newpage));
+
 	if (pgstat_should_count_relation(rel))
 	{
 		PgStat_TableStatus *pgstat_info = rel->pgstat_info;
@@ -382,9 +384,14 @@ pgstat_count_heap_update(Relation rel, bool hot)
 		ensure_tabstat_xact_level(pgstat_info);
 		pgstat_info->trans->tuples_updated++;
 
-		/* t_tuples_hot_updated is nontransactional, so just advance it */
+		/*
+		 * t_tuples_hot_updated and t_tuples_newpage_updated counters are
+		 * nontransactional, so just advance them
+		 */
 		if (hot)
 			pgstat_info->t_counts.t_tuples_hot_updated++;
+		else if (newpage)
+			pgstat_info->t_counts.t_tuples_newpage_updated++;
 	}
 }
 
@@ -804,6 +811,7 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 	tabentry->tuples_updated += lstats->t_counts.t_tuples_updated;
 	tabentry->tuples_deleted += lstats->t_counts.t_tuples_deleted;
 	tabentry->tuples_hot_updated += lstats->t_counts.t_tuples_hot_updated;
+	tabentry->tuples_newpage_updated += lstats->t_counts.t_tuples_newpage_updated;
 
 	/*
 	 * If table was truncated/dropped, first reset the live/dead counters.
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 35c6d4655..56119737c 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -92,6 +92,9 @@ PG_STAT_GET_RELENTRY_INT64(tuples_fetched)
 /* pg_stat_get_tuples_hot_updated */
 PG_STAT_GET_RELENTRY_INT64(tuples_hot_updated)
 
+/* pg_stat_get_tuples_newpage_updated */
+PG_STAT_GET_RELENTRY_INT64(tuples_newpage_updated)
+
 /* pg_stat_get_tuples_inserted */
 PG_STAT_GET_RELENTRY_INT64(tuples_inserted)
 
@@ -1618,6 +1621,21 @@ pg_stat_get_xact_tuples_hot_updated(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_xact_tuples_newpage_updated(PG_FUNCTION_ARGS)
+{
+	Oid			relid = PG_GETARG_OID(0);
+	int64		result;
+	PgStat_TableStatus *tabentry;
+
+	if ((tabentry = find_tabstat_entry(relid)) == NULL)
+		result = 0;
+	else
+		result = (int64) (tabentry->t_counts.t_tuples_newpage_updated);
+
+	PG_RETURN_INT64(result);
+}
+
 Datum
 pg_stat_get_xact_blocks_fetched(PG_FUNCTION_ARGS)
 {
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 2ae24127a..329c68ff8 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -4789,7 +4789,7 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        <structfield>n_tup_ins</structfield> <type>bigint</type>
       </para>
       <para>
-       Number of rows inserted
+       Total number of rows inserted
       </para></entry>
      </row>
 
@@ -4798,7 +4798,9 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        <structfield>n_tup_upd</structfield> <type>bigint</type>
       </para>
       <para>
-       Number of rows updated (includes <link linkend="storage-hot">HOT updated rows</link>)
+       Total number of rows updated (this includes row updates also
+       counted by <structfield>n_tup_hot_upd</structfield> and
+       <structfield>n_tup_newpage_upd</structfield>)
       </para></entry>
      </row>
 
@@ -4807,7 +4809,7 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        <structfield>n_tup_del</structfield> <type>bigint</type>
       </para>
       <para>
-       Number of rows deleted
+       Total number of rows deleted
       </para></entry>
      </row>
 
@@ -4816,8 +4818,21 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        <structfield>n_tup_hot_upd</structfield> <type>bigint</type>
       </para>
       <para>
-       Number of rows HOT updated (i.e., with no separate index
-       update required)
+       Number of rows <link linkend="storage-hot">HOT updated</link>
+       (i.e., successor version fits on same page as original, no
+       successor versions required in indexes)
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>n_tup_newpage_upd</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of rows updated where the successor version goes onto a
+       new page, leaving behind an original version whose
+       <link linkend="storage-tuple-layout"><structfield>t_ctid</structfield>
+       field</link> links to a different page
       </para></entry>
      </row>
 
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index e953d1f51..996d22b7d 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1789,6 +1789,7 @@ pg_stat_all_tables| SELECT c.oid AS relid,
     pg_stat_get_tuples_updated(c.oid) AS n_tup_upd,
     pg_stat_get_tuples_deleted(c.oid) AS n_tup_del,
     pg_stat_get_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
+    pg_stat_get_tuples_newpage_updated(c.oid) AS n_tup_newpage_upd,
     pg_stat_get_live_tuples(c.oid) AS n_live_tup,
     pg_stat_get_dead_tuples(c.oid) AS n_dead_tup,
     pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
@@ -2146,6 +2147,7 @@ pg_stat_sys_tables| SELECT relid,
     n_tup_upd,
     n_tup_del,
     n_tup_hot_upd,
+    n_tup_newpage_upd,
     n_live_tup,
     n_dead_tup,
     n_mod_since_analyze,
@@ -2193,6 +2195,7 @@ pg_stat_user_tables| SELECT relid,
     n_tup_upd,
     n_tup_del,
     n_tup_hot_upd,
+    n_tup_newpage_upd,
     n_live_tup,
     n_dead_tup,
     n_mod_since_analyze,
@@ -2244,7 +2247,8 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid,
     pg_stat_get_xact_tuples_inserted(c.oid) AS n_tup_ins,
     pg_stat_get_xact_tuples_updated(c.oid) AS n_tup_upd,
     pg_stat_get_xact_tuples_deleted(c.oid) AS n_tup_del,
-    pg_stat_get_xact_tuples_hot_updated(c.oid) AS n_tup_hot_upd
+    pg_stat_get_xact_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
+    pg_stat_get_xact_tuples_newpage_updated(c.oid) AS n_tup_newpage_upd
    FROM ((pg_class c
      LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))
      LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
@@ -2260,7 +2264,8 @@ pg_stat_xact_sys_tables| SELECT relid,
     n_tup_ins,
     n_tup_upd,
     n_tup_del,
-    n_tup_hot_upd
+    n_tup_hot_upd,
+    n_tup_newpage_upd
    FROM pg_stat_xact_all_tables
   WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text));
 pg_stat_xact_user_functions| SELECT p.oid AS funcid,
@@ -2282,7 +2287,8 @@ pg_stat_xact_user_tables| SELECT relid,
     n_tup_ins,
     n_tup_upd,
     n_tup_del,
-    n_tup_hot_upd
+    n_tup_hot_upd,
+    n_tup_newpage_upd
    FROM pg_stat_xact_all_tables
   WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text));
 pg_statio_all_indexes| SELECT c.oid AS relid,
-- 
2.39.2

#10Michael Paquier
michael@paquier.xyz
In reply to: Peter Geoghegan (#9)
Re: Add n_tup_newpage_upd to pg_stat table views

On Wed, Mar 22, 2023 at 05:14:08PM -0700, Peter Geoghegan wrote:

* Small adjustments to the documentation.

Nearby related items were tweaked slightly to make everything fit
together a bit better. For example, the description of n_tup_hot_upd
is revised to make it obvious that n_tup_hot_upd counts row updates
that can never get counted under the new n_tup_newpage_upd counter.

@@ -168,6 +168,7 @@ typedef struct PgStat_TableCounts
        PgStat_Counter t_tuples_updated;
        PgStat_Counter t_tuples_deleted;
        PgStat_Counter t_tuples_hot_updated;
+       PgStat_Counter t_tuples_newpage_updated;
        bool            t_truncdropped;

I have in the works something that's going to rename these fields to
not have the "t_" prefix anymore, to ease some global refactoring in
pgstatfuncs.c so as we have less repetitive code with the functions
that grab these counters. I don't think that's something you need to
name without the prefix here, just a FYI that this is going to be
immediately renamed ;)
--
Michael

#11Corey Huinker
corey.huinker@gmail.com
In reply to: Peter Geoghegan (#9)
Re: Add n_tup_newpage_upd to pg_stat table views

* No more dedicated struct to carry around the type of an update.

We just use two boolean arguments to the pgstats function instead. The
struct didn't seem to be adding much, and it was distracting to track
the information this way within heap_update().

That's probably a good move, especially if we start tallying updates that
use TOAST.

In reply to: Corey Huinker (#11)
Re: Add n_tup_newpage_upd to pg_stat table views

On Wed, Mar 22, 2023 at 10:38 PM Corey Huinker <corey.huinker@gmail.com> wrote:

That's probably a good move, especially if we start tallying updates that use TOAST.

Okay, pushed.

Thanks
--
Peter Geoghegan