On 22/01/2023 18:23, Justin Pryzby wrote:
pg_stat_progress_analyze was added in v13 (a166d408e).

For tables with inheritance children, do_analyze_rel() and
acquire_sample_rows() are called twice.  The first time through,
pgstat_progress_start_command() has memset() the progress array to zero.

But the 2nd time, ANALYZE_BLOCKS_DONE is already set from the previous
call, and BLOCKS_TOTAL can be set to some lower value (and in any case a
value unrelated to the pre-existing value of BLOCKS_DONE).  So the
progress report briefly shows a bogus combination of values and, with
these assertions, fails regression tests in master and v13, unless
BLOCKS_DONE is first zeroed.

Good catch!

I think the counts need do be reset even earlier, in acquire_inherited_sample_rows(), at the same time that we update PROGRESS_ANALYZE_CURRENT_CHILD_TABLE_RELID. See attached patch. Otherwise, there's a brief moment where we have already updated the child table ID, but the PROGRESS_ANALYZE_BLOCKS_TOTAL PROGRESS_ANALYZE_BLOCKS_DONE still show the counts from the previous child table. And if it's a foreign table, the FDW's sampling function might not update the progress report at all, in which case the old values will be displayed until the table is fully processed.

I appreciate the assertions you added, that made it easy to reproduce the problem. I'm inclined to not commit that though. It seems like a modularity violation for the code in backend_progress.c to have such intimate knowledge of what the different counters mean.

--
Heikki Linnakangas
Neon (https://neon.tech)
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index bfd981aa3f..206d1689ef 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1531,8 +1531,25 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
 		AcquireSampleRowsFunc acquirefunc = acquirefuncs[i];
 		double		childblocks = relblocks[i];
 
-		pgstat_progress_update_param(PROGRESS_ANALYZE_CURRENT_CHILD_TABLE_RELID,
-									 RelationGetRelid(childrel));
+		/*
+		 * Report progress.  The sampling function will normally report blocks
+		 * done/total, but we need to reset them to 0 here, so that they don't
+		 * show an old value until that.
+		 */
+		{
+			const int	progress_index[] = {
+				PROGRESS_ANALYZE_CURRENT_CHILD_TABLE_RELID,
+				PROGRESS_ANALYZE_BLOCKS_DONE,
+				PROGRESS_ANALYZE_BLOCKS_TOTAL
+			};
+			const int64 progress_vals[] = {
+				RelationGetRelid(childrel),
+				0,
+				0,
+			};
+
+			pgstat_progress_update_multi_param(3, progress_index, progress_vals);
+		}
 
 		if (childblocks > 0)
 		{

Reply via email to