From b9bfc805aa17e07ab7a92b6478daa9ff09b2b8e6 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Wed, 1 Feb 2023 10:23:53 -0600
Subject: [PATCH 2/3] assertions for progress reporting

---
 src/backend/commands/analyze.c                | 10 ++-
 src/backend/utils/activity/backend_progress.c | 84 +++++++++++++++++++
 2 files changed, 92 insertions(+), 2 deletions(-)

diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 65750958bb2..3bfc941aa2c 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1145,6 +1145,12 @@ acquire_sample_rows(Relation onerel, int elevel,
 	TableScanDesc scan;
 	BlockNumber nblocks;
 	BlockNumber blksdone = 0;
+	int64		progress_vals[2] = {0};
+	int const	progress_inds[2] = {
+		PROGRESS_ANALYZE_BLOCKS_DONE,
+		PROGRESS_ANALYZE_BLOCKS_TOTAL
+	};
+
 #ifdef USE_PREFETCH
 	int			prefetch_maximum = 0;	/* blocks to prefetch if enabled */
 	BlockSamplerData prefetch_bs;
@@ -1169,8 +1175,8 @@ acquire_sample_rows(Relation onerel, int elevel,
 #endif
 
 	/* Report sampling block numbers */
-	pgstat_progress_update_param(PROGRESS_ANALYZE_BLOCKS_TOTAL,
-								 nblocks);
+	progress_vals[1] = nblocks;
+	pgstat_progress_update_multi_param(2, progress_inds, progress_vals);
 
 	/* Prepare for sampling rows */
 	reservoir_init_selection_state(&rstate, targrows);
diff --git a/src/backend/utils/activity/backend_progress.c b/src/backend/utils/activity/backend_progress.c
index 2a9994b98fd..63f9482b175 100644
--- a/src/backend/utils/activity/backend_progress.c
+++ b/src/backend/utils/activity/backend_progress.c
@@ -10,6 +10,7 @@
  */
 #include "postgres.h"
 
+#include "commands/progress.h"
 #include "port/atomics.h"		/* for memory barriers */
 #include "utils/backend_progress.h"
 #include "utils/backend_status.h"
@@ -37,6 +38,85 @@ pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
 	PGSTAT_END_WRITE_ACTIVITY(beentry);
 }
 
+/*
+ * Check for consistency of progress data (current < total).
+ *
+ * Check during pgstat_progress_updates_*() rather than only from
+ * pgstat_progress_end_command() to catch issues with uninitialized/stale data
+ * from previous progress commands.
+ *
+ * If a command fails due to interrupt or error, the values may be less than
+ * the expected final value.
+ */
+static void
+pgstat_progress_asserts(void)
+{
+	volatile PgBackendStatus *beentry = MyBEEntry;
+	volatile int64 *a = beentry->st_progress_param;
+
+	switch (beentry->st_progress_command)
+	{
+		case PROGRESS_COMMAND_VACUUM:
+			Assert(a[PROGRESS_VACUUM_HEAP_BLKS_SCANNED] <=
+				   a[PROGRESS_VACUUM_TOTAL_HEAP_BLKS]);
+			Assert(a[PROGRESS_VACUUM_HEAP_BLKS_VACUUMED] <=
+				   a[PROGRESS_VACUUM_TOTAL_HEAP_BLKS]);
+			Assert(a[PROGRESS_VACUUM_NUM_DEAD_TUPLES] <=
+				   a[PROGRESS_VACUUM_MAX_DEAD_TUPLES]);
+			break;
+
+		case PROGRESS_COMMAND_ANALYZE:
+			Assert(a[PROGRESS_ANALYZE_BLOCKS_DONE] <=
+				   a[PROGRESS_ANALYZE_BLOCKS_TOTAL]);
+			Assert(a[PROGRESS_ANALYZE_EXT_STATS_COMPUTED] <=
+				   a[PROGRESS_ANALYZE_EXT_STATS_TOTAL]);
+			Assert(a[PROGRESS_ANALYZE_CHILD_TABLES_DONE] <=
+				   a[PROGRESS_ANALYZE_CHILD_TABLES_TOTAL]);
+			break;
+
+		case PROGRESS_COMMAND_CLUSTER:
+			Assert(a[PROGRESS_CLUSTER_HEAP_BLKS_SCANNED] <=
+				   a[PROGRESS_CLUSTER_TOTAL_HEAP_BLKS]);
+			/* FALLTHROUGH */
+			/* ..because CLUSTER rebuilds indexes */
+
+		case PROGRESS_COMMAND_CREATE_INDEX:
+			Assert(a[PROGRESS_CREATEIDX_TUPLES_DONE] <=
+				   a[PROGRESS_CREATEIDX_TUPLES_TOTAL]);
+			Assert(a[PROGRESS_CREATEIDX_PARTITIONS_DONE] <=
+				   a[PROGRESS_CREATEIDX_PARTITIONS_TOTAL]);
+			break;
+
+		case PROGRESS_COMMAND_BASEBACKUP:
+			/* progress reporting is optional for these */
+			if (a[PROGRESS_BASEBACKUP_BACKUP_TOTAL] >= 0)
+			{
+				Assert(a[PROGRESS_BASEBACKUP_BACKUP_STREAMED] <=
+					   a[PROGRESS_BASEBACKUP_BACKUP_TOTAL]);
+				Assert(a[PROGRESS_BASEBACKUP_TBLSPC_STREAMED] <=
+					   a[PROGRESS_BASEBACKUP_TBLSPC_TOTAL]);
+			}
+			break;
+
+		case PROGRESS_COMMAND_COPY:
+#if 0
+			//This currently fails file_fdw tests, since pgstat_prorgress evidently fails
+				// to support simultaneous copy commands, as happens during JOIN.
+			/* bytes progress is not available in all cases */
+				if (a[PROGRESS_COPY_BYTES_TOTAL] > 0)
+				//Assert(a[PROGRESS_COPY_BYTES_PROCESSED] <= a[PROGRESS_COPY_BYTES_TOTAL]);
+			if (a[PROGRESS_COPY_BYTES_PROCESSED] > a[PROGRESS_COPY_BYTES_TOTAL])
+				elog(WARNING, "PROGRESS_COPY_BYTES_PROCESSED %ld %ld",
+					 a[PROGRESS_COPY_BYTES_PROCESSED],
+					 a[PROGRESS_COPY_BYTES_TOTAL]);
+#endif
+			break;
+
+		case PROGRESS_COMMAND_INVALID:
+			break;				/* Do nothing */
+	}
+}
+
 /*-----------
  * pgstat_progress_update_param() -
  *
@@ -56,6 +136,8 @@ pgstat_progress_update_param(int index, int64 val)
 	PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
 	beentry->st_progress_param[index] = val;
 	PGSTAT_END_WRITE_ACTIVITY(beentry);
+
+	pgstat_progress_asserts();
 }
 
 /*-----------
@@ -113,6 +195,8 @@ pgstat_progress_update_multi_param(int nparam, const int *index,
 	}
 
 	PGSTAT_END_WRITE_ACTIVITY(beentry);
+
+	pgstat_progress_asserts();
 }
 
 /*-----------
-- 
2.34.1

