ANALYZE command progress checker

Started by vinayakalmost 9 years ago47 messages
#1vinayak
Pokale_Vinayak_q3@lab.ntt.co.jp
1 attachment(s)

Hello Hackers,

Following is a proposal for reporting the progress of ANALYZE command:

It seems that the following could be the phases of ANALYZE processing:
1. Collecting sample rows
2. Collecting inherited sample rows
3. Computing heap stats
4. Computing index stats
5. Cleaning up indexes

The first phase is easy if there is no inheritance but in case of
inheritance we need to sample the blocks from multiple heaps.
Here the progress is counted against total number of blocks processed.

The view provides the information of analyze command progress details as
follows
postgres=# \d pg_stat_progress_analyze
View "pg_catalog.pg_stat_progress_analyze"
Column | Type | Collation | Nullable | Default
-------------------+---------+-----------+----------+---------
pid | integer | | |
datid | oid | | |
datname | name | | |
relid | oid | | |
phase | text | | |
heap_blks_total | bigint | | |
heap_blks_scanned | bigint | | |
total_sample_rows | bigint | | |

I feel this view information may be useful in checking the progress of
long running ANALYZE command.

The attached patch reports the different phases of analyze command.
Added this patch to CF 2017-03.

Opinions?

Note: Collecting inherited sample rows phase is not reported yet in the
patch.

Regards,
Vinayak Pokale
NTT Open Source Software Center

Attachments:

pg_stat_progress_analyze_v1.patchbinary/octet-stream; name=pg_stat_progress_analyze_v1.patchDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fad5cb0..85b2935 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -521,6 +521,13 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       <command>VACUUM</>, showing current progress.
       See <xref linkend='vacuum-progress-reporting'>.</entry>
      </row>
+
+     <row>
+      <entry><structname>pg_stat_progress_analyze</><indexterm><primary>pg_stat_progress_analyze</primary></indexterm></entry>
+      <entry>One row for each backend running
+      <command>ANALYZE</>, showing current progress.
+      See <xref linkend='analyze-progress-reporting'>.</entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
@@ -3008,7 +3015,135 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
    </tbody>
    </tgroup>
   </table>
+ </sect2>
+
+ <sect2 id="analyze-progress-reporting">
+  <title>ANALYZE Progress Reporting</title>
+
+  <para>
+   Whenever <command>ANALYZE</> is running, the
+   <structname>pg_stat_progress_analyze</structname> view will contain
+   one row for each backend that is currently analyzing.  
+   The tables below describe the information that will be reported and 
+   provide information about how to interpret it.
+  </para>
+
+  <table id="pg-stat-progress-analyze-view" xreflabel="pg_stat_progress_analyze">
+   <title><structname>pg_stat_progress_analyze</structname> View</title>
+   <tgroup cols="3">
+    <thead>
+    <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
 
+   <tbody>
+    <row>
+     <entry><structfield>pid</></entry>
+     <entry><type>integer</></entry>
+     <entry>Process ID of backend.</entry>
+    </row>
+    <row>
+     <entry><structfield>datid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>datname</></entry>
+     <entry><type>name</></entry>
+     <entry>Name of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>relid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the table being vacuumed.</entry>
+    </row>
+    <row>
+     <entry><structfield>phase</></entry>
+     <entry><type>text</></entry>
+     <entry>
+       Current processing phase of analyze.  See <xref linkend='analyze-phases'>.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>heap_blks_total</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of heap blocks in the table.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>heap_blks_scanned</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Number of heap blocks scanned.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>total_sample_rows</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of sample rows.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
+
+  <table id="analyze-phases">
+   <title>ANALYZE phases</title>
+   <tgroup cols="2">
+    <thead>
+    <row>
+      <entry>Phase</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><literal>initializing</literal></entry>
+     <entry>
+       <command>ANALYZE</> is preparing to collect sample rows.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>collecting sample rows</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently collecting the sample rows. 
+       The sample it reads is taken randomly.Its size depends on
+       the default_statistics_target parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>collecting inherited sample rows</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently collecting inherited sample rows.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>computing heap stats</literal></entry>
+     <entry>
+       <command>VACUUM</> is currently computing heap stats.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>computing index stats</literal></entry>
+     <entry>
+       <command>VACUUM</> is currently computing index stats.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>cleaning up indexes</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently cleaning up indexes.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
  </sect2>
  </sect1>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 38be9cf..909feb6 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -872,6 +872,22 @@ CREATE VIEW pg_stat_progress_vacuum AS
     FROM pg_stat_get_progress_info('VACUUM') AS S
 		LEFT JOIN pg_database D ON S.datid = D.oid;
 
+CREATE VIEW pg_stat_progress_analyze AS
+	SELECT
+		S.pid AS pid, S.datid AS datid, D.datname AS datname,
+		S.relid AS relid,
+		CASE S.param1 WHEN 0 THEN 'initializing'
+					  WHEN 1 THEN 'collecting sample rows'
+					  WHEN 2 THEN 'collecting inherited sample rows'
+					  WHEN 3 THEN 'computing heap stats'
+					  WHEN 4 THEN 'computing index stats'
+					  WHEN 5 THEN 'cleaning up indexes'
+					  END AS phase,
+		S.param2 AS heap_blks_total, S.param3 AS heap_blks_scanned,
+		S.param4 AS total_sample_rows
+    FROM pg_stat_get_progress_info('ANALYZE') AS S
+		LEFT JOIN pg_database D ON S.datid = D.oid;
+
 CREATE VIEW pg_user_mappings AS
     SELECT
         U.oid       AS umid,
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index ed3acb1..e8a3388 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -31,6 +31,7 @@
 #include "commands/dbcommands.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "commands/progress.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -204,6 +205,8 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
 		onerel->rd_rel->relkind == RELKIND_MATVIEW ||
 		onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 	{
+		pgstat_progress_start_command(PROGRESS_COMMAND_ANALYZE,
+												RelationGetRelid(onerel));
 		/* Regular table, so we'll use the regular row acquisition function */
 		acquirefunc = acquire_sample_rows;
 		/* Also get regular table's size */
@@ -489,7 +492,6 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 		numrows = (*acquirefunc) (onerel, elevel,
 								  rows, targrows,
 								  &totalrows, &totaldeadrows);
-
 	/*
 	 * Compute the statistics.  Temporary results during the calculations for
 	 * each column are stored in a child context.  The calc routines are
@@ -518,6 +520,10 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 									 numrows,
 									 totalrows);
 
+			/* Report compute heap stats phase */
+			pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+										PROGRESS_ANALYZE_PHASE_COMPUTE_HEAP_STATS);
+
 			/*
 			 * If the appropriate flavor of the n_distinct option is
 			 * specified, override with the corresponding value.
@@ -541,6 +547,10 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 								rows, numrows,
 								col_context);
 
+		/* Report compute index stats phase */
+		pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+									PROGRESS_ANALYZE_PHASE_COMPUTE_INDEX_STATS);
+
 		MemoryContextSwitchTo(old_context);
 		MemoryContextDelete(col_context);
 
@@ -637,6 +647,9 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 				pfree(stats);
 		}
 	}
+	/* Report that we are now doing index cleanup */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+								PROGRESS_ANALYZE_PHASE_INDEX_CLEANUP);
 
 	/* Done with indexes */
 	vac_close_indexes(nindexes, Irel, NoLock);
@@ -665,6 +678,8 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	MemoryContextSwitchTo(caller_context);
 	MemoryContextDelete(anl_context);
 	anl_context = NULL;
+
+	pgstat_progress_end_command();
 }
 
 /*
@@ -988,6 +1003,12 @@ acquire_sample_rows(Relation onerel, int elevel,
 	BlockSamplerData bs;
 	ReservoirStateData rstate;
 
+	const int   initprog_index[] = {
+	PROGRESS_ANALYZE_PHASE,
+	PROGRESS_ANALYZE_TOTAL_HEAP_BLKS,
+	};
+	int64       initprog_val[2];
+
 	Assert(targrows > 0);
 
 	totalblocks = RelationGetNumberOfBlocks(onerel);
@@ -1000,6 +1021,11 @@ acquire_sample_rows(Relation onerel, int elevel,
 	/* Prepare for sampling rows */
 	reservoir_init_selection_state(&rstate, targrows);
 
+	/* Report total number of heap blocks and collectinf sample row phase*/
+	initprog_val[0] = PROGRESS_ANALYZE_PHASE_COLLECT_HEAP_SAMPLE_ROWS;
+	initprog_val[1] = totalblocks;
+	pgstat_progress_update_multi_param(2, initprog_index, initprog_val);
+
 	/* Outer loop over blocks to sample */
 	while (BlockSampler_HasMore(&bs))
 	{
@@ -1009,6 +1035,9 @@ acquire_sample_rows(Relation onerel, int elevel,
 		OffsetNumber targoffset,
 					maxoffset;
 
+		/* Report number of heap blocks scaned so far*/
+		pgstat_progress_update_param(PROGRESS_ANALYZE_HEAP_BLKS_SCANNED, targblock);
+
 		vacuum_delay_point();
 
 		/*
@@ -1210,6 +1239,9 @@ acquire_sample_rows(Relation onerel, int elevel,
 					liverows, deadrows,
 					numrows, *totalrows)));
 
+	/* Report total number of sample rows*/
+	pgstat_progress_update_param(PROGRESS_ANALYZE_TOTAL_SAMPLE_ROWS, numrows);
+
 	return numrows;
 }
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index a987d0d..eb1dd63 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -465,6 +465,8 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 	/* Translate command name into command type code. */
 	if (pg_strcasecmp(cmd, "VACUUM") == 0)
 		cmdtype = PROGRESS_COMMAND_VACUUM;
+	else if (pg_strcasecmp(cmd, "ANALYZE") == 0)
+		cmdtype = PROGRESS_COMMAND_ANALYZE;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h
index 9472ecc..e8dae70 100644
--- a/src/include/commands/progress.h
+++ b/src/include/commands/progress.h
@@ -34,4 +34,17 @@
 #define PROGRESS_VACUUM_PHASE_TRUNCATE			5
 #define PROGRESS_VACUUM_PHASE_FINAL_CLEANUP		6
 
+/* Progress parameters for analyze */
+#define PROGRESS_ANALYZE_PHASE					0
+#define PROGRESS_ANALYZE_TOTAL_HEAP_BLKS		1
+#define PROGRESS_ANALYZE_HEAP_BLKS_SCANNED		2
+#define PROGRESS_ANALYZE_TOTAL_SAMPLE_ROWS		3
+
+/* Phases of analyze */
+#define PROGRESS_ANALYZE_PHASE_COLLECT_HEAP_SAMPLE_ROWS		1
+#define PROGRESS_ANALYZE_PHASE_COLLECT_INH_SAMPLE_ROWS		2
+#define PROGRESS_ANALYZE_PHASE_COMPUTE_HEAP_STATS			3
+#define PROGRESS_ANALYZE_PHASE_COMPUTE_INDEX_STATS			4
+#define PROGRESS_ANALYZE_PHASE_INDEX_CLEANUP				5
+
 #endif
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8b710ec..ccb7b08 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -811,7 +811,8 @@ typedef enum
 typedef enum ProgressCommandType
 {
 	PROGRESS_COMMAND_INVALID,
-	PROGRESS_COMMAND_VACUUM
+	PROGRESS_COMMAND_VACUUM,
+	PROGRESS_COMMAND_ANALYZE
 } ProgressCommandType;
 
 #define PGSTAT_NUM_PROGRESS_PARAM	10
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index c661f1d..a01c464 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1817,6 +1817,24 @@ pg_stat_progress_vacuum| SELECT s.pid,
     s.param7 AS num_dead_tuples
    FROM (pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)
      LEFT JOIN pg_database d ON ((s.datid = d.oid)));
+pg_stat_progress_analyze| SELECT s.pid,
+    s.datid,
+    d.datname,
+    s.relid,
+        CASE s.param1
+            WHEN 0 THEN 'initializing'::text
+            WHEN 1 THEN 'collecting sample rows'::text
+            WHEN 2 THEN 'collecting inherited sample rows'::text
+            WHEN 3 THEN 'computing heap stats'::text
+            WHEN 4 THEN 'computing index stats'::text
+            WHEN 5 THEN 'cleaning up indexes'::text
+            ELSE NULL::text
+        END AS phase,
+    s.param2 AS heap_blks_total,
+    s.param3 AS heap_blks_scanned,
+    s.param4 AS total_sample_rows,
+   FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)
+     LEFT JOIN pg_database d ON ((s.datid = d.oid)));
 pg_stat_replication| SELECT s.pid,
     s.usesysid,
     u.rolname AS usename,
#2Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: vinayak (#1)
Re: ANALYZE command progress checker

On 2/28/17 04:24, vinayak wrote:

The view provides the information of analyze command progress details as
follows
postgres=# \d pg_stat_progress_analyze
View "pg_catalog.pg_stat_progress_analyze"

Hmm, do we want a separate "progress" system view for every kind of
command? What if someone comes up with a progress checker for CREATE
INDEX, REINDEX, CLUSTER, etc.?

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3David Fetter
david@fetter.org
In reply to: Peter Eisentraut (#2)
Re: ANALYZE command progress checker

On Wed, Mar 01, 2017 at 09:45:40AM -0500, Peter Eisentraut wrote:

On 2/28/17 04:24, vinayak wrote:

The view provides the information of analyze command progress details as
follows
postgres=# \d pg_stat_progress_analyze
View "pg_catalog.pg_stat_progress_analyze"

Hmm, do we want a separate "progress" system view for every kind of
command? What if someone comes up with a progress checker for CREATE
INDEX, REINDEX, CLUSTER, etc.?

Some kind of design for progress seems like a good plan. Some ideas:

- System view(s)

This has the advantage of being shown to work at least to a PoC by
this patch, and is similar to extant system views like
pg_stat_activity in the sense of capturing a moment in time.

- NOTIFY

Event-driven model as opposed to a polling one. This is
attractive on efficiency grounds, less so on reliability ones.

- Something added to the wire protocol

More specialized, limits the information to the session where the
command was issued

- Other things not named here

Best,
David.
--
David Fetter <david(at)fetter(dot)org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david(dot)fetter(at)gmail(dot)com

Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Andres Freund
andres@anarazel.de
In reply to: David Fetter (#3)
Re: ANALYZE command progress checker

On 2017-03-01 10:20:41 -0800, David Fetter wrote:

On Wed, Mar 01, 2017 at 09:45:40AM -0500, Peter Eisentraut wrote:

On 2/28/17 04:24, vinayak wrote:

The view provides the information of analyze command progress details as
follows
postgres=# \d pg_stat_progress_analyze
View "pg_catalog.pg_stat_progress_analyze"

Hmm, do we want a separate "progress" system view for every kind of
command? What if someone comes up with a progress checker for CREATE
INDEX, REINDEX, CLUSTER, etc.?

I don't think that'd be that bad, otherwise the naming of the fields is
complicated. I guess the alternative (or do both?) would be to to have
a pivoted table, but that'd painful to query. Do you have a better idea?

Some kind of design for progress seems like a good plan. Some ideas:

- System view(s)

This has the advantage of being shown to work at least to a PoC by
this patch, and is similar to extant system views like
pg_stat_activity in the sense of capturing a moment in time.

- NOTIFY

Event-driven model as opposed to a polling one. This is
attractive on efficiency grounds, less so on reliability ones.

- Something added to the wire protocol

More specialized, limits the information to the session where the
command was issued

- Other things not named here

We now have a framework for this [1]https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=c16dc1aca5e (currently used by vacuum, but
extensible). The question is about presentation. I'm fairly sure that
we shouldn't just add yet another framework, and I doubt that that's
what's proposed by Peter.

[1]: https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=c16dc1aca5e

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Andres Freund
andres@anarazel.de
In reply to: Andres Freund (#4)
Re: ANALYZE command progress checker

On 2017-03-01 10:25:49 -0800, Andres Freund wrote:

We now have a framework for this [1] (currently used by vacuum, but
extensible). The question is about presentation. I'm fairly sure that
we shouldn't just add yet another framework, and I doubt that that's
what's proposed by Peter.

[1]
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=c16dc1aca5e

Majority of that is actually in
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=b6fb6471f6afaf649e52f38269fd8c5c60647669

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6David Fetter
david@fetter.org
In reply to: Andres Freund (#5)
Re: ANALYZE command progress checker

On Wed, Mar 01, 2017 at 10:28:23AM -0800, Andres Freund wrote:

On 2017-03-01 10:25:49 -0800, Andres Freund wrote:

We now have a framework for this [1] (currently used by vacuum, but
extensible). The question is about presentation. I'm fairly sure that
we shouldn't just add yet another framework, and I doubt that that's
what's proposed by Peter.

[1]
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=c16dc1aca5e

Majority of that is actually in
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=b6fb6471f6afaf649e52f38269fd8c5c60647669

If that's even vaguely usable, I'd say we should use it for this.

I notice that that commit has no SGML component. Should it have one?

Best,
David.
--
David Fetter <david(at)fetter(dot)org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david(dot)fetter(at)gmail(dot)com

Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Andres Freund
andres@anarazel.de
In reply to: David Fetter (#6)
Re: ANALYZE command progress checker

On March 1, 2017 11:34:48 AM PST, David Fetter <david@fetter.org> wrote:

I notice that that commit has no SGML component. Should it have one?

Don't think so.

--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8David Steele
david@pgmasters.net
In reply to: Andres Freund (#4)
Re: ANALYZE command progress checker

On 3/1/17 1:25 PM, Andres Freund wrote:

On 2017-03-01 10:20:41 -0800, David Fetter wrote:

On Wed, Mar 01, 2017 at 09:45:40AM -0500, Peter Eisentraut wrote:

On 2/28/17 04:24, vinayak wrote:

The view provides the information of analyze command progress details as
follows
postgres=# \d pg_stat_progress_analyze
View "pg_catalog.pg_stat_progress_analyze"

Hmm, do we want a separate "progress" system view for every kind of
command? What if someone comes up with a progress checker for CREATE
INDEX, REINDEX, CLUSTER, etc.?

I don't think that'd be that bad, otherwise the naming of the fields is
complicated. I guess the alternative (or do both?) would be to to have
a pivoted table, but that'd painful to query. Do you have a better idea?

Some kind of design for progress seems like a good plan. Some ideas:

- System view(s)

This has the advantage of being shown to work at least to a PoC by
this patch, and is similar to extant system views like
pg_stat_activity in the sense of capturing a moment in time.

- NOTIFY

Event-driven model as opposed to a polling one. This is
attractive on efficiency grounds, less so on reliability ones.

- Something added to the wire protocol

More specialized, limits the information to the session where the
command was issued

- Other things not named here

We now have a framework for this [1] (currently used by vacuum, but
extensible). The question is about presentation. I'm fairly sure that
we shouldn't just add yet another framework, and I doubt that that's
what's proposed by Peter.

I think the idea of a general progress view is very valuable and there
are a ton of operations it could be used for: full table scans, index
rebuilds, vacuum, copy, etc.

However, I feel that this proposal is not flexible enough and comes too
late in the release cycle to allow development into something that could
be committed.

I propose we move this to the 2017-07 CF so the idea can be more fully
developed.

--
-David
david@pgmasters.net

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Michael Paquier
michael.paquier@gmail.com
In reply to: David Steele (#8)
Re: ANALYZE command progress checker

On Sat, Mar 4, 2017 at 5:33 AM, David Steele <david@pgmasters.net> wrote:

I think the idea of a general progress view is very valuable and there
are a ton of operations it could be used for: full table scans, index
rebuilds, vacuum, copy, etc.

However, I feel that this proposal is not flexible enough and comes too
late in the release cycle to allow development into something that could
be committed.

Well, each command really has its own requirements in terms of data to
store, so we either finish with a bunch of small tables that anyone
could query and join as they wish or a somewhat unique table that is
bloated with all the information, with a set of views on top of it to
query all the information. For extensibility's sake of each command
(for example imagine that REINDEX could be extended with a
CONCURRENTLY option and multiple phases), I would think that having a
table per command type would not be that bad.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Andres Freund
andres@anarazel.de
In reply to: David Steele (#8)
Re: ANALYZE command progress checker

On 2017-03-03 15:33:15 -0500, David Steele wrote:

On 3/1/17 1:25 PM, Andres Freund wrote:

On 2017-03-01 10:20:41 -0800, David Fetter wrote:

On Wed, Mar 01, 2017 at 09:45:40AM -0500, Peter Eisentraut wrote:

On 2/28/17 04:24, vinayak wrote:

The view provides the information of analyze command progress details as
follows
postgres=# \d pg_stat_progress_analyze
View "pg_catalog.pg_stat_progress_analyze"

Hmm, do we want a separate "progress" system view for every kind of
command? What if someone comes up with a progress checker for CREATE
INDEX, REINDEX, CLUSTER, etc.?

I don't think that'd be that bad, otherwise the naming of the fields is
complicated. I guess the alternative (or do both?) would be to to have
a pivoted table, but that'd painful to query. Do you have a better idea?

Some kind of design for progress seems like a good plan. Some ideas:

- System view(s)

This has the advantage of being shown to work at least to a PoC by
this patch, and is similar to extant system views like
pg_stat_activity in the sense of capturing a moment in time.

- NOTIFY

Event-driven model as opposed to a polling one. This is
attractive on efficiency grounds, less so on reliability ones.

- Something added to the wire protocol

More specialized, limits the information to the session where the
command was issued

- Other things not named here

We now have a framework for this [1] (currently used by vacuum, but
extensible). The question is about presentation. I'm fairly sure that
we shouldn't just add yet another framework, and I doubt that that's
what's proposed by Peter.

I think the idea of a general progress view is very valuable and there
are a ton of operations it could be used for: full table scans, index
rebuilds, vacuum, copy, etc.
However, I feel that this proposal is not flexible enough and comes too
late in the release cycle to allow development into something that could
be committed.

I'm not following. As I pointed out, we already have this framework?
This patch is just a short one using that framework?

I propose we move this to the 2017-07 CF so the idea can be more fully
developed.

I don't see that being warranted in this case, we're really not talking
about something complicated:
doc/src/sgml/monitoring.sgml | 135 +++++++++++++++++++++++++++++++++++
src/backend/catalog/system_views.sql | 16 ++++
src/backend/commands/analyze.c | 34 ++++++++
src/backend/utils/adt/pgstatfuncs.c | 2
src/include/commands/progress.h | 13 +++
src/include/pgstat.h | 3
src/test/regress/expected/rules.out | 18 ++++
7 files changed, 219 insertions(+), 2 deletions(-)
excepting tests and docs, this is very little actual code.

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: vinayak (#1)
Re: ANALYZE command progress checker

Hi Vinayak,

On 2017/02/28 18:24, vinayak wrote:

The attached patch reports the different phases of analyze command.
Added this patch to CF 2017-03.

In the updated monitoring.sgml:

+    <row>
+     <entry><literal>computing heap stats</literal></entry>
+     <entry>
+       <command>VACUUM</> is currently computing heap stats.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>computing index stats</literal></entry>
+     <entry>
+       <command>VACUUM</> is currently computing index stats.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>cleaning up indexes</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently cleaning up indexes.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>

The entries mentioning VACUUM should actually say ANALYZE.

Thanks,
Amit

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12vinayak
Pokale_Vinayak_q3@lab.ntt.co.jp
In reply to: Amit Langote (#11)
Re: ANALYZE command progress checker

On 2017/03/06 17:02, Amit Langote wrote:

Hi Vinayak,

On 2017/02/28 18:24, vinayak wrote:

The attached patch reports the different phases of analyze command.
Added this patch to CF 2017-03.

In the updated monitoring.sgml:

+    <row>
+     <entry><literal>computing heap stats</literal></entry>
+     <entry>
+       <command>VACUUM</> is currently computing heap stats.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>computing index stats</literal></entry>
+     <entry>
+       <command>VACUUM</> is currently computing index stats.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>cleaning up indexes</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently cleaning up indexes.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>

The entries mentioning VACUUM should actually say ANALYZE.

Yes. Thank you.
I will fix it.

Regards,
Vinayak Pokale
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13David Steele
david@pgmasters.net
In reply to: Andres Freund (#10)
Re: ANALYZE command progress checker

On 3/6/17 1:58 AM, Andres Freund wrote:

On 2017-03-03 15:33:15 -0500, David Steele wrote:

I propose we move this to the 2017-07 CF so the idea can be more fully
developed.

I don't see that being warranted in this case, we're really not talking
about something complicated:

<...>

excepting tests and docs, this is very little actual code.

Fair enough. From my read through it appeared a redesign was
required/requested.

--
-David
david@pgmasters.net

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Michael Paquier
michael.paquier@gmail.com
In reply to: Andres Freund (#10)
Re: ANALYZE command progress checker

On Mon, Mar 6, 2017 at 3:58 PM, Andres Freund <andres@anarazel.de> wrote:

On 2017-03-03 15:33:15 -0500, David Steele wrote:

On 3/1/17 1:25 PM, Andres Freund wrote:

On 2017-03-01 10:20:41 -0800, David Fetter wrote:

On Wed, Mar 01, 2017 at 09:45:40AM -0500, Peter Eisentraut wrote:

On 2/28/17 04:24, vinayak wrote:

The view provides the information of analyze command progress details as
follows
postgres=# \d pg_stat_progress_analyze
View "pg_catalog.pg_stat_progress_analyze"

Hmm, do we want a separate "progress" system view for every kind of
command? What if someone comes up with a progress checker for CREATE
INDEX, REINDEX, CLUSTER, etc.?

I don't think that'd be that bad, otherwise the naming of the fields is
complicated. I guess the alternative (or do both?) would be to to have
a pivoted table, but that'd painful to query. Do you have a better idea?

Some kind of design for progress seems like a good plan. Some ideas:

- System view(s)

This has the advantage of being shown to work at least to a PoC by
this patch, and is similar to extant system views like
pg_stat_activity in the sense of capturing a moment in time.

- NOTIFY

Event-driven model as opposed to a polling one. This is
attractive on efficiency grounds, less so on reliability ones.

- Something added to the wire protocol

More specialized, limits the information to the session where the
command was issued

- Other things not named here

We now have a framework for this [1] (currently used by vacuum, but
extensible). The question is about presentation. I'm fairly sure that
we shouldn't just add yet another framework, and I doubt that that's
what's proposed by Peter.

I think the idea of a general progress view is very valuable and there
are a ton of operations it could be used for: full table scans, index
rebuilds, vacuum, copy, etc.
However, I feel that this proposal is not flexible enough and comes too
late in the release cycle to allow development into something that could
be committed.

I'm not following. As I pointed out, we already have this framework?
This patch is just a short one using that framework?

I propose we move this to the 2017-07 CF so the idea can be more fully
developed.

I don't see that being warranted in this case, we're really not talking
about something complicated:
doc/src/sgml/monitoring.sgml | 135 +++++++++++++++++++++++++++++++++++
src/backend/catalog/system_views.sql | 16 ++++
src/backend/commands/analyze.c | 34 ++++++++
src/backend/utils/adt/pgstatfuncs.c | 2
src/include/commands/progress.h | 13 +++
src/include/pgstat.h | 3
src/test/regress/expected/rules.out | 18 ++++
7 files changed, 219 insertions(+), 2 deletions(-)
excepting tests and docs, this is very little actual code.

Or 35 lines just for the backend portion, it is hard to something smaller.

@@ -496,7 +499,6 @@ do_analyze_rel(Relation onerel, int options,
VacuumParams *params,
numrows = (*acquirefunc) (onerel, elevel,
rows, targrows,
&totalrows, &totaldeadrows);
-
/*
Useless diff.

+     <entry>
+       <command>ANALYZE</> is currently collecting the sample rows.
+       The sample it reads is taken randomly.Its size depends on
+       the default_statistics_target parameter value.
+     </entry>
This should use a <varname> markup for default_statistics_target.
@@ -203,6 +204,8 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
    if (onerel->rd_rel->relkind == RELKIND_RELATION ||
        onerel->rd_rel->relkind == RELKIND_MATVIEW)
    {
+       pgstat_progress_start_command(PROGRESS_COMMAND_ANALYZE,
+                                               RelationGetRelid(onerel));
It seems to me that the report should begin in do_analyze_rel().
-- 
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Michael Paquier (#14)
Re: ANALYZE command progress checker

On Tue, Mar 7, 2017 at 5:01 PM, Michael Paquier <michael.paquier@gmail.com>
wrote:

@@ -496,7 +499,6 @@ do_analyze_rel(Relation onerel, int options,
VacuumParams *params,
numrows = (*acquirefunc) (onerel, elevel,
rows, targrows,
&totalrows, &totaldeadrows);
-
/*
Useless diff.

+     <entry>
+       <command>ANALYZE</> is currently collecting the sample rows.
+       The sample it reads is taken randomly.Its size depends on
+       the default_statistics_target parameter value.
+     </entry>
This should use a <varname> markup for default_statistics_target.
@@ -203,6 +204,8 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
if (onerel->rd_rel->relkind == RELKIND_RELATION ||
onerel->rd_rel->relkind == RELKIND_MATVIEW)
{
+       pgstat_progress_start_command(PROGRESS_COMMAND_ANALYZE,
+                                               RelationGetRelid(onerel));
It seems to me that the report should begin in do_analyze_rel().

some more comments,

+ /* Report compute heap stats phase */
+ pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+ PROGRESS_ANALYZE_PHASE_COMPUTE_HEAP_STATS);

The above analyze phase is updated inside a for loop, instead just set it
above once.

+ /* Report compute index stats phase */
+ pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+ PROGRESS_ANALYZE_PHASE_COMPUTE_INDEX_STATS);

Irrespective of whether the index exists on the table or not, the above
analyze phase
is set. It is better to set the above phase and index cleanup phase only
when there
are indexes on the table.

+ /* Report total number of heap blocks and collectinf sample row phase*/

Typo? collecting?

+ /* Report total number of heap blocks and collectinf sample row phase*/
+ initprog_val[0] = PROGRESS_ANALYZE_PHASE_COLLECT_HEAP_SAMPLE_ROWS;
+ initprog_val[1] = totalblocks;
+ pgstat_progress_update_multi_param(2, initprog_index, initprog_val);

acquire_sample_rows function is called from acquire_inherited_sample_rows
function, so adding the phase in that function will provide wrong info.

+#define PROGRESS_ANALYZE_PHASE_COLLECT_INH_SAMPLE_ROWS 2

why there is no code added for the phase, any specific reason?

+/* Phases of analyze */

Can be written as following for better understanding, and also
similar like vacuum.

/* Phases of analyze (as advertised via PROGRESS_ANALYZE_PHASE) */

Regards,
Hari Babu
Fujitsu Australia

#16Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#4)
Re: ANALYZE command progress checker

On Wed, Mar 1, 2017 at 1:25 PM, Andres Freund <andres@anarazel.de> wrote:

On 2017-03-01 10:20:41 -0800, David Fetter wrote:

On Wed, Mar 01, 2017 at 09:45:40AM -0500, Peter Eisentraut wrote:

On 2/28/17 04:24, vinayak wrote:

The view provides the information of analyze command progress details as
follows
postgres=# \d pg_stat_progress_analyze
View "pg_catalog.pg_stat_progress_analyze"

Hmm, do we want a separate "progress" system view for every kind of
command? What if someone comes up with a progress checker for CREATE
INDEX, REINDEX, CLUSTER, etc.?

I don't think that'd be that bad, otherwise the naming of the fields is
complicated.

+1.

I suppose if it gets really out of control we might have to rethink,
but 2 is not 50, and having appropriate column names is worth a lot.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#17vinayak
Pokale_Vinayak_q3@lab.ntt.co.jp
In reply to: Haribabu Kommi (#15)
1 attachment(s)
Re: ANALYZE command progress checker

Thank you for reviewing the patch.

The attached patch incorporated Michael and Amit comments also.

On 2017/03/07 15:45, Haribabu Kommi wrote:

On Tue, Mar 7, 2017 at 5:01 PM, Michael Paquier
<michael.paquier@gmail.com <mailto:michael.paquier@gmail.com>> wrote:

@@ -496,7 +499,6 @@ do_analyze_rel(Relation onerel, int options,
VacuumParams *params,
numrows = (*acquirefunc) (onerel, elevel,
rows, targrows,
&totalrows, &totaldeadrows);
-
/*
Useless diff.

Fixed.

+     <entry>
+       <command>ANALYZE</> is currently collecting the sample rows.
+       The sample it reads is taken randomly.Its size depends on
+       the default_statistics_target parameter value.
+     </entry>
This should use a <varname> markup for default_statistics_target.

Fixed.

@@ -203,6 +204,8 @@ analyze_rel(Oid relid, RangeVar *relation, int
options,
if (onerel->rd_rel->relkind == RELKIND_RELATION ||
onerel->rd_rel->relkind == RELKIND_MATVIEW)
{
+       pgstat_progress_start_command(PROGRESS_COMMAND_ANALYZE,
+  RelationGetRelid(onerel));
It seems to me that the report should begin in do_analyze_rel().

Fixed.

some more comments,

+/* Report compute heap stats phase */
+pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+PROGRESS_ANALYZE_PHASE_COMPUTE_HEAP_STATS);

The above analyze phase is updated inside a for loop, instead just set
it above once.

Fixed.

+ /* Report compute index stats phase */
+ pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+ PROGRESS_ANALYZE_PHASE_COMPUTE_INDEX_STATS);

Irrespective of whether the index exists on the table or not, the
above analyze phase
is set. It is better to set the above phase and index cleanup phase
only when there
are indexes on the table.

Agreed. Fixed.

+/* Report total number of heap blocks and collectinf sample row phase*/

Typo? collecting?

Fixed.

+/* Report total number of heap blocks and collectinf sample row phase*/
+initprog_val[0] = PROGRESS_ANALYZE_PHASE_COLLECT_HEAP_SAMPLE_ROWS;
+initprog_val[1] = totalblocks;
+pgstat_progress_update_multi_param(2, initprog_index, initprog_val);
acquire_sample_rows function is called from acquire_inherited_sample_rows
function, so adding the phase in that function will provide wrong info.

I agree with you.

+#define PROGRESS_ANALYZE_PHASE_COLLECT_INH_SAMPLE_ROWS2

why there is no code added for the phase, any specific reason?

I am thinking how to report this phase. Do you have any suggestion?

+/* Phases of analyze */

Can be written as following for better understanding, and also
similar like vacuum.

/* Phases of analyze (as advertised via PROGRESS_ANALYZE_PHASE) */

Done.

Regards,
Vinayak Pokale
NTT Open Source Software Center

Attachments:

pg_stat_progress_analyze_v2.patchbinary/octet-stream; name=pg_stat_progress_analyze_v2.patchDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 4d03531..8aeca41 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -521,6 +521,13 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       <command>VACUUM</>, showing current progress.
       See <xref linkend='vacuum-progress-reporting'>.</entry>
      </row>
+
+     <row>
+      <entry><structname>pg_stat_progress_analyze</><indexterm><primary>pg_stat_progress_analyze</primary></indexterm></entry>
+      <entry>One row for each backend running
+      <command>ANALYZE</>, showing current progress.
+      See <xref linkend='analyze-progress-reporting'>.</entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
@@ -3016,7 +3023,135 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
    </tbody>
    </tgroup>
   </table>
+ </sect2>
+
+ <sect2 id="analyze-progress-reporting">
+  <title>ANALYZE Progress Reporting</title>
+
+  <para>
+   Whenever <command>ANALYZE</> is running, the
+   <structname>pg_stat_progress_analyze</structname> view will contain
+   one row for each backend that is currently analyzing.  
+   The tables below describe the information that will be reported and 
+   provide information about how to interpret it.
+  </para>
+
+  <table id="pg-stat-progress-analyze-view" xreflabel="pg_stat_progress_analyze">
+   <title><structname>pg_stat_progress_analyze</structname> View</title>
+   <tgroup cols="3">
+    <thead>
+    <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
 
+   <tbody>
+    <row>
+     <entry><structfield>pid</></entry>
+     <entry><type>integer</></entry>
+     <entry>Process ID of backend.</entry>
+    </row>
+    <row>
+     <entry><structfield>datid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>datname</></entry>
+     <entry><type>name</></entry>
+     <entry>Name of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>relid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the table being vacuumed.</entry>
+    </row>
+    <row>
+     <entry><structfield>phase</></entry>
+     <entry><type>text</></entry>
+     <entry>
+       Current processing phase of analyze.  See <xref linkend='analyze-phases'>.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>heap_blks_total</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of heap blocks in the table.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>heap_blks_scanned</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Number of heap blocks scanned.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>total_sample_rows</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of sample rows.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
+
+  <table id="analyze-phases">
+   <title>ANALYZE phases</title>
+   <tgroup cols="2">
+    <thead>
+    <row>
+      <entry>Phase</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><literal>initializing</literal></entry>
+     <entry>
+       <command>ANALYZE</> is preparing to collect sample rows.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>collecting sample rows</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently collecting the sample rows. 
+       The sample it reads is taken randomly.Its size depends on
+       the <varname>default_statistics_target</> parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>collecting inherited sample rows</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently collecting inherited sample rows.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>computing heap stats</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently computing heap stats.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>computing index stats</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently computing index stats.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>cleaning up indexes</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently cleaning up indexes.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
  </sect2>
  </sect1>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index ba980de..90e692a 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -872,6 +872,22 @@ CREATE VIEW pg_stat_progress_vacuum AS
     FROM pg_stat_get_progress_info('VACUUM') AS S
 		LEFT JOIN pg_database D ON S.datid = D.oid;
 
+CREATE VIEW pg_stat_progress_analyze AS
+	SELECT
+		S.pid AS pid, S.datid AS datid, D.datname AS datname,
+		S.relid AS relid,
+		CASE S.param1 WHEN 0 THEN 'initializing'
+					  WHEN 1 THEN 'collecting sample rows'
+					  WHEN 2 THEN 'collecting inherited sample rows'
+					  WHEN 3 THEN 'computing heap stats'
+					  WHEN 4 THEN 'computing index stats'
+					  WHEN 5 THEN 'cleaning up indexes'
+					  END AS phase,
+		S.param2 AS heap_blks_total, S.param3 AS heap_blks_scanned,
+		S.param4 AS total_sample_rows
+    FROM pg_stat_get_progress_info('ANALYZE') AS S
+		LEFT JOIN pg_database D ON S.datid = D.oid;
+
 CREATE VIEW pg_user_mappings AS
     SELECT
         U.oid       AS umid,
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index b91df98..2f744f4 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -31,6 +31,7 @@
 #include "commands/dbcommands.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "commands/progress.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -333,6 +334,7 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 				(errmsg("analyzing \"%s.%s\"",
 						get_namespace_name(RelationGetNamespace(onerel)),
 						RelationGetRelationName(onerel))));
+	pgstat_progress_start_command(PROGRESS_COMMAND_ANALYZE, RelationGetRelid(onerel));
 
 	/*
 	 * Set up a working context so that we can easily free whatever junk gets
@@ -423,6 +425,9 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	if (hasindex)
 	{
 		indexdata = (AnlIndexData *) palloc0(nindexes * sizeof(AnlIndexData));
+		/* Report compute index stats phase */
+		pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+									PROGRESS_ANALYZE_PHASE_COMPUTE_INDEX_STATS);
 		for (ind = 0; ind < nindexes; ind++)
 		{
 			AnlIndexData *thisdata = &indexdata[ind];
@@ -488,6 +493,9 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	 * Acquire the sample rows
 	 */
 	rows = (HeapTuple *) palloc(targrows * sizeof(HeapTuple));
+	/* Report collecting sample row phase*/
+	pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+								PROGRESS_ANALYZE_PHASE_COLLECT_HEAP_SAMPLE_ROWS);
 	if (inh)
 		numrows = acquire_inherited_sample_rows(onerel, elevel,
 												rows, targrows,
@@ -512,6 +520,9 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 											"Analyze Column",
 											ALLOCSET_DEFAULT_SIZES);
 		old_context = MemoryContextSwitchTo(col_context);
+		/* Report compute heap stats phase */
+		pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+									PROGRESS_ANALYZE_PHASE_COMPUTE_HEAP_STATS);
 
 		for (i = 0; i < attr_cnt; i++)
 		{
@@ -644,6 +655,9 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 				pfree(stats);
 		}
 	}
+	/* Report that we are now doing index cleanup */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+								PROGRESS_ANALYZE_PHASE_INDEX_CLEANUP);
 
 	/* Done with indexes */
 	vac_close_indexes(nindexes, Irel, NoLock);
@@ -672,6 +686,8 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	MemoryContextSwitchTo(caller_context);
 	MemoryContextDelete(anl_context);
 	anl_context = NULL;
+
+	pgstat_progress_end_command();
 }
 
 /*
@@ -998,6 +1014,8 @@ acquire_sample_rows(Relation onerel, int elevel,
 	Assert(targrows > 0);
 
 	totalblocks = RelationGetNumberOfBlocks(onerel);
+	/* Report total number of heap blocks */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_TOTAL_HEAP_BLKS, totalblocks);
 
 	/* Need a cutoff xmin for HeapTupleSatisfiesVacuum */
 	OldestXmin = GetOldestXmin(onerel, true);
@@ -1016,6 +1034,9 @@ acquire_sample_rows(Relation onerel, int elevel,
 		OffsetNumber targoffset,
 					maxoffset;
 
+		/* Report number of heap blocks scaned so far*/
+		pgstat_progress_update_param(PROGRESS_ANALYZE_HEAP_BLKS_SCANNED, targblock);
+
 		vacuum_delay_point();
 
 		/*
@@ -1217,6 +1238,9 @@ acquire_sample_rows(Relation onerel, int elevel,
 					liverows, deadrows,
 					numrows, *totalrows)));
 
+	/* Report total number of sample rows*/
+	pgstat_progress_update_param(PROGRESS_ANALYZE_TOTAL_SAMPLE_ROWS, numrows);
+
 	return numrows;
 }
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index a987d0d..eb1dd63 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -465,6 +465,8 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 	/* Translate command name into command type code. */
 	if (pg_strcasecmp(cmd, "VACUUM") == 0)
 		cmdtype = PROGRESS_COMMAND_VACUUM;
+	else if (pg_strcasecmp(cmd, "ANALYZE") == 0)
+		cmdtype = PROGRESS_COMMAND_ANALYZE;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h
index 9472ecc..6423e5a 100644
--- a/src/include/commands/progress.h
+++ b/src/include/commands/progress.h
@@ -34,4 +34,17 @@
 #define PROGRESS_VACUUM_PHASE_TRUNCATE			5
 #define PROGRESS_VACUUM_PHASE_FINAL_CLEANUP		6
 
+/* Progress parameters for analyze */
+#define PROGRESS_ANALYZE_PHASE					0
+#define PROGRESS_ANALYZE_TOTAL_HEAP_BLKS		1
+#define PROGRESS_ANALYZE_HEAP_BLKS_SCANNED		2
+#define PROGRESS_ANALYZE_TOTAL_SAMPLE_ROWS		3
+
+/* Phases of analyze (as advertised via PROGRESS_ANALYZE_PHASE) */
+#define PROGRESS_ANALYZE_PHASE_COLLECT_HEAP_SAMPLE_ROWS		1
+#define PROGRESS_ANALYZE_PHASE_COLLECT_INH_SAMPLE_ROWS		2
+#define PROGRESS_ANALYZE_PHASE_COMPUTE_HEAP_STATS			3
+#define PROGRESS_ANALYZE_PHASE_COMPUTE_INDEX_STATS			4
+#define PROGRESS_ANALYZE_PHASE_INDEX_CLEANUP				5
+
 #endif
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 60c78d1..2b51d76 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -812,7 +812,8 @@ typedef enum
 typedef enum ProgressCommandType
 {
 	PROGRESS_COMMAND_INVALID,
-	PROGRESS_COMMAND_VACUUM
+	PROGRESS_COMMAND_VACUUM,
+	PROGRESS_COMMAND_ANALYZE
 } ProgressCommandType;
 
 #define PGSTAT_NUM_PROGRESS_PARAM	10
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index c661f1d..a01c464 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1817,6 +1817,24 @@ pg_stat_progress_vacuum| SELECT s.pid,
     s.param7 AS num_dead_tuples
    FROM (pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)
      LEFT JOIN pg_database d ON ((s.datid = d.oid)));
+pg_stat_progress_analyze| SELECT s.pid,
+    s.datid,
+    d.datname,
+    s.relid,
+        CASE s.param1
+            WHEN 0 THEN 'initializing'::text
+            WHEN 1 THEN 'collecting sample rows'::text
+            WHEN 2 THEN 'collecting inherited sample rows'::text
+            WHEN 3 THEN 'computing heap stats'::text
+            WHEN 4 THEN 'computing index stats'::text
+            WHEN 5 THEN 'cleaning up indexes'::text
+            ELSE NULL::text
+        END AS phase,
+    s.param2 AS heap_blks_total,
+    s.param3 AS heap_blks_scanned,
+    s.param4 AS total_sample_rows,
+   FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)
+     LEFT JOIN pg_database d ON ((s.datid = d.oid)));
 pg_stat_replication| SELECT s.pid,
     s.usesysid,
     u.rolname AS usename,
#18Jim Nasby
jim.nasby@openscg.com
In reply to: Michael Paquier (#9)
Re: ANALYZE command progress checker

On 3/6/17 12:49 AM, Michael Paquier wrote:

On Sat, Mar 4, 2017 at 5:33 AM, David Steele <david@pgmasters.net> wrote:

I think the idea of a general progress view is very valuable and there
are a ton of operations it could be used for: full table scans, index
rebuilds, vacuum, copy, etc.

However, I feel that this proposal is not flexible enough and comes too
late in the release cycle to allow development into something that could
be committed.

Well, each command really has its own requirements in terms of data to
store, so we either finish with a bunch of small tables that anyone
could query and join as they wish or a somewhat unique table that is
bloated with all the information, with a set of views on top of it to
query all the information. For extensibility's sake of each command
(for example imagine that REINDEX could be extended with a
CONCURRENTLY option and multiple phases), I would think that having a
table per command type would not be that bad.

Well, the ideal scenario is that someone uses the raw data to come up
with a good way to just provide ye olde 0-100% progress bar. At that
point a single view would do the trick.

Perhaps instead of adding more clutter to \dvS we could just have a SRF
for now. At over 2800 rows currently, you're not going to notice one
more addition to \dfS.
--
Jim Nasby, Chief Data Architect, OpenSCG
http://OpenSCG.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#19Andres Freund
andres@anarazel.de
In reply to: Jim Nasby (#18)
Re: ANALYZE command progress checker

Hi,

On 2017-03-10 02:11:18 -0600, Jim Nasby wrote:

Perhaps instead of adding more clutter to \dvS we could just have a SRF for
now.

I don't see that as clutter, it's useful information, and keeping it
discoverable is good, not bad.

At over 2800 rows currently, you're not going to notice one more
addition to \dfS.

I think it's hard to design a good SRF for this. Because the fields for
different types of progress are different / empty, you can't just
trivially return them as rows. You'd have to do some EAV like
'command, field_name1, field_value1, ...' type of thing - not
particularly pretty / easy to use.

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Jim Nasby
jim.nasby@openscg.com
In reply to: Andres Freund (#19)
Re: ANALYZE command progress checker

On 3/10/17 1:06 PM, Andres Freund wrote:

Hi,

On 2017-03-10 02:11:18 -0600, Jim Nasby wrote:

Perhaps instead of adding more clutter to \dvS we could just have a SRF for
now.

I don't see that as clutter, it's useful information, and keeping it
discoverable is good, not bad.

If we keep adding status reporting commands at some point it's going to
get unwieldy. Though, if they were in their own schema...

At over 2800 rows currently, you're not going to notice one more
addition to \dfS.

I think it's hard to design a good SRF for this. Because the fields for
different types of progress are different / empty, you can't just
trivially return them as rows. You'd have to do some EAV like
'command, field_name1, field_value1, ...' type of thing - not
particularly pretty / easy to use.

Oh, I wasn't suggesting a single SRF for everything. Hopefully users
will eventually figure out a good formula to drive a "progress bar" for
each type of monitor, which is what you really want anyway (at least 99%
of the time). If we got there we could have a single view that gave the
% complete for every command that was providing feedback. If someone
wanted details they could hit the individual SRF.
--
Jim Nasby, Chief Data Architect, OpenSCG
http://OpenSCG.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#21Robert Haas
robertmhaas@gmail.com
In reply to: Jim Nasby (#20)
Re: ANALYZE command progress checker

On Fri, Mar 10, 2017 at 2:57 PM, Jim Nasby <jim.nasby@openscg.com> wrote:

Oh, I wasn't suggesting a single SRF for everything. Hopefully users will
eventually figure out a good formula to drive a "progress bar" for each type
of monitor, which is what you really want anyway (at least 99% of the time).
If we got there we could have a single view that gave the % complete for
every command that was providing feedback. If someone wanted details they
could hit the individual SRF.

So, the point, at least with the VACUUM progress indicator and perhaps
here also, is that we really can't just give a single measure of
progress -- we are 58.32% done. That's almost impossible to estimate
accurately. For example, consider VACUUM. maintenance_work_mem is
69% used, and we are 74% done vacuuming the table. What percentage
done are we? Well, it depends. If the latter part of the table has
exactly the same density of dead tuples we've already witnessed, we
will need only one index vac pass, but there is a good chance that
there's more activity toward the tail end of the table and we will
need two index vac passes. If the table has several large indexes,
that's a big difference, so if we guess wrong about whether we're
going to run out of memory, we will be way off in estimating overall
progress. That's why the vacuum progress checker reports the facts we
actually know -- like how many blocks of the relation we've scanned,
and how many dead tuples we've accumulated so far, and how many dead
tuples we are able to store before triggering index vacuuming --
rather than just a percentage. You can try to derive a percentage
from that if you want, and you can use any formula you like to derive
that, but what the system reports are straight-up facts, not
predictions.

I don't really want to get into the prediction business. The in-core
progress reporting structure can spit out enough data for people to
write queries or tools that attempt to predict stuff, and *maybe*
someday somebody will write one of those tools that is enough unlike
https://xkcd.com/612/ that we'll feel moved to put it in core. But my
guess is most likely not. It's easy to get the easy cases right in
such a prediction tool, but AFAICS getting the hard cases right
requires a crystal ball.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#22Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: vinayak (#17)
Re: ANALYZE command progress checker

On Fri, Mar 10, 2017 at 6:46 PM, vinayak <Pokale_Vinayak_q3@lab.ntt.co.jp>
wrote:

+ /* Report total number of heap blocks and collectinf sample row phase*/
+ initprog_val[0] = PROGRESS_ANALYZE_PHASE_COLLECT_HEAP_SAMPLE_ROWS;
+ initprog_val[1] = totalblocks;
+ pgstat_progress_update_multi_param(2, initprog_index, initprog_val);

acquire_sample_rows function is called from acquire_inherited_sample_rows
function, so adding the phase in that function will provide wrong info.

I agree with you.

+#define PROGRESS_ANALYZE_PHASE_COLLECT_INH_SAMPLE_ROWS 2

why there is no code added for the phase, any specific reason?

I am thinking how to report this phase. Do you have any suggestion?

Thanks for the update patch.

Actually the analyze operation is done in two stages for the relation that
contains child relations,
1. For parent relation
2. All child relations

So the progress with start each time for the above two stages. This needs
to clearly mentioned in the docs.

The acquire_sample_rows function collects statistics of the relation
that is provided, updating the analyze details like number of rows and etc
works fine for a single relation, but if it contains many child relations,
the
data will be misleading.

Apart from the above, some more comments.

+ /* Report compute index stats phase */
+ pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+ PROGRESS_ANALYZE_PHASE_COMPUTE_INDEX_STATS);

The above block of the code should be set when it actually doing the
compute_index_stats.

+ /* Report that we are now doing index cleanup */
+ pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+ PROGRESS_ANALYZE_PHASE_INDEX_CLEANUP);

The above code block should be inside if (!(options & VACOPT_VACUUM))
block.

Regards,
Hari Babu
Fujitsu Australia

#23Robert Haas
robertmhaas@gmail.com
In reply to: vinayak (#17)
Re: ANALYZE command progress checker

On Fri, Mar 10, 2017 at 2:46 AM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

Thank you for reviewing the patch.

The attached patch incorporated Michael and Amit comments also.

I reviewed this tonight.

+        /* Report compute index stats phase */
+        pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+
PROGRESS_ANALYZE_PHASE_COMPUTE_INDEX_STATS);

Hmm, is there really any point to this? And is it even accurate? It
doesn't look to me like we are computing any index statistics here;
we're only allocating a few in-memory data structures here, I think,
which is not a statistics computation and probably doesn't take any
significant time.

+        /* Report compute heap stats phase */
+        pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+                                    PROGRESS_ANALYZE_PHASE_COMPUTE_HEAP_STATS);

The phase you label as computing heap statistics also includes the
computation of index statistics. I wouldn't bother separating the
computation of heap and index statistics; I'd just have a "compute
statistics" phase that begins right after the comment that starts
"Compute the statistics."

+    /* Report that we are now doing index cleanup */
+    pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+                                PROGRESS_ANALYZE_PHASE_INDEX_CLEANUP);

OK, this doesn't make any sense either. We are not cleaning up the
indexes here. We are just closing them and releasing resources. I
don't see why you need this phase at all; it can't last more than some
tiny fraction of a second. It seems like you're trying to copy the
exact same phases that exist for vacuum instead of thinking about what
makes sense for ANALYZE.

+        /* Report number of heap blocks scaned so far*/
+        pgstat_progress_update_param(PROGRESS_ANALYZE_HEAP_BLKS_SCANNED,
targblock);

While I don't think this is necessarily a bad thing to report, I find
it very surprising that you're not reporting what seems to me like the
most useful thing here - namely, the number of blocks that will be
sampled in total and the number of those that you've selected.
Instead, you're just reporting the length of the relation and the
last-sampled block, which is a less-accurate guide to total progress.
There are enough counters to report both things, so maybe we should.

+    /* Report total number of sample rows*/
+    pgstat_progress_update_param(PROGRESS_ANALYZE_TOTAL_SAMPLE_ROWS, numrows);

As an alternative to the previous paragraph, yet another thing you
could report is number of rows sampled out of total rows to be
sampled. But this isn't the way to do it: you are reporting the
number of rows you sampled only once you've finished sampling. The
point of progress reporting is to report progress -- that is, to
report this value as it's updated, not just to report it when ANALYZE
is practically done and the value has reached its maximum.

The documentation for this patch isn't very good, either. You forgot
to update the part of the documentation that says that VACUUM is the
only command that currently supports progress reporting, and the
descriptions of the phases and progress counters are brief and not
entirely accurate - e.g. heap_blks_scanned is not actually the number
of heap blocks scanned, because we are sampling, not reading all the
blocks.

When adding calls to the progress reporting functions, please consider
whether a blank line should be added before or after the new code, or
both, or neither. I'd say some blank lines are needed in a few places
where you didn't add them.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#24vinayak
Pokale_Vinayak_q3@lab.ntt.co.jp
In reply to: Robert Haas (#23)
1 attachment(s)
Re: ANALYZE command progress checker

On 2017/03/17 10:38, Robert Haas wrote:

On Fri, Mar 10, 2017 at 2:46 AM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

Thank you for reviewing the patch.

The attached patch incorporated Michael and Amit comments also.

I reviewed this tonight.

Thank you for reviewing the patch.

+        /* Report compute index stats phase */
+        pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+
PROGRESS_ANALYZE_PHASE_COMPUTE_INDEX_STATS);

Hmm, is there really any point to this? And is it even accurate? It
doesn't look to me like we are computing any index statistics here;
we're only allocating a few in-memory data structures here, I think,
which is not a statistics computation and probably doesn't take any
significant time.

+        /* Report compute heap stats phase */
+        pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+                                    PROGRESS_ANALYZE_PHASE_COMPUTE_HEAP_STATS);

The phase you label as computing heap statistics also includes the
computation of index statistics. I wouldn't bother separating the
computation of heap and index statistics; I'd just have a "compute
statistics" phase that begins right after the comment that starts
"Compute the statistics."

Understood. Fixed in the attached patch.

+    /* Report that we are now doing index cleanup */
+    pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+                                PROGRESS_ANALYZE_PHASE_INDEX_CLEANUP);

OK, this doesn't make any sense either. We are not cleaning up the
indexes here. We are just closing them and releasing resources. I
don't see why you need this phase at all; it can't last more than some
tiny fraction of a second. It seems like you're trying to copy the
exact same phases that exist for vacuum instead of thinking about what
makes sense for ANALYZE.

Understood. I have removed this phase.

+        /* Report number of heap blocks scaned so far*/
+        pgstat_progress_update_param(PROGRESS_ANALYZE_HEAP_BLKS_SCANNED,
targblock);

While I don't think this is necessarily a bad thing to report, I find
it very surprising that you're not reporting what seems to me like the
most useful thing here - namely, the number of blocks that will be
sampled in total and the number of those that you've selected.
Instead, you're just reporting the length of the relation and the
last-sampled block, which is a less-accurate guide to total progress.
There are enough counters to report both things, so maybe we should.

+    /* Report total number of sample rows*/
+    pgstat_progress_update_param(PROGRESS_ANALYZE_TOTAL_SAMPLE_ROWS, numrows);

As an alternative to the previous paragraph, yet another thing you
could report is number of rows sampled out of total rows to be
sampled. But this isn't the way to do it: you are reporting the
number of rows you sampled only once you've finished sampling. The
point of progress reporting is to report progress -- that is, to
report this value as it's updated, not just to report it when ANALYZE
is practically done and the value has reached its maximum.

Understood.
I have reported number of rows sampled out of total rows to be sampled.
I have not reported the number of blocks that will be sampled in total.
So currect pg_stat_progress_analyze view looks like:

=# \d+ pg_stat_progress_analyze
View "pg_catalog.pg_stat_progress_analyze"
Column | Type | Collation | Nullable | Default |
Storage | Descripti
on
------------------------+---------+-----------+----------+---------+----------+----------
---
pid | integer | | | |
plain |
datid | oid | | | |
plain |
datname | name | | | |
plain |
relid | oid | | | |
plain |
phase | text | | | |
extended |
num_target_sample_rows | bigint | | | |
plain |
num_rows_sampled | bigint | | | |
plain |
View definition:
SELECT s.pid,
s.datid,
d.datname,
s.relid,
CASE s.param1
WHEN 0 THEN 'initializing'::text
WHEN 1 THEN 'collecting sample rows'::text
WHEN 2 THEN 'computing statistics'::text
ELSE NULL::text
END AS phase,
s.param2 AS num_target_sample_rows,
s.param3 AS num_rows_sampled
FROM pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid,
param1, param2, p
aram3, param4, param5, param6, param7, param8, param9, param10)
LEFT JOIN pg_database d ON s.datid = d.oid;

Comment?

The documentation for this patch isn't very good, either. You forgot
to update the part of the documentation that says that VACUUM is the
only command that currently supports progress reporting, and the
descriptions of the phases and progress counters are brief and not
entirely accurate - e.g. heap_blks_scanned is not actually the number
of heap blocks scanned, because we are sampling, not reading all the
blocks.

Understood. I have updated the documentation.
I will also try to improve documentation.

When adding calls to the progress reporting functions, please consider
whether a blank line should be added before or after the new code, or
both, or neither. I'd say some blank lines are needed in a few places
where you didn't add them.

+1. I have added blank lines in a few places.

Regards,
Vinayak Pokale

Attachments:

pg_stat_progress_analyze_v3.patchbinary/octet-stream; name=pg_stat_progress_analyze_v3.patchDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 9eaf43a..f03a657 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -521,6 +521,13 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       <command>VACUUM</>, showing current progress.
       See <xref linkend='vacuum-progress-reporting'>.</entry>
      </row>
+
+     <row>
+      <entry><structname>pg_stat_progress_analyze</><indexterm><primary>pg_stat_progress_analyze</primary></indexterm></entry>
+      <entry>One row for each backend running
+      <command>ANALYZE</>, showing current progress.
+      See <xref linkend='analyze-progress-reporting'>.</entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
@@ -2824,8 +2831,8 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
 
   <para>
    <productname>PostgreSQL</> has the ability to report the progress of
-   certain commands during command execution.  Currently, the only command
-   which supports progress reporting is <command>VACUUM</>.  This may be
+   certain commands during command execution.  Currently, the command
+   which supports progress reporting is <command>VACUUM</> and <command>ANALYZE</>.  This may be
    expanded in the future.
   </para>
 
@@ -3016,7 +3023,116 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
    </tbody>
    </tgroup>
   </table>
+ </sect2>
+
+ <sect2 id="analyze-progress-reporting">
+  <title>ANALYZE Progress Reporting</title>
+
+  <para>
+   Whenever <command>ANALYZE</> is running, the
+   <structname>pg_stat_progress_analyze</structname> view will contain
+   one row for each backend that is currently analyzing.  
+   The tables below describe the information that will be reported and 
+   provide information about how to interpret it.
+  </para>
+
+  <table id="pg-stat-progress-analyze-view" xreflabel="pg_stat_progress_analyze">
+   <title><structname>pg_stat_progress_analyze</structname> View</title>
+   <tgroup cols="3">
+    <thead>
+    <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><structfield>pid</></entry>
+     <entry><type>integer</></entry>
+     <entry>Process ID of backend.</entry>
+    </row>
+    <row>
+     <entry><structfield>datid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>datname</></entry>
+     <entry><type>name</></entry>
+     <entry>Name of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>relid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the table being analyzed.</entry>
+    </row>
+    <row>
+     <entry><structfield>phase</></entry>
+     <entry><type>text</></entry>
+     <entry>
+       Current processing phase of analyze.  See <xref linkend='analyze-phases'>.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>num_target_sample_rows</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of sample rows. The sample it reads is taken randomly. 
+       Its size depends on the <varname>default_statistics_target</> parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>num_rows_sampled</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of rows sampled so far.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
 
+  <table id="analyze-phases">
+   <title>ANALYZE phases</title>
+   <tgroup cols="2">
+    <thead>
+    <row>
+      <entry>Phase</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><literal>initializing</literal></entry>
+     <entry>
+       <command>ANALYZE</> is preparing to collect sample rows.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>collecting sample rows</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently collecting the sample rows. 
+       The sample it reads is taken randomly. Its size depends on
+       the <varname>default_statistics_target</> parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>computing stats</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently computing heap statistics 
+       and index statistics, such as the percentage of NULL values, 
+       the average width of a row, the number of distinct values etc. 
+       It also stores the most common values and their frequencies. 
+       The number of these values depends on the value of the 
+       <varname>default_statistics_target</> parameter.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
  </sect2>
  </sect1>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index b6552da..63451ba 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -872,6 +872,19 @@ CREATE VIEW pg_stat_progress_vacuum AS
     FROM pg_stat_get_progress_info('VACUUM') AS S
 		LEFT JOIN pg_database D ON S.datid = D.oid;
 
+CREATE VIEW pg_stat_progress_analyze AS
+	SELECT
+		S.pid AS pid, S.datid AS datid, D.datname AS datname,
+		S.relid AS relid,
+		CASE S.param1 WHEN 0 THEN 'initializing'
+					  WHEN 1 THEN 'collecting sample rows'
+					  WHEN 2 THEN 'computing statistics'
+					  END AS phase,
+		S.param2 AS num_target_sample_rows,
+		S.param3 AS num_rows_sampled
+    FROM pg_stat_get_progress_info('ANALYZE') AS S
+		LEFT JOIN pg_database D ON S.datid = D.oid;
+
 CREATE VIEW pg_user_mappings AS
     SELECT
         U.oid       AS umid,
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index b91df98..aa59a6e 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -31,6 +31,7 @@
 #include "commands/dbcommands.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "commands/progress.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -322,6 +323,11 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	Oid			save_userid;
 	int			save_sec_context;
 	int			save_nestlevel;
+	const int   initprog_index[] = {
+	PROGRESS_ANALYZE_PHASE,
+	PROGRESS_ANALYZE_NUM_TARGET_SAMPLE_ROWS
+	};
+	int64		initprog_val[2];
 
 	if (inh)
 		ereport(elevel,
@@ -334,6 +340,8 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 						get_namespace_name(RelationGetNamespace(onerel)),
 						RelationGetRelationName(onerel))));
 
+	pgstat_progress_start_command(PROGRESS_COMMAND_ANALYZE, RelationGetRelid(onerel));
+
 	/*
 	 * Set up a working context so that we can easily free whatever junk gets
 	 * created.
@@ -487,6 +495,12 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	/*
 	 * Acquire the sample rows
 	 */
+
+	/* Report collecting sample rows phase and number of target sample rows */
+	initprog_val[0] = PROGRESS_ANALYZE_PHASE_COLLECT_SAMPLE_ROWS;
+	initprog_val[1] = targrows;
+	pgstat_progress_update_multi_param(2, initprog_index, initprog_val);
+
 	rows = (HeapTuple *) palloc(targrows * sizeof(HeapTuple));
 	if (inh)
 		numrows = acquire_inherited_sample_rows(onerel, elevel,
@@ -503,6 +517,10 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	 * responsible to make sure that whatever they store into the VacAttrStats
 	 * structure is allocated in anl_context.
 	 */
+
+	/* Report compute statistics phase */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+								PROGRESS_ANALYZE_PHASE_COMPUTE_STATS);
 	if (numrows > 0)
 	{
 		MemoryContext col_context,
@@ -672,6 +690,8 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	MemoryContextSwitchTo(caller_context);
 	MemoryContextDelete(anl_context);
 	anl_context = NULL;
+
+	pgstat_progress_end_command();
 }
 
 /*
@@ -1172,6 +1192,9 @@ acquire_sample_rows(Relation onerel, int elevel,
 				}
 
 				samplerows += 1;
+
+				/* Report number of rows sampled so far */
+				pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_SAMPLED, numrows);
 			}
 		}
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index a987d0d..eb1dd63 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -465,6 +465,8 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 	/* Translate command name into command type code. */
 	if (pg_strcasecmp(cmd, "VACUUM") == 0)
 		cmdtype = PROGRESS_COMMAND_VACUUM;
+	else if (pg_strcasecmp(cmd, "ANALYZE") == 0)
+		cmdtype = PROGRESS_COMMAND_ANALYZE;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h
index 9472ecc..c32b4eb 100644
--- a/src/include/commands/progress.h
+++ b/src/include/commands/progress.h
@@ -34,4 +34,13 @@
 #define PROGRESS_VACUUM_PHASE_TRUNCATE			5
 #define PROGRESS_VACUUM_PHASE_FINAL_CLEANUP		6
 
+/* Progress parameters for analyze */
+#define PROGRESS_ANALYZE_PHASE					0
+#define PROGRESS_ANALYZE_NUM_TARGET_SAMPLE_ROWS	1
+#define PROGRESS_ANALYZE_NUM_ROWS_SAMPLED		2
+
+/* Phases of analyze (as advertised via PROGRESS_ANALYZE_PHASE) */
+#define PROGRESS_ANALYZE_PHASE_COLLECT_SAMPLE_ROWS		1
+#define PROGRESS_ANALYZE_PHASE_COMPUTE_STATS			2
+
 #endif
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 60c78d1..2b51d76 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -812,7 +812,8 @@ typedef enum
 typedef enum ProgressCommandType
 {
 	PROGRESS_COMMAND_INVALID,
-	PROGRESS_COMMAND_VACUUM
+	PROGRESS_COMMAND_VACUUM,
+	PROGRESS_COMMAND_ANALYZE
 } ProgressCommandType;
 
 #define PGSTAT_NUM_PROGRESS_PARAM	10
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index bd13ae6..8a8caeb 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1817,6 +1817,20 @@ pg_stat_progress_vacuum| SELECT s.pid,
     s.param7 AS num_dead_tuples
    FROM (pg_stat_get_progress_info('VACUUM'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)
      LEFT JOIN pg_database d ON ((s.datid = d.oid)));
+pg_stat_progress_analyze| SELECT s.pid,
+    s.datid,
+    d.datname,
+    s.relid,
+        CASE s.param1
+            WHEN 0 THEN 'initializing'::text
+            WHEN 1 THEN 'collecting sample rows'::text
+            WHEN 2 THEN 'computing statistics'::text
+            ELSE NULL::text
+        END AS phase,
+	S.param2 AS num_target_sample_rows,
+	S.param3 AS num_rows_sampled
+   FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)
+     LEFT JOIN pg_database d ON ((s.datid = d.oid)));
 pg_stat_replication| SELECT s.pid,
     s.usesysid,
     u.rolname AS usename,
#25Ashutosh Sharma
ashu.coek88@gmail.com
In reply to: vinayak (#24)
Re: ANALYZE command progress checker

Hi Vinayak,

Here are couple of review comments that may need your attention.

1. Firstly, I am seeing some trailing whitespace errors when trying to
apply your v3 patch using git apply command.

[ashu@localhost postgresql]$ git apply pg_stat_progress_analyze_v3.patch
pg_stat_progress_analyze_v3.patch:155: trailing whitespace.
CREATE VIEW pg_stat_progress_analyze AS
pg_stat_progress_analyze_v3.patch:156: trailing whitespace.
SELECT
pg_stat_progress_analyze_v3.patch:157: trailing whitespace.
S.pid AS pid, S.datid AS datid, D.datname AS datname,
pg_stat_progress_analyze_v3.patch:158: trailing whitespace.
S.relid AS relid,
pg_stat_progress_analyze_v3.patch:159: trailing whitespace.
CASE S.param1 WHEN 0 THEN 'initializing'
error: patch failed: doc/src/sgml/monitoring.sgml:521

2) The test-case 'rules' is failing. I think it's failing because in
rules.sql 'ORDERBY viewname' is used which will put
'pg_stat_progress_analyze' ahead of 'pg_stat_progress_vacuum' in the
list of catalog views. You may need to correct rules.out file
accordingly. This is what i could see in rules.sql,

SELECT viewname, definition FROM pg_views WHERE schemaname <>
'information_schema' ORDER BY viewname;

I am still reviewing your patch and doing some testing. Will update if
i find any issues. Thanks.

--
With Regards,
Ashutosh Sharma
EnterpriseDB:http://www.enterprisedb.com

On Fri, Mar 17, 2017 at 3:16 PM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

On 2017/03/17 10:38, Robert Haas wrote:

On Fri, Mar 10, 2017 at 2:46 AM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

Thank you for reviewing the patch.

The attached patch incorporated Michael and Amit comments also.

I reviewed this tonight.

Thank you for reviewing the patch.

+        /* Report compute index stats phase */
+        pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+
PROGRESS_ANALYZE_PHASE_COMPUTE_INDEX_STATS);

Hmm, is there really any point to this? And is it even accurate? It
doesn't look to me like we are computing any index statistics here;
we're only allocating a few in-memory data structures here, I think,
which is not a statistics computation and probably doesn't take any
significant time.

+        /* Report compute heap stats phase */
+        pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+
PROGRESS_ANALYZE_PHASE_COMPUTE_HEAP_STATS);

The phase you label as computing heap statistics also includes the
computation of index statistics. I wouldn't bother separating the
computation of heap and index statistics; I'd just have a "compute
statistics" phase that begins right after the comment that starts
"Compute the statistics."

Understood. Fixed in the attached patch.

+    /* Report that we are now doing index cleanup */
+    pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+                                PROGRESS_ANALYZE_PHASE_INDEX_CLEANUP);

OK, this doesn't make any sense either. We are not cleaning up the
indexes here. We are just closing them and releasing resources. I
don't see why you need this phase at all; it can't last more than some
tiny fraction of a second. It seems like you're trying to copy the
exact same phases that exist for vacuum instead of thinking about what
makes sense for ANALYZE.

Understood. I have removed this phase.

+        /* Report number of heap blocks scaned so far*/
+        pgstat_progress_update_param(PROGRESS_ANALYZE_HEAP_BLKS_SCANNED,
targblock);

While I don't think this is necessarily a bad thing to report, I find
it very surprising that you're not reporting what seems to me like the
most useful thing here - namely, the number of blocks that will be
sampled in total and the number of those that you've selected.
Instead, you're just reporting the length of the relation and the
last-sampled block, which is a less-accurate guide to total progress.
There are enough counters to report both things, so maybe we should.

+    /* Report total number of sample rows*/
+    pgstat_progress_update_param(PROGRESS_ANALYZE_TOTAL_SAMPLE_ROWS,
numrows);

As an alternative to the previous paragraph, yet another thing you
could report is number of rows sampled out of total rows to be
sampled. But this isn't the way to do it: you are reporting the
number of rows you sampled only once you've finished sampling. The
point of progress reporting is to report progress -- that is, to
report this value as it's updated, not just to report it when ANALYZE
is practically done and the value has reached its maximum.

Understood.
I have reported number of rows sampled out of total rows to be sampled.
I have not reported the number of blocks that will be sampled in total.
So currect pg_stat_progress_analyze view looks like:

=# \d+ pg_stat_progress_analyze
View "pg_catalog.pg_stat_progress_analyze"
Column | Type | Collation | Nullable | Default | Storage
| Descripti
on
------------------------+---------+-----------+----------+---------+----------+----------
---
pid | integer | | | | plain
|
datid | oid | | | | plain
|
datname | name | | | | plain
|
relid | oid | | | | plain
|
phase | text | | | |
extended |
num_target_sample_rows | bigint | | | | plain
|
num_rows_sampled | bigint | | | | plain
|
View definition:
SELECT s.pid,
s.datid,
d.datname,
s.relid,
CASE s.param1
WHEN 0 THEN 'initializing'::text
WHEN 1 THEN 'collecting sample rows'::text
WHEN 2 THEN 'computing statistics'::text
ELSE NULL::text
END AS phase,
s.param2 AS num_target_sample_rows,
s.param3 AS num_rows_sampled
FROM pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid,
param1, param2, p
aram3, param4, param5, param6, param7, param8, param9, param10)
LEFT JOIN pg_database d ON s.datid = d.oid;

Comment?

The documentation for this patch isn't very good, either. You forgot
to update the part of the documentation that says that VACUUM is the
only command that currently supports progress reporting, and the
descriptions of the phases and progress counters are brief and not
entirely accurate - e.g. heap_blks_scanned is not actually the number
of heap blocks scanned, because we are sampling, not reading all the
blocks.

Understood. I have updated the documentation.
I will also try to improve documentation.

When adding calls to the progress reporting functions, please consider
whether a blank line should be added before or after the new code, or
both, or neither. I'd say some blank lines are needed in a few places
where you didn't add them.

+1. I have added blank lines in a few places.

Regards,
Vinayak Pokale

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#26Ashutosh Sharma
ashu.coek88@gmail.com
In reply to: Ashutosh Sharma (#25)
Re: ANALYZE command progress checker

Hi,

I didn't find any major issues with the patch. It works as expected.
However, I have few minor comments that I would like to share,

+     <entry>
+       Total number of sample rows. The sample it reads is taken randomly.
+       Its size depends on the <varname>default_statistics_target</>
parameter value.
+     </entry>

1) I think it would be better if you could specify reference link to
the guc variable 'default_statistics_target'. Something like this,

<xref linkend='guc-default-statistics-target'>.

If you go through monitoring.sgml, you would find that there is
reference link to all guc variables being used.

2) I feel that the 'computing_statistics' phase could have been
splitted into 'computing_column_stats', 'computing_index_stats'....
Please let me know your thoughts on this.

+   certain commands during command execution.  Currently, the command
+   which supports progress reporting is <command>VACUUM</> and
<command>ANALYZE</>.  This may be
    expanded in the future.

3) I think above needs to be rephrased. Something like...Currently,
the supported progress reporting commands are 'VACUUM' and
'ANALYZE'.

Moreover, I also ran your patch on Windows platform and didn't find
any issues. Thanks.

--
With Regards,
Ashutosh Sharma
EnterpriseDB:http://www.enterprisedb.com

On Sat, Mar 18, 2017 at 5:30 PM, Ashutosh Sharma <ashu.coek88@gmail.com> wrote:

Hi Vinayak,

Here are couple of review comments that may need your attention.

1. Firstly, I am seeing some trailing whitespace errors when trying to
apply your v3 patch using git apply command.

[ashu@localhost postgresql]$ git apply pg_stat_progress_analyze_v3.patch
pg_stat_progress_analyze_v3.patch:155: trailing whitespace.
CREATE VIEW pg_stat_progress_analyze AS
pg_stat_progress_analyze_v3.patch:156: trailing whitespace.
SELECT
pg_stat_progress_analyze_v3.patch:157: trailing whitespace.
S.pid AS pid, S.datid AS datid, D.datname AS datname,
pg_stat_progress_analyze_v3.patch:158: trailing whitespace.
S.relid AS relid,
pg_stat_progress_analyze_v3.patch:159: trailing whitespace.
CASE S.param1 WHEN 0 THEN 'initializing'
error: patch failed: doc/src/sgml/monitoring.sgml:521

2) The test-case 'rules' is failing. I think it's failing because in
rules.sql 'ORDERBY viewname' is used which will put
'pg_stat_progress_analyze' ahead of 'pg_stat_progress_vacuum' in the
list of catalog views. You may need to correct rules.out file
accordingly. This is what i could see in rules.sql,

SELECT viewname, definition FROM pg_views WHERE schemaname <>
'information_schema' ORDER BY viewname;

I am still reviewing your patch and doing some testing. Will update if
i find any issues. Thanks.

--
With Regards,
Ashutosh Sharma
EnterpriseDB:http://www.enterprisedb.com

On Fri, Mar 17, 2017 at 3:16 PM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

On 2017/03/17 10:38, Robert Haas wrote:

On Fri, Mar 10, 2017 at 2:46 AM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

Thank you for reviewing the patch.

The attached patch incorporated Michael and Amit comments also.

I reviewed this tonight.

Thank you for reviewing the patch.

+        /* Report compute index stats phase */
+        pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+
PROGRESS_ANALYZE_PHASE_COMPUTE_INDEX_STATS);

Hmm, is there really any point to this? And is it even accurate? It
doesn't look to me like we are computing any index statistics here;
we're only allocating a few in-memory data structures here, I think,
which is not a statistics computation and probably doesn't take any
significant time.

+        /* Report compute heap stats phase */
+        pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+
PROGRESS_ANALYZE_PHASE_COMPUTE_HEAP_STATS);

The phase you label as computing heap statistics also includes the
computation of index statistics. I wouldn't bother separating the
computation of heap and index statistics; I'd just have a "compute
statistics" phase that begins right after the comment that starts
"Compute the statistics."

Understood. Fixed in the attached patch.

+    /* Report that we are now doing index cleanup */
+    pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+                                PROGRESS_ANALYZE_PHASE_INDEX_CLEANUP);

OK, this doesn't make any sense either. We are not cleaning up the
indexes here. We are just closing them and releasing resources. I
don't see why you need this phase at all; it can't last more than some
tiny fraction of a second. It seems like you're trying to copy the
exact same phases that exist for vacuum instead of thinking about what
makes sense for ANALYZE.

Understood. I have removed this phase.

+        /* Report number of heap blocks scaned so far*/
+        pgstat_progress_update_param(PROGRESS_ANALYZE_HEAP_BLKS_SCANNED,
targblock);

While I don't think this is necessarily a bad thing to report, I find
it very surprising that you're not reporting what seems to me like the
most useful thing here - namely, the number of blocks that will be
sampled in total and the number of those that you've selected.
Instead, you're just reporting the length of the relation and the
last-sampled block, which is a less-accurate guide to total progress.
There are enough counters to report both things, so maybe we should.

+    /* Report total number of sample rows*/
+    pgstat_progress_update_param(PROGRESS_ANALYZE_TOTAL_SAMPLE_ROWS,
numrows);

As an alternative to the previous paragraph, yet another thing you
could report is number of rows sampled out of total rows to be
sampled. But this isn't the way to do it: you are reporting the
number of rows you sampled only once you've finished sampling. The
point of progress reporting is to report progress -- that is, to
report this value as it's updated, not just to report it when ANALYZE
is practically done and the value has reached its maximum.

Understood.
I have reported number of rows sampled out of total rows to be sampled.
I have not reported the number of blocks that will be sampled in total.
So currect pg_stat_progress_analyze view looks like:

=# \d+ pg_stat_progress_analyze
View "pg_catalog.pg_stat_progress_analyze"
Column | Type | Collation | Nullable | Default | Storage
| Descripti
on
------------------------+---------+-----------+----------+---------+----------+----------
---
pid | integer | | | | plain
|
datid | oid | | | | plain
|
datname | name | | | | plain
|
relid | oid | | | | plain
|
phase | text | | | |
extended |
num_target_sample_rows | bigint | | | | plain
|
num_rows_sampled | bigint | | | | plain
|
View definition:
SELECT s.pid,
s.datid,
d.datname,
s.relid,
CASE s.param1
WHEN 0 THEN 'initializing'::text
WHEN 1 THEN 'collecting sample rows'::text
WHEN 2 THEN 'computing statistics'::text
ELSE NULL::text
END AS phase,
s.param2 AS num_target_sample_rows,
s.param3 AS num_rows_sampled
FROM pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid,
param1, param2, p
aram3, param4, param5, param6, param7, param8, param9, param10)
LEFT JOIN pg_database d ON s.datid = d.oid;

Comment?

The documentation for this patch isn't very good, either. You forgot
to update the part of the documentation that says that VACUUM is the
only command that currently supports progress reporting, and the
descriptions of the phases and progress counters are brief and not
entirely accurate - e.g. heap_blks_scanned is not actually the number
of heap blocks scanned, because we are sampling, not reading all the
blocks.

Understood. I have updated the documentation.
I will also try to improve documentation.

When adding calls to the progress reporting functions, please consider
whether a blank line should be added before or after the new code, or
both, or neither. I'd say some blank lines are needed in a few places
where you didn't add them.

+1. I have added blank lines in a few places.

Regards,
Vinayak Pokale

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27vinayak
Pokale_Vinayak_q3@lab.ntt.co.jp
In reply to: Ashutosh Sharma (#25)
Re: ANALYZE command progress checker

Hi Ashutosh,

Thank you for reviewing the patch.

On 2017/03/18 21:00, Ashutosh Sharma wrote:

Hi Vinayak,

Here are couple of review comments that may need your attention.

1. Firstly, I am seeing some trailing whitespace errors when trying to
apply your v3 patch using git apply command.

[ashu@localhost postgresql]$ git apply pg_stat_progress_analyze_v3.patch
pg_stat_progress_analyze_v3.patch:155: trailing whitespace.
CREATE VIEW pg_stat_progress_analyze AS
pg_stat_progress_analyze_v3.patch:156: trailing whitespace.
SELECT
pg_stat_progress_analyze_v3.patch:157: trailing whitespace.
S.pid AS pid, S.datid AS datid, D.datname AS datname,
pg_stat_progress_analyze_v3.patch:158: trailing whitespace.
S.relid AS relid,
pg_stat_progress_analyze_v3.patch:159: trailing whitespace.
CASE S.param1 WHEN 0 THEN 'initializing'
error: patch failed: doc/src/sgml/monitoring.sgml:521

I have tried to apply patch using "git apply" and it works fine in my
environment.
I use below command to apply the patch.
patch -p1 < pg_stat_progress_analyze_v3.patch

2) The test-case 'rules' is failing. I think it's failing because in
rules.sql 'ORDERBY viewname' is used which will put
'pg_stat_progress_analyze' ahead of 'pg_stat_progress_vacuum' in the
list of catalog views. You may need to correct rules.out file
accordingly. This is what i could see in rules.sql,

SELECT viewname, definition FROM pg_views WHERE schemaname <>
'information_schema' ORDER BY viewname;

I am still reviewing your patch and doing some testing. Will update if
i find any issues. Thanks.

Understood. I have fixed it.

Regards,
Vinayak Pokale
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#28vinayak
Pokale_Vinayak_q3@lab.ntt.co.jp
In reply to: Ashutosh Sharma (#26)
1 attachment(s)
Re: ANALYZE command progress checker

Hi Ashutosh,

On 2017/03/19 17:56, Ashutosh Sharma wrote:

Hi,

I didn't find any major issues with the patch. It works as expected.
However, I have few minor comments that I would like to share,

+     <entry>
+       Total number of sample rows. The sample it reads is taken randomly.
+       Its size depends on the <varname>default_statistics_target</>
parameter value.
+     </entry>

1) I think it would be better if you could specify reference link to
the guc variable 'default_statistics_target'. Something like this,

<xref linkend='guc-default-statistics-target'>.

If you go through monitoring.sgml, you would find that there is
reference link to all guc variables being used.

+1. Updated in the attached patch.

2) I feel that the 'computing_statistics' phase could have been
splitted into 'computing_column_stats', 'computing_index_stats'....
Please let me know your thoughts on this.

Initially I have spitted this phase as 'computing heap stats' and
'computing index stats' but
I agreed with Roberts suggestion to merge two phases into one as
'computing statistics'.

+   certain commands during command execution.  Currently, the command
+   which supports progress reporting is <command>VACUUM</> and
<command>ANALYZE</>.  This may be
expanded in the future.

3) I think above needs to be rephrased. Something like...Currently,
the supported progress reporting commands are 'VACUUM' and
'ANALYZE'.

+1. Updated in the attached patch.

Moreover, I also ran your patch on Windows platform and didn't find
any issues. Thanks.

Thank you for testing the patch on Windows platform.

Regards,
Vinayak Pokale
NTT Open Source Software Center

Attachments:

pg_stat_progress_analyze_v4.patchbinary/octet-stream; name=pg_stat_progress_analyze_v4.patchDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index dcb2d33..9b9d72e 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -521,6 +521,13 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       <command>VACUUM</>, showing current progress.
       See <xref linkend='vacuum-progress-reporting'>.</entry>
      </row>
+
+     <row>
+      <entry><structname>pg_stat_progress_analyze</><indexterm><primary>pg_stat_progress_analyze</primary></indexterm></entry>
+      <entry>One row for each backend running
+      <command>ANALYZE</>, showing current progress.
+      See <xref linkend='analyze-progress-reporting'>.</entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
@@ -3095,9 +3102,8 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
 
   <para>
    <productname>PostgreSQL</> has the ability to report the progress of
-   certain commands during command execution.  Currently, the only command
-   which supports progress reporting is <command>VACUUM</>.  This may be
-   expanded in the future.
+   certain commands during command execution.  Currently, the supported progress 
+   reporting commands are <command>VACUUM</> and <command>ANALYZE</>.
   </para>
 
  <sect2 id="vacuum-progress-reporting">
@@ -3287,7 +3293,116 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
    </tbody>
    </tgroup>
   </table>
+ </sect2>
+
+ <sect2 id="analyze-progress-reporting">
+  <title>ANALYZE Progress Reporting</title>
+
+  <para>
+   Whenever <command>ANALYZE</> is running, the
+   <structname>pg_stat_progress_analyze</structname> view will contain
+   one row for each backend that is currently analyzing.  
+   The tables below describe the information that will be reported and 
+   provide information about how to interpret it.
+  </para>
+
+  <table id="pg-stat-progress-analyze-view" xreflabel="pg_stat_progress_analyze">
+   <title><structname>pg_stat_progress_analyze</structname> View</title>
+   <tgroup cols="3">
+    <thead>
+    <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><structfield>pid</></entry>
+     <entry><type>integer</></entry>
+     <entry>Process ID of backend.</entry>
+    </row>
+    <row>
+     <entry><structfield>datid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>datname</></entry>
+     <entry><type>name</></entry>
+     <entry>Name of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>relid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the table being analyzed.</entry>
+    </row>
+    <row>
+     <entry><structfield>phase</></entry>
+     <entry><type>text</></entry>
+     <entry>
+       Current processing phase of analyze.  See <xref linkend='analyze-phases'>.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>num_target_sample_rows</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of sample rows. The sample it reads is taken randomly. 
+       Its size depends on the <xref linkend="guc-default-statistics-target"> parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>num_rows_sampled</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of rows sampled so far.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
 
+  <table id="analyze-phases">
+   <title>ANALYZE phases</title>
+   <tgroup cols="2">
+    <thead>
+    <row>
+      <entry>Phase</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><literal>initializing</literal></entry>
+     <entry>
+       <command>ANALYZE</> is preparing to collect sample rows.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>collecting sample rows</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently collecting the sample rows. 
+       The sample it reads is taken randomly. Its size depends on
+       the <xref linkend="guc-default-statistics-target"> parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>computing stats</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently computing heap statistics 
+       and index statistics, such as the percentage of NULL values, 
+       the average width of a row, the number of distinct values etc. 
+       It also stores the most common values and their frequencies. 
+       The number of these values depends on the value of the 
+       <xref linkend="guc-default-statistics-target"> parameter.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
  </sect2>
  </sect1>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index b6552da..63451ba 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -872,6 +872,19 @@ CREATE VIEW pg_stat_progress_vacuum AS
     FROM pg_stat_get_progress_info('VACUUM') AS S
 		LEFT JOIN pg_database D ON S.datid = D.oid;
 
+CREATE VIEW pg_stat_progress_analyze AS
+	SELECT
+		S.pid AS pid, S.datid AS datid, D.datname AS datname,
+		S.relid AS relid,
+		CASE S.param1 WHEN 0 THEN 'initializing'
+					  WHEN 1 THEN 'collecting sample rows'
+					  WHEN 2 THEN 'computing statistics'
+					  END AS phase,
+		S.param2 AS num_target_sample_rows,
+		S.param3 AS num_rows_sampled
+    FROM pg_stat_get_progress_info('ANALYZE') AS S
+		LEFT JOIN pg_database D ON S.datid = D.oid;
+
 CREATE VIEW pg_user_mappings AS
     SELECT
         U.oid       AS umid,
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index b91df98..aa59a6e 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -31,6 +31,7 @@
 #include "commands/dbcommands.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "commands/progress.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -322,6 +323,11 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	Oid			save_userid;
 	int			save_sec_context;
 	int			save_nestlevel;
+	const int   initprog_index[] = {
+	PROGRESS_ANALYZE_PHASE,
+	PROGRESS_ANALYZE_NUM_TARGET_SAMPLE_ROWS
+	};
+	int64		initprog_val[2];
 
 	if (inh)
 		ereport(elevel,
@@ -334,6 +340,8 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 						get_namespace_name(RelationGetNamespace(onerel)),
 						RelationGetRelationName(onerel))));
 
+	pgstat_progress_start_command(PROGRESS_COMMAND_ANALYZE, RelationGetRelid(onerel));
+
 	/*
 	 * Set up a working context so that we can easily free whatever junk gets
 	 * created.
@@ -487,6 +495,12 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	/*
 	 * Acquire the sample rows
 	 */
+
+	/* Report collecting sample rows phase and number of target sample rows */
+	initprog_val[0] = PROGRESS_ANALYZE_PHASE_COLLECT_SAMPLE_ROWS;
+	initprog_val[1] = targrows;
+	pgstat_progress_update_multi_param(2, initprog_index, initprog_val);
+
 	rows = (HeapTuple *) palloc(targrows * sizeof(HeapTuple));
 	if (inh)
 		numrows = acquire_inherited_sample_rows(onerel, elevel,
@@ -503,6 +517,10 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	 * responsible to make sure that whatever they store into the VacAttrStats
 	 * structure is allocated in anl_context.
 	 */
+
+	/* Report compute statistics phase */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+								PROGRESS_ANALYZE_PHASE_COMPUTE_STATS);
 	if (numrows > 0)
 	{
 		MemoryContext col_context,
@@ -672,6 +690,8 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	MemoryContextSwitchTo(caller_context);
 	MemoryContextDelete(anl_context);
 	anl_context = NULL;
+
+	pgstat_progress_end_command();
 }
 
 /*
@@ -1172,6 +1192,9 @@ acquire_sample_rows(Relation onerel, int elevel,
 				}
 
 				samplerows += 1;
+
+				/* Report number of rows sampled so far */
+				pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_SAMPLED, numrows);
 			}
 		}
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index a987d0d..eb1dd63 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -465,6 +465,8 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 	/* Translate command name into command type code. */
 	if (pg_strcasecmp(cmd, "VACUUM") == 0)
 		cmdtype = PROGRESS_COMMAND_VACUUM;
+	else if (pg_strcasecmp(cmd, "ANALYZE") == 0)
+		cmdtype = PROGRESS_COMMAND_ANALYZE;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h
index 9472ecc..c32b4eb 100644
--- a/src/include/commands/progress.h
+++ b/src/include/commands/progress.h
@@ -34,4 +34,13 @@
 #define PROGRESS_VACUUM_PHASE_TRUNCATE			5
 #define PROGRESS_VACUUM_PHASE_FINAL_CLEANUP		6
 
+/* Progress parameters for analyze */
+#define PROGRESS_ANALYZE_PHASE					0
+#define PROGRESS_ANALYZE_NUM_TARGET_SAMPLE_ROWS	1
+#define PROGRESS_ANALYZE_NUM_ROWS_SAMPLED		2
+
+/* Phases of analyze (as advertised via PROGRESS_ANALYZE_PHASE) */
+#define PROGRESS_ANALYZE_PHASE_COLLECT_SAMPLE_ROWS		1
+#define PROGRESS_ANALYZE_PHASE_COMPUTE_STATS			2
+
 #endif
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index f2daf32..7dc3392 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -890,7 +890,8 @@ typedef enum
 typedef enum ProgressCommandType
 {
 	PROGRESS_COMMAND_INVALID,
-	PROGRESS_COMMAND_VACUUM
+	PROGRESS_COMMAND_VACUUM,
+	PROGRESS_COMMAND_ANALYZE
 } ProgressCommandType;
 
 #define PGSTAT_NUM_PROGRESS_PARAM	10
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index bd13ae6..69484ed 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1795,6 +1795,20 @@ pg_stat_database_conflicts| SELECT d.oid AS datid,
     pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin,
     pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock
    FROM pg_database d;
+pg_stat_progress_analyze| SELECT s.pid,
+    s.datid,
+    d.datname,
+    s.relid,
+        CASE s.param1
+            WHEN 0 THEN 'initializing'::text
+            WHEN 1 THEN 'collecting sample rows'::text
+            WHEN 2 THEN 'computing statistics'::text
+            ELSE NULL::text
+        END AS phase,
+    s.param2 AS num_target_sample_rows,
+    s.param3 AS num_rows_sampled
+   FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)
+     LEFT JOIN pg_database d ON ((s.datid = d.oid)));
 pg_stat_progress_vacuum| SELECT s.pid,
     s.datid,
     d.datname,
#29Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: vinayak (#28)
Re: ANALYZE command progress checker

On Tue, Mar 21, 2017 at 3:41 PM, vinayak <Pokale_Vinayak_q3@lab.ntt.co.jp>
wrote:

Thank you for testing the patch on Windows platform.

Thanks for the updated patch.

It works good for a normal relation. But for a relation that contains
child tables,
the PROGRESS_ANALYZE_NUM_ROWS_SAMPLED produces wrong results.

How about adding another phase called
PROGRESS_ANALYZE_PHASE_COLLECT_INHERIT_SAMPLE_ROWS
and set this phase only when it is an inheritance analyze operation. And
adding
some explanation of ROWS_SAMPLED phase about inheritance tables
how these sampled rows are calculated will provide good analyze progress of
relation that contains child relations also.

Regards,
Hari Babu
Fujitsu Australia

#30vinayak
Pokale_Vinayak_q3@lab.ntt.co.jp
In reply to: Haribabu Kommi (#29)
1 attachment(s)
Re: ANALYZE command progress checker

On 2017/03/21 21:25, Haribabu Kommi wrote:

On Tue, Mar 21, 2017 at 3:41 PM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp
<mailto:Pokale_Vinayak_q3@lab.ntt.co.jp>> wrote:

Thank you for testing the patch on Windows platform.

Thanks for the updated patch.

It works good for a normal relation. But for a relation that contains
child tables,
the PROGRESS_ANALYZE_NUM_ROWS_SAMPLED produces wrong results.

Thank you for reviewing the patch.
The attached patch implements a way to report sample rows count from
acquire_sample_rows() even if called for child tables.

How about adding another phase called
PROGRESS_ANALYZE_PHASE_COLLECT_INHERIT_SAMPLE_ROWS
and set this phase only when it is an inheritance analyze operation.
And adding
some explanation of ROWS_SAMPLED phase about inheritance tables
how these sampled rows are calculated will provide good analyze
progress of
relation that contains child relations also.

I have added the phase called
PROGRESS_ANALYZE_PHASE_COLLECT_INH_SAMPLE_ROWS.
I have also updated the documentation.

The ANALYZE command takes long time in computing statistics phase.So I
think we can add some column or phase so that user can easily understand
the progress.
How about adding new column like "num_rows_processed" will compute the
statistics of specified column?
How about separate the computing "inheritance statistics" phase from the
computing regular "single table" statistics.
Comment?

Regards,
Vinayak Pokale
NTT Open Source Software Center

Attachments:

pg_stat_progress_analyze_v5.patchbinary/octet-stream; name=pg_stat_progress_analyze_v5.patchDownload
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 735b794..4af317f 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -157,7 +157,7 @@ static void estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
 			   FileFdwPlanState *fdw_private,
 			   Cost *startup_cost, Cost *total_cost);
 static int file_acquire_sample_rows(Relation onerel, int elevel,
-						 HeapTuple *rows, int targrows,
+						 HeapTuple *rows, void *target_info,
 						 double *totalrows, double *totaldeadrows);
 
 
@@ -1069,9 +1069,10 @@ estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
  */
 static int
 file_acquire_sample_rows(Relation onerel, int elevel,
-						 HeapTuple *rows, int targrows,
+						 HeapTuple *rows, void *target_info,
 						 double *totalrows, double *totaldeadrows)
 {
+	int			targrows = *(int *) target_info;
 	int			numrows = 0;
 	double		rowstoskip = -1;	/* -1 means not set yet */
 	ReservoirStateData rstate;
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index e8cb2d0..215aa7a 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -391,7 +391,7 @@ static void process_query_params(ExprContext *econtext,
 					 List *param_exprs,
 					 const char **param_values);
 static int postgresAcquireSampleRowsFunc(Relation relation, int elevel,
-							  HeapTuple *rows, int targrows,
+							  HeapTuple *rows, void *target_info,
 							  double *totalrows,
 							  double *totaldeadrows);
 static void analyze_row_processor(PGresult *res, int row,
@@ -3551,10 +3551,11 @@ postgresAnalyzeForeignTable(Relation relation,
  */
 static int
 postgresAcquireSampleRowsFunc(Relation relation, int elevel,
-							  HeapTuple *rows, int targrows,
+							  HeapTuple *rows, void *target_info,
 							  double *totalrows,
 							  double *totaldeadrows)
 {
+	int		targrows = *(int *) target_info;
 	PgFdwAnalyzeState astate;
 	ForeignTable *table;
 	ForeignServer *server;
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index dcb2d33..f97e8bf 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -521,6 +521,13 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       <command>VACUUM</>, showing current progress.
       See <xref linkend='vacuum-progress-reporting'>.</entry>
      </row>
+
+     <row>
+      <entry><structname>pg_stat_progress_analyze</><indexterm><primary>pg_stat_progress_analyze</primary></indexterm></entry>
+      <entry>One row for each backend running
+      <command>ANALYZE</>, showing current progress.
+      See <xref linkend='analyze-progress-reporting'>.</entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
@@ -3095,9 +3102,8 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
 
   <para>
    <productname>PostgreSQL</> has the ability to report the progress of
-   certain commands during command execution.  Currently, the only command
-   which supports progress reporting is <command>VACUUM</>.  This may be
-   expanded in the future.
+   certain commands during command execution.  Currently, the supported progress 
+   reporting commands are <command>VACUUM</> and <command>ANALYZE</>.
   </para>
 
  <sect2 id="vacuum-progress-reporting">
@@ -3287,7 +3293,131 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
    </tbody>
    </tgroup>
   </table>
+ </sect2>
+
+ <sect2 id="analyze-progress-reporting">
+  <title>ANALYZE Progress Reporting</title>
+
+  <para>
+   Whenever <command>ANALYZE</> is running, the
+   <structname>pg_stat_progress_analyze</structname> view will contain
+   one row for each backend that is currently analyzing.
+   The tables below describe the information that will be reported and
+   provide information about how to interpret it.
+  </para>
+
+  <table id="pg-stat-progress-analyze-view" xreflabel="pg_stat_progress_analyze">
+   <title><structname>pg_stat_progress_analyze</structname> View</title>
+   <tgroup cols="3">
+    <thead>
+    <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><structfield>pid</></entry>
+     <entry><type>integer</></entry>
+     <entry>Process ID of backend.</entry>
+    </row>
+    <row>
+     <entry><structfield>datid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>datname</></entry>
+     <entry><type>name</></entry>
+     <entry>Name of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>relid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the table being analyzed.</entry>
+    </row>
+    <row>
+     <entry><structfield>phase</></entry>
+     <entry><type>text</></entry>
+     <entry>
+       Current processing phase of analyze.  See <xref linkend='analyze-phases'>.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>num_target_sample_rows</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of sample rows of the relation. The sample it reads
+       is taken randomly. Its size depends on the
+       <xref linkend="guc-default-statistics-target"> parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>num_rows_sampled</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of rows sampled so far. If the table being analyzed
+       has one or more children, <command>ANALYZE</> will gather sample
+       rows twice. Once on the rows of parent table only and second time
+       on the rows of parent table with all of its children.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
+
+  <table id="analyze-phases">
+   <title>ANALYZE phases</title>
+   <tgroup cols="2">
+    <thead>
+    <row>
+      <entry>Phase</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
 
+   <tbody>
+    <row>
+     <entry><literal>initializing</literal></entry>
+     <entry>
+       <command>ANALYZE</> is preparing to collect sample rows.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>collecting sample rows</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently collecting the sample rows of
+       single relation. The sample it reads is taken randomly. Its size
+       depends on the <xref linkend="guc-default-statistics-target">
+       parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>collecting inherited sample rows</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently collecting inherited sample rows.
+       If the table being analyzed has one or more children, this phase will
+       collect sample rows of parent table and all associated child relation.
+       The sample it reads is taken randomly. Its size depends on
+       the <xref linkend="guc-default-statistics-target"> parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>computing statistics</literal></entry>
+     <entry>
+       On the collected sample <command>ANALYZE</> is currently computing some
+       statistical informations, such as the percentage of NULL values,
+       the average width of a row, the number of distinct values etc.
+       It also stores the most common values and their frequencies.
+       The number of these values depends on the value of the
+       <xref linkend="guc-default-statistics-target"> parameter.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
  </sect2>
  </sect1>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index b6552da..30ffe62 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -872,6 +872,20 @@ CREATE VIEW pg_stat_progress_vacuum AS
     FROM pg_stat_get_progress_info('VACUUM') AS S
 		LEFT JOIN pg_database D ON S.datid = D.oid;
 
+CREATE VIEW pg_stat_progress_analyze AS
+	SELECT
+		S.pid AS pid, S.datid AS datid, D.datname AS datname,
+		S.relid AS relid,
+		CASE S.param1 WHEN 0 THEN 'initializing'
+					  WHEN 1 THEN 'collecting sample rows'
+					  WHEN 2 THEN 'collecting inherited sample rows'
+					  WHEN 3 THEN 'computing statistics'
+					  END AS phase,
+		S.param2 AS num_target_sample_rows,
+		S.param3 AS num_rows_sampled
+    FROM pg_stat_get_progress_info('ANALYZE') AS S
+		LEFT JOIN pg_database D ON S.datid = D.oid;
+
 CREATE VIEW pg_user_mappings AS
     SELECT
         U.oid       AS umid,
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index b91df98..7bcb519 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -31,6 +31,7 @@
 #include "commands/dbcommands.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "commands/progress.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -70,6 +71,12 @@ typedef struct AnlIndexData
 /* Default statistics target (GUC parameter) */
 int			default_statistics_target = 100;
 
+typedef struct AcquireSampleRowsInfo
+{
+	int		rows_collected;
+	int		rows_to_collect;
+} AcquireSampleRowsInfo;
+
 /* A few variables that don't seem worth passing around as parameters */
 static MemoryContext anl_context = NULL;
 static BufferAccessStrategy vac_strategy;
@@ -86,7 +93,7 @@ static void compute_index_stats(Relation onerel, double totalrows,
 static VacAttrStats *examine_attribute(Relation onerel, int attnum,
 				  Node *index_expr);
 static int acquire_sample_rows(Relation onerel, int elevel,
-					HeapTuple *rows, int targrows,
+					HeapTuple *rows, void *target_info,
 					double *totalrows, double *totaldeadrows);
 static int	compare_rows(const void *a, const void *b);
 static int acquire_inherited_sample_rows(Relation onerel, int elevel,
@@ -257,10 +264,16 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
 	MyPgXact->vacuumFlags |= PROC_IN_ANALYZE;
 	LWLockRelease(ProcArrayLock);
 
+	/* Report that we are going to start analyzing onerel. */
+	pgstat_progress_start_command(PROGRESS_COMMAND_ANALYZE,
+								  RelationGetRelid(onerel));
+
 	/*
 	 * Do the normal non-recursive ANALYZE.  We can skip this for partitioned
 	 * tables, which don't contain any rows.
 	 */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+								 PROGRESS_ANALYZE_PHASE_COLLECT_SAMPLE_ROWS);
 	if (onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
 		do_analyze_rel(onerel, options, params, va_cols, acquirefunc,
 					   relpages, false, in_outer_xact, elevel);
@@ -269,8 +282,15 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
 	 * If there are child tables, do recursive ANALYZE.
 	 */
 	if (onerel->rd_rel->relhassubclass)
+	{
+		pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+							 PROGRESS_ANALYZE_PHASE_COLLECT_INH_SAMPLE_ROWS);
 		do_analyze_rel(onerel, options, params, va_cols, acquirefunc, relpages,
 					   true, in_outer_xact, elevel);
+	}
+
+	/* We're done analyzing. */
+	pgstat_progress_end_command();
 
 	/*
 	 * Close source relation now, but keep lock so that no one deletes it
@@ -487,15 +507,24 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	/*
 	 * Acquire the sample rows
 	 */
+
+	/* Report the number of target sample rows */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_TARGET_SAMPLE_ROWS,
+								 targrows);
+
 	rows = (HeapTuple *) palloc(targrows * sizeof(HeapTuple));
 	if (inh)
 		numrows = acquire_inherited_sample_rows(onerel, elevel,
 												rows, targrows,
 												&totalrows, &totaldeadrows);
 	else
+	{
+		AcquireSampleRowsInfo	target = {0, targrows};
+
 		numrows = (*acquirefunc) (onerel, elevel,
-								  rows, targrows,
+								  rows, (void *) &target,
 								  &totalrows, &totaldeadrows);
+	}
 
 	/*
 	 * Compute the statistics.  Temporary results during the calculations for
@@ -503,6 +532,10 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	 * responsible to make sure that whatever they store into the VacAttrStats
 	 * structure is allocated in anl_context.
 	 */
+
+	/* Report that statistics will now be computed. */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+								 PROGRESS_ANALYZE_PHASE_COMPUTE_STATS);
 	if (numrows > 0)
 	{
 		MemoryContext col_context,
@@ -982,7 +1015,7 @@ examine_attribute(Relation onerel, int attnum, Node *index_expr)
  */
 static int
 acquire_sample_rows(Relation onerel, int elevel,
-					HeapTuple *rows, int targrows,
+					HeapTuple *rows, void *target_info,
 					double *totalrows, double *totaldeadrows)
 {
 	int			numrows = 0;	/* # rows now in reservoir */
@@ -994,8 +1027,9 @@ acquire_sample_rows(Relation onerel, int elevel,
 	TransactionId OldestXmin;
 	BlockSamplerData bs;
 	ReservoirStateData rstate;
+	AcquireSampleRowsInfo *target = (AcquireSampleRowsInfo *) target_info;
 
-	Assert(targrows > 0);
+	Assert(target->rows_to_collect > 0);
 
 	totalblocks = RelationGetNumberOfBlocks(onerel);
 
@@ -1003,9 +1037,9 @@ acquire_sample_rows(Relation onerel, int elevel,
 	OldestXmin = GetOldestXmin(onerel, true);
 
 	/* Prepare for sampling block numbers */
-	BlockSampler_Init(&bs, totalblocks, targrows, random());
+	BlockSampler_Init(&bs, totalblocks, target->rows_to_collect, random());
 	/* Prepare for sampling rows */
-	reservoir_init_selection_state(&rstate, targrows);
+	reservoir_init_selection_state(&rstate, target->rows_to_collect);
 
 	/* Outer loop over blocks to sample */
 	while (BlockSampler_HasMore(&bs))
@@ -1142,7 +1176,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 				 * we've passed over so far, so when we fall off the end of
 				 * the relation we're done.
 				 */
-				if (numrows < targrows)
+				if (numrows < target->rows_to_collect)
 					rows[numrows++] = heap_copytuple(&targtuple);
 				else
 				{
@@ -1153,7 +1187,8 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * t.
 					 */
 					if (rowstoskip < 0)
-						rowstoskip = reservoir_get_next_S(&rstate, samplerows, targrows);
+						rowstoskip = reservoir_get_next_S(&rstate, samplerows,
+													target->rows_to_collect);
 
 					if (rowstoskip <= 0)
 					{
@@ -1161,9 +1196,10 @@ acquire_sample_rows(Relation onerel, int elevel,
 						 * Found a suitable tuple, so save it, replacing one
 						 * old tuple at random
 						 */
-						int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+						int			k = (int) (target->rows_to_collect *
+									sampler_random_fract(rstate.randstate));
 
-						Assert(k >= 0 && k < targrows);
+						Assert(k >= 0 && k < target->rows_to_collect);
 						heap_freetuple(rows[k]);
 						rows[k] = heap_copytuple(&targtuple);
 					}
@@ -1172,6 +1208,10 @@ acquire_sample_rows(Relation onerel, int elevel,
 				}
 
 				samplerows += 1;
+
+				/* Report number of rows sampled so far */
+				pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_SAMPLED,
+											 target->rows_collected + numrows);
 			}
 		}
 
@@ -1187,7 +1227,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 	 * (itempointer). It's not worth worrying about corner cases where the
 	 * tuples are already sorted.
 	 */
-	if (numrows == targrows)
+	if (numrows == target->rows_to_collect)
 		qsort((void *) rows, numrows, sizeof(HeapTuple), compare_rows);
 
 	/*
@@ -1417,13 +1457,14 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
 			childtargrows = Min(childtargrows, targrows - numrows);
 			if (childtargrows > 0)
 			{
+				AcquireSampleRowsInfo	target = {numrows, childtargrows};
 				int			childrows;
 				double		trows,
 							tdrows;
 
 				/* Fetch a random sample of the child's rows */
 				childrows = (*acquirefunc) (childrel, elevel,
-											rows + numrows, childtargrows,
+											rows + numrows, (void *) &target,
 											&trows, &tdrows);
 
 				/* We may need to convert from child's rowtype to parent's */
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index a987d0d..eb1dd63 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -465,6 +465,8 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 	/* Translate command name into command type code. */
 	if (pg_strcasecmp(cmd, "VACUUM") == 0)
 		cmdtype = PROGRESS_COMMAND_VACUUM;
+	else if (pg_strcasecmp(cmd, "ANALYZE") == 0)
+		cmdtype = PROGRESS_COMMAND_ANALYZE;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h
index 9472ecc..6718c06 100644
--- a/src/include/commands/progress.h
+++ b/src/include/commands/progress.h
@@ -34,4 +34,14 @@
 #define PROGRESS_VACUUM_PHASE_TRUNCATE			5
 #define PROGRESS_VACUUM_PHASE_FINAL_CLEANUP		6
 
+/* Progress parameters for analyze */
+#define PROGRESS_ANALYZE_PHASE					0
+#define PROGRESS_ANALYZE_NUM_TARGET_SAMPLE_ROWS	1
+#define PROGRESS_ANALYZE_NUM_ROWS_SAMPLED		2
+
+/* Phases of analyze (as advertised via PROGRESS_ANALYZE_PHASE) */
+#define PROGRESS_ANALYZE_PHASE_COLLECT_SAMPLE_ROWS		1
+#define PROGRESS_ANALYZE_PHASE_COLLECT_INH_SAMPLE_ROWS	2
+#define PROGRESS_ANALYZE_PHASE_COMPUTE_STATS			3
+
 #endif
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 6ca44f7..8349268 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -132,9 +132,10 @@ typedef void (*ExplainDirectModify_function) (ForeignScanState *node,
 													struct ExplainState *es);
 
 typedef int (*AcquireSampleRowsFunc) (Relation relation, int elevel,
-											   HeapTuple *rows, int targrows,
-												  double *totalrows,
-												  double *totaldeadrows);
+											   HeapTuple *rows,
+											   void *target_info,
+											   double *totalrows,
+											   double *totaldeadrows);
 
 typedef bool (*AnalyzeForeignTable_function) (Relation relation,
 												 AcquireSampleRowsFunc *func,
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index f2daf32..7dc3392 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -890,7 +890,8 @@ typedef enum
 typedef enum ProgressCommandType
 {
 	PROGRESS_COMMAND_INVALID,
-	PROGRESS_COMMAND_VACUUM
+	PROGRESS_COMMAND_VACUUM,
+	PROGRESS_COMMAND_ANALYZE
 } ProgressCommandType;
 
 #define PGSTAT_NUM_PROGRESS_PARAM	10
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index bd13ae6..7ab2832 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1795,6 +1795,21 @@ pg_stat_database_conflicts| SELECT d.oid AS datid,
     pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin,
     pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock
    FROM pg_database d;
+pg_stat_progress_analyze| SELECT s.pid,
+    s.datid,
+    d.datname,
+    s.relid,
+        CASE s.param1
+            WHEN 0 THEN 'initializing'::text
+            WHEN 1 THEN 'collecting sample rows'::text
+            WHEN 2 THEN 'collecting inherited sample rows'::text
+            WHEN 3 THEN 'computing statistics'::text
+            ELSE NULL::text
+        END AS phase,
+    s.param2 AS num_target_sample_rows,
+    s.param3 AS num_rows_sampled
+   FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)
+     LEFT JOIN pg_database d ON ((s.datid = d.oid)));
 pg_stat_progress_vacuum| SELECT s.pid,
     s.datid,
     d.datname,
#31Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: vinayak (#30)
Re: ANALYZE command progress checker

On Wed, Mar 22, 2017 at 8:11 PM, vinayak <Pokale_Vinayak_q3@lab.ntt.co.jp>
wrote:

On 2017/03/21 21:25, Haribabu Kommi wrote:

On Tue, Mar 21, 2017 at 3:41 PM, vinayak <Pokale_Vinayak_q3@lab.ntt.co.jp>
wrote:

Thank you for testing the patch on Windows platform.

Thanks for the updated patch.

It works good for a normal relation. But for a relation that contains
child tables,
the PROGRESS_ANALYZE_NUM_ROWS_SAMPLED produces wrong results.

Thank you for reviewing the patch.
The attached patch implements a way to report sample rows count from
acquire_sample_rows() even if called for child tables.

How about adding another phase called PROGRESS_ANALYZE_PHASE_
COLLECT_INHERIT_SAMPLE_ROWS
and set this phase only when it is an inheritance analyze operation. And
adding
some explanation of ROWS_SAMPLED phase about inheritance tables
how these sampled rows are calculated will provide good analyze progress of
relation that contains child relations also.

I have added the phase called PROGRESS_ANALYZE_PHASE_
COLLECT_INH_SAMPLE_ROWS.
I have also updated the documentation.

Thanks for the updated patch. I will check it.

The ANALYZE command takes long time in computing statistics phase.So I
think we can add some column or phase so that user can easily understand
the progress.
How about adding new column like "num_rows_processed" will compute the
statistics of specified column?

I prefer a column with rows processed instead of a phase.
Because we already set the phase of compute stats and showing
the progress there will number of rows processed will be good.

How about separate the computing "inheritance statistics" phase from the
computing regular "single table" statistics.
Comment?

Yes, this will be good to show both that states of inheritance of sampled
rows and
compute inheritance stats, so that it will be clearly visible to the user
the current
status.

Regards,
Hari Babu
Fujitsu Australia

#32vinayak
Pokale_Vinayak_q3@lab.ntt.co.jp
In reply to: Haribabu Kommi (#31)
1 attachment(s)
Re: ANALYZE command progress checker

On 2017/03/23 15:04, Haribabu Kommi wrote:

On Wed, Mar 22, 2017 at 8:11 PM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp
<mailto:Pokale_Vinayak_q3@lab.ntt.co.jp>> wrote:

On 2017/03/21 21:25, Haribabu Kommi wrote:

On Tue, Mar 21, 2017 at 3:41 PM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp
<mailto:Pokale_Vinayak_q3@lab.ntt.co.jp>> wrote:

Thank you for testing the patch on Windows platform.

Thanks for the updated patch.

It works good for a normal relation. But for a relation that
contains child tables,
the PROGRESS_ANALYZE_NUM_ROWS_SAMPLED produces wrong results.

Thank you for reviewing the patch.
The attached patch implements a way to report sample rows count
from acquire_sample_rows() even if called for child tables.

How about adding another phase called
PROGRESS_ANALYZE_PHASE_COLLECT_INHERIT_SAMPLE_ROWS
and set this phase only when it is an inheritance analyze
operation. And adding
some explanation of ROWS_SAMPLED phase about inheritance tables
how these sampled rows are calculated will provide good analyze
progress of
relation that contains child relations also.

I have added the phase called
PROGRESS_ANALYZE_PHASE_COLLECT_INH_SAMPLE_ROWS.
I have also updated the documentation.

Thanks for the updated patch. I will check it.

The ANALYZE command takes long time in computing statistics
phase.So I think we can add some column or phase so that user can
easily understand the progress.
How about adding new column like "num_rows_processed" will compute
the statistics of specified column?

I prefer a column with rows processed instead of a phase.
Because we already set the phase of compute stats and showing
the progress there will number of rows processed will be good.

How about separate the computing "inheritance statistics" phase
from the computing regular "single table" statistics.
Comment?

Yes, this will be good to show both that states of inheritance of
sampled rows and
compute inheritance stats, so that it will be clearly visible to the
user the current
status.

I have updated the patch.
I have added column "num_rows_processed" and phase "computing inherited
statistics" in the view.

=# \d+ pg_stat_progress_analyze
View "pg_catalog.pg_stat_progress_analyze"
Column | Type | Collation | Nullable | Default |
Storage | Description
------------------------+---------+-----------+----------+---------+----------+-------------
pid | integer | | | |
plain |
datid | oid | | | |
plain |
datname | name | | | |
plain |
relid | oid | | | |
plain |
phase | text | | | |
extended |
num_target_sample_rows | bigint | | | |
plain |
num_rows_sampled | bigint | | | |
plain |
num_rows_processed | bigint | | | |
plain |
View definition:
SELECT s.pid,
s.datid,
d.datname,
s.relid,
CASE s.param1
WHEN 0 THEN 'initializing'::text
WHEN 1 THEN 'collecting sample rows'::text
WHEN 2 THEN 'collecting inherited sample rows'::text
WHEN 3 THEN 'computing statistics'::text
WHEN 4 THEN 'computing inherited statistics'::text
ELSE NULL::text
END AS phase,
s.param2 AS num_target_sample_rows,
s.param3 AS num_rows_sampled,
s.param4 AS num_rows_processed
FROM pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid,
param1, param2, param3, param4, param5, param6, param7, param8, param9,
param10)
LEFT JOIN pg_database d ON s.datid = d.oid;

Regards,
Vinayak Pokale
NTT Open Source Software Center

Attachments:

pg_stat_progress_analyze_v6.patchbinary/octet-stream; name=pg_stat_progress_analyze_v6.patchDownload
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 277639f..75c3473 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -157,7 +157,7 @@ static void estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
 			   FileFdwPlanState *fdw_private,
 			   Cost *startup_cost, Cost *total_cost);
 static int file_acquire_sample_rows(Relation onerel, int elevel,
-						 HeapTuple *rows, int targrows,
+						 HeapTuple *rows, void *target_info,
 						 double *totalrows, double *totaldeadrows);
 
 
@@ -1071,9 +1071,10 @@ estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
  */
 static int
 file_acquire_sample_rows(Relation onerel, int elevel,
-						 HeapTuple *rows, int targrows,
+						 HeapTuple *rows, void *target_info,
 						 double *totalrows, double *totaldeadrows)
 {
+	int			targrows = *(int *) target_info;
 	int			numrows = 0;
 	double		rowstoskip = -1;	/* -1 means not set yet */
 	ReservoirStateData rstate;
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index e8cb2d0..215aa7a 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -391,7 +391,7 @@ static void process_query_params(ExprContext *econtext,
 					 List *param_exprs,
 					 const char **param_values);
 static int postgresAcquireSampleRowsFunc(Relation relation, int elevel,
-							  HeapTuple *rows, int targrows,
+							  HeapTuple *rows, void *target_info,
 							  double *totalrows,
 							  double *totaldeadrows);
 static void analyze_row_processor(PGresult *res, int row,
@@ -3551,10 +3551,11 @@ postgresAnalyzeForeignTable(Relation relation,
  */
 static int
 postgresAcquireSampleRowsFunc(Relation relation, int elevel,
-							  HeapTuple *rows, int targrows,
+							  HeapTuple *rows, void *target_info,
 							  double *totalrows,
 							  double *totaldeadrows)
 {
+	int		targrows = *(int *) target_info;
 	PgFdwAnalyzeState astate;
 	ForeignTable *table;
 	ForeignServer *server;
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index e930731..a5b2f7f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -521,6 +521,13 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       <command>VACUUM</>, showing current progress.
       See <xref linkend='vacuum-progress-reporting'>.</entry>
      </row>
+
+     <row>
+      <entry><structname>pg_stat_progress_analyze</><indexterm><primary>pg_stat_progress_analyze</primary></indexterm></entry>
+      <entry>One row for each backend running
+      <command>ANALYZE</>, showing current progress.
+      See <xref linkend='analyze-progress-reporting'>.</entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
@@ -3175,9 +3182,8 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
 
   <para>
    <productname>PostgreSQL</> has the ability to report the progress of
-   certain commands during command execution.  Currently, the only command
-   which supports progress reporting is <command>VACUUM</>.  This may be
-   expanded in the future.
+   certain commands during command execution.  Currently, the supported progress 
+   reporting commands are <command>VACUUM</> and <command>ANALYZE</>.
   </para>
 
  <sect2 id="vacuum-progress-reporting">
@@ -3367,7 +3373,149 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
    </tbody>
    </tgroup>
   </table>
+ </sect2>
+
+ <sect2 id="analyze-progress-reporting">
+  <title>ANALYZE Progress Reporting</title>
+
+  <para>
+   Whenever <command>ANALYZE</> is running, the
+   <structname>pg_stat_progress_analyze</structname> view will contain
+   one row for each backend that is currently analyzing.
+   The tables below describe the information that will be reported and
+   provide information about how to interpret it.
+  </para>
+
+  <table id="pg-stat-progress-analyze-view" xreflabel="pg_stat_progress_analyze">
+   <title><structname>pg_stat_progress_analyze</structname> View</title>
+   <tgroup cols="3">
+    <thead>
+    <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
 
+   <tbody>
+    <row>
+     <entry><structfield>pid</></entry>
+     <entry><type>integer</></entry>
+     <entry>Process ID of backend.</entry>
+    </row>
+    <row>
+     <entry><structfield>datid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>datname</></entry>
+     <entry><type>name</></entry>
+     <entry>Name of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>relid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the table being analyzed.</entry>
+    </row>
+    <row>
+     <entry><structfield>phase</></entry>
+     <entry><type>text</></entry>
+     <entry>
+       Current processing phase of analyze.  See <xref linkend='analyze-phases'>.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>num_target_sample_rows</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of sample rows of the relation. The sample it reads
+       is taken randomly. Its size depends on the
+       <xref linkend="guc-default-statistics-target"> parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>num_rows_sampled</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of rows sampled so far. If the table being analyzed
+       has one or more children, <command>ANALYZE</> will gather sample
+       rows twice. Once on the rows of parent table only and second time
+       on the rows of parent table with all of its children.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>num_rows_processed</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of rows processed so far.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
+
+  <table id="analyze-phases">
+   <title>ANALYZE phases</title>
+   <tgroup cols="2">
+    <thead>
+    <row>
+      <entry>Phase</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><literal>initializing</literal></entry>
+     <entry>
+       <command>ANALYZE</> is preparing to collect sample rows.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>collecting sample rows</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently collecting the sample rows of
+       single relation. The sample it reads is taken randomly. Its size
+       depends on the <xref linkend="guc-default-statistics-target">
+       parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>collecting inherited sample rows</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently collecting inherited sample rows.
+       If the table being analyzed has one or more children, this phase will
+       collect sample rows of parent table and all associated child relation.
+       The sample it reads is taken randomly. Its size depends on
+       the <xref linkend="guc-default-statistics-target"> parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>computing statistics</literal></entry>
+     <entry>
+       On the collected sample <command>ANALYZE</> is currently computing some
+       statistical informations, such as the percentage of NULL values,
+       the average width of a row, the number of distinct values etc.
+       It also stores the most common values and their frequencies.
+       The number of these values depends on the value of the
+       <xref linkend="guc-default-statistics-target"> parameter.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>computing inherited statistics</literal></entry>
+     <entry>
+       On the collected sample <command>ANALYZE</> is currently computing some
+       statistical informations of child tables, such as the percentage of NULL values,
+       the average width of a row, the number of distinct values etc.
+       It also stores the most common values and their frequencies.
+       The number of these values depends on the value of the
+       <xref linkend="guc-default-statistics-target"> parameter.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
  </sect2>
  </sect1>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 80d1429..8b6e4c0 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -876,6 +876,22 @@ CREATE VIEW pg_stat_progress_vacuum AS
     FROM pg_stat_get_progress_info('VACUUM') AS S
 		LEFT JOIN pg_database D ON S.datid = D.oid;
 
+CREATE VIEW pg_stat_progress_analyze AS
+	SELECT
+		S.pid AS pid, S.datid AS datid, D.datname AS datname,
+		S.relid AS relid,
+		CASE S.param1 WHEN 0 THEN 'initializing'
+					  WHEN 1 THEN 'collecting sample rows'
+					  WHEN 2 THEN 'collecting inherited sample rows'
+					  WHEN 3 THEN 'computing statistics'
+					  WHEN 4 THEN 'computing inherited statistics'
+					  END AS phase,
+		S.param2 AS num_target_sample_rows,
+		S.param3 AS num_rows_sampled,
+		S.param4 AS num_rows_processed
+    FROM pg_stat_get_progress_info('ANALYZE') AS S
+		LEFT JOIN pg_database D ON S.datid = D.oid;
+
 CREATE VIEW pg_user_mappings AS
     SELECT
         U.oid       AS umid,
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 055338f..8e18c23 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -31,6 +31,7 @@
 #include "commands/dbcommands.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "commands/progress.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -70,6 +71,12 @@ typedef struct AnlIndexData
 /* Default statistics target (GUC parameter) */
 int			default_statistics_target = 100;
 
+typedef struct AcquireSampleRowsInfo
+{
+	int		rows_collected;
+	int		rows_to_collect;
+} AcquireSampleRowsInfo;
+
 /* A few variables that don't seem worth passing around as parameters */
 static MemoryContext anl_context = NULL;
 static BufferAccessStrategy vac_strategy;
@@ -86,7 +93,7 @@ static void compute_index_stats(Relation onerel, double totalrows,
 static VacAttrStats *examine_attribute(Relation onerel, int attnum,
 				  Node *index_expr);
 static int acquire_sample_rows(Relation onerel, int elevel,
-					HeapTuple *rows, int targrows,
+					HeapTuple *rows, void *target_info,
 					double *totalrows, double *totaldeadrows);
 static int	compare_rows(const void *a, const void *b);
 static int acquire_inherited_sample_rows(Relation onerel, int elevel,
@@ -257,10 +264,16 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
 	MyPgXact->vacuumFlags |= PROC_IN_ANALYZE;
 	LWLockRelease(ProcArrayLock);
 
+	/* Report that we are going to start analyzing onerel. */
+	pgstat_progress_start_command(PROGRESS_COMMAND_ANALYZE,
+								  RelationGetRelid(onerel));
+
 	/*
 	 * Do the normal non-recursive ANALYZE.  We can skip this for partitioned
 	 * tables, which don't contain any rows.
 	 */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+								 PROGRESS_ANALYZE_PHASE_COLLECT_SAMPLE_ROWS);
 	if (onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
 		do_analyze_rel(onerel, options, params, va_cols, acquirefunc,
 					   relpages, false, in_outer_xact, elevel);
@@ -269,8 +282,15 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
 	 * If there are child tables, do recursive ANALYZE.
 	 */
 	if (onerel->rd_rel->relhassubclass)
+	{
+		pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+							 PROGRESS_ANALYZE_PHASE_COLLECT_INH_SAMPLE_ROWS);
 		do_analyze_rel(onerel, options, params, va_cols, acquirefunc, relpages,
 					   true, in_outer_xact, elevel);
+	}
+
+	/* We're done analyzing. */
+	pgstat_progress_end_command();
 
 	/*
 	 * Close source relation now, but keep lock so that no one deletes it
@@ -487,15 +507,24 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	/*
 	 * Acquire the sample rows
 	 */
+
+	/* Report the number of target sample rows */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_TARGET_SAMPLE_ROWS,
+								 targrows);
+
 	rows = (HeapTuple *) palloc(targrows * sizeof(HeapTuple));
 	if (inh)
 		numrows = acquire_inherited_sample_rows(onerel, elevel,
 												rows, targrows,
 												&totalrows, &totaldeadrows);
 	else
+	{
+		AcquireSampleRowsInfo	target = {0, targrows};
+
 		numrows = (*acquirefunc) (onerel, elevel,
-								  rows, targrows,
+								  rows, (void *) &target,
 								  &totalrows, &totaldeadrows);
+	}
 
 	/*
 	 * Compute the statistics.  Temporary results during the calculations for
@@ -503,6 +532,15 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	 * responsible to make sure that whatever they store into the VacAttrStats
 	 * structure is allocated in anl_context.
 	 */
+
+	/* Report that statistics will now be computed. */
+	if (inh)
+		pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+									 PROGRESS_ANALYZE_PHASE_COMPUTE_INH_STATS);
+	else
+		pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+									 PROGRESS_ANALYZE_PHASE_COMPUTE_STATS);
+
 	if (numrows > 0)
 	{
 		MemoryContext col_context,
@@ -540,6 +578,9 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 			}
 
 			MemoryContextResetAndDeleteChildren(col_context);
+
+			/* Reset rows processed to zero for the next column */
+			pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_PROCESSED, 0);
 		}
 
 		if (hasindex)
@@ -982,7 +1023,7 @@ examine_attribute(Relation onerel, int attnum, Node *index_expr)
  */
 static int
 acquire_sample_rows(Relation onerel, int elevel,
-					HeapTuple *rows, int targrows,
+					HeapTuple *rows, void *target_info,
 					double *totalrows, double *totaldeadrows)
 {
 	int			numrows = 0;	/* # rows now in reservoir */
@@ -994,8 +1035,9 @@ acquire_sample_rows(Relation onerel, int elevel,
 	TransactionId OldestXmin;
 	BlockSamplerData bs;
 	ReservoirStateData rstate;
+	AcquireSampleRowsInfo *target = (AcquireSampleRowsInfo *) target_info;
 
-	Assert(targrows > 0);
+	Assert(target->rows_to_collect > 0);
 
 	totalblocks = RelationGetNumberOfBlocks(onerel);
 
@@ -1003,9 +1045,9 @@ acquire_sample_rows(Relation onerel, int elevel,
 	OldestXmin = GetOldestXmin(onerel, PROCARRAY_FLAGS_VACUUM);
 
 	/* Prepare for sampling block numbers */
-	BlockSampler_Init(&bs, totalblocks, targrows, random());
+	BlockSampler_Init(&bs, totalblocks, target->rows_to_collect, random());
 	/* Prepare for sampling rows */
-	reservoir_init_selection_state(&rstate, targrows);
+	reservoir_init_selection_state(&rstate, target->rows_to_collect);
 
 	/* Outer loop over blocks to sample */
 	while (BlockSampler_HasMore(&bs))
@@ -1142,7 +1184,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 				 * we've passed over so far, so when we fall off the end of
 				 * the relation we're done.
 				 */
-				if (numrows < targrows)
+				if (numrows < target->rows_to_collect)
 					rows[numrows++] = heap_copytuple(&targtuple);
 				else
 				{
@@ -1153,7 +1195,8 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * t.
 					 */
 					if (rowstoskip < 0)
-						rowstoskip = reservoir_get_next_S(&rstate, samplerows, targrows);
+						rowstoskip = reservoir_get_next_S(&rstate, samplerows,
+													target->rows_to_collect);
 
 					if (rowstoskip <= 0)
 					{
@@ -1161,9 +1204,10 @@ acquire_sample_rows(Relation onerel, int elevel,
 						 * Found a suitable tuple, so save it, replacing one
 						 * old tuple at random
 						 */
-						int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+						int			k = (int) (target->rows_to_collect *
+									sampler_random_fract(rstate.randstate));
 
-						Assert(k >= 0 && k < targrows);
+						Assert(k >= 0 && k < target->rows_to_collect);
 						heap_freetuple(rows[k]);
 						rows[k] = heap_copytuple(&targtuple);
 					}
@@ -1172,6 +1216,10 @@ acquire_sample_rows(Relation onerel, int elevel,
 				}
 
 				samplerows += 1;
+
+				/* Report number of rows sampled so far */
+				pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_SAMPLED,
+											 target->rows_collected + numrows);
 			}
 		}
 
@@ -1187,7 +1235,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 	 * (itempointer). It's not worth worrying about corner cases where the
 	 * tuples are already sorted.
 	 */
-	if (numrows == targrows)
+	if (numrows == target->rows_to_collect)
 		qsort((void *) rows, numrows, sizeof(HeapTuple), compare_rows);
 
 	/*
@@ -1417,13 +1465,14 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
 			childtargrows = Min(childtargrows, targrows - numrows);
 			if (childtargrows > 0)
 			{
+				AcquireSampleRowsInfo	target = {numrows, childtargrows};
 				int			childrows;
 				double		trows,
 							tdrows;
 
 				/* Fetch a random sample of the child's rows */
 				childrows = (*acquirefunc) (childrel, elevel,
-											rows + numrows, childtargrows,
+											rows + numrows, (void *) &target,
 											&trows, &tdrows);
 
 				/* We may need to convert from child's rowtype to parent's */
@@ -1828,6 +1877,9 @@ compute_trivial_stats(VacAttrStatsP stats,
 
 		vacuum_delay_point();
 
+		/* Update the number of processed rows. */
+		pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_PROCESSED, i+1);
+
 		value = fetchfunc(stats, i, &isnull);
 
 		/* Check for null/nonnull */
@@ -1944,6 +1996,9 @@ compute_distinct_stats(VacAttrStatsP stats,
 
 		vacuum_delay_point();
 
+		/* Update the number of processed rows. */
+		pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_PROCESSED, i+1);
+
 		value = fetchfunc(stats, i, &isnull);
 
 		/* Check for null/nonnull */
@@ -2301,6 +2356,9 @@ compute_scalar_stats(VacAttrStatsP stats,
 
 		vacuum_delay_point();
 
+		/* Update the number of processed rows. */
+		pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_PROCESSED, i+1);
+
 		value = fetchfunc(stats, i, &isnull);
 
 		/* Check for null/nonnull */
diff --git a/src/backend/tsearch/ts_typanalyze.c b/src/backend/tsearch/ts_typanalyze.c
index 017435c..e9f85a6 100644
--- a/src/backend/tsearch/ts_typanalyze.c
+++ b/src/backend/tsearch/ts_typanalyze.c
@@ -18,7 +18,7 @@
 #include "commands/vacuum.h"
 #include "tsearch/ts_type.h"
 #include "utils/builtins.h"
-
+#include "commands/progress.h"
 
 /* A hash key for lexemes */
 typedef struct
@@ -206,6 +206,10 @@ compute_tsvector_stats(VacAttrStats *stats,
 
 		vacuum_delay_point();
 
+		/* Update the number of processed rows. */
+		pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_PROCESSED,
+									 vector_no+1);
+
 		value = fetchfunc(stats, vector_no, &isnull);
 
 		/*
diff --git a/src/backend/utils/adt/array_typanalyze.c b/src/backend/utils/adt/array_typanalyze.c
index 85b7a43..89f4766 100644
--- a/src/backend/utils/adt/array_typanalyze.c
+++ b/src/backend/utils/adt/array_typanalyze.c
@@ -22,7 +22,7 @@
 #include "utils/datum.h"
 #include "utils/lsyscache.h"
 #include "utils/typcache.h"
-
+#include "commands/progress.h"
 
 /*
  * To avoid consuming too much memory, IO and CPU load during analysis, and/or
@@ -318,6 +318,10 @@ compute_array_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
 
 		vacuum_delay_point();
 
+		/* Update the number of processed rows. */
+		pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_PROCESSED,
+									 array_no+1);
+
 		value = fetchfunc(stats, array_no, &isnull);
 		if (isnull)
 		{
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index a987d0d..eb1dd63 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -465,6 +465,8 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 	/* Translate command name into command type code. */
 	if (pg_strcasecmp(cmd, "VACUUM") == 0)
 		cmdtype = PROGRESS_COMMAND_VACUUM;
+	else if (pg_strcasecmp(cmd, "ANALYZE") == 0)
+		cmdtype = PROGRESS_COMMAND_ANALYZE;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
diff --git a/src/backend/utils/adt/rangetypes_typanalyze.c b/src/backend/utils/adt/rangetypes_typanalyze.c
index a8d585c..a63581d 100644
--- a/src/backend/utils/adt/rangetypes_typanalyze.c
+++ b/src/backend/utils/adt/rangetypes_typanalyze.c
@@ -29,6 +29,7 @@
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/rangetypes.h"
+#include "commands/progress.h"
 
 static int	float8_qsort_cmp(const void *a1, const void *a2);
 static int	range_bound_qsort_cmp(const void *a1, const void *a2, void *arg);
@@ -129,6 +130,9 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
 
 		vacuum_delay_point();
 
+		/* Update the number of processed rows. */
+		pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_PROCESSED, range_no+1);
+
 		value = fetchfunc(stats, range_no, &isnull);
 		if (isnull)
 		{
diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h
index 9472ecc..acad833 100644
--- a/src/include/commands/progress.h
+++ b/src/include/commands/progress.h
@@ -34,4 +34,16 @@
 #define PROGRESS_VACUUM_PHASE_TRUNCATE			5
 #define PROGRESS_VACUUM_PHASE_FINAL_CLEANUP		6
 
+/* Progress parameters for analyze */
+#define PROGRESS_ANALYZE_PHASE					0
+#define PROGRESS_ANALYZE_NUM_TARGET_SAMPLE_ROWS	1
+#define PROGRESS_ANALYZE_NUM_ROWS_SAMPLED		2
+#define PROGRESS_ANALYZE_NUM_ROWS_PROCESSED		3
+
+/* Phases of analyze (as advertised via PROGRESS_ANALYZE_PHASE) */
+#define PROGRESS_ANALYZE_PHASE_COLLECT_SAMPLE_ROWS		1
+#define PROGRESS_ANALYZE_PHASE_COLLECT_INH_SAMPLE_ROWS	2
+#define PROGRESS_ANALYZE_PHASE_COMPUTE_STATS			3
+#define PROGRESS_ANALYZE_PHASE_COMPUTE_INH_STATS		4
+
 #endif
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 6ca44f7..8349268 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -132,9 +132,10 @@ typedef void (*ExplainDirectModify_function) (ForeignScanState *node,
 													struct ExplainState *es);
 
 typedef int (*AcquireSampleRowsFunc) (Relation relation, int elevel,
-											   HeapTuple *rows, int targrows,
-												  double *totalrows,
-												  double *totaldeadrows);
+											   HeapTuple *rows,
+											   void *target_info,
+											   double *totalrows,
+											   double *totaldeadrows);
 
 typedef bool (*AnalyzeForeignTable_function) (Relation relation,
 												 AcquireSampleRowsFunc *func,
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 2015625..3ceeac0 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -892,7 +892,8 @@ typedef enum
 typedef enum ProgressCommandType
 {
 	PROGRESS_COMMAND_INVALID,
-	PROGRESS_COMMAND_VACUUM
+	PROGRESS_COMMAND_VACUUM,
+	PROGRESS_COMMAND_ANALYZE
 } ProgressCommandType;
 
 #define PGSTAT_NUM_PROGRESS_PARAM	10
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index c4c8450..5b211f4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1795,6 +1795,23 @@ pg_stat_database_conflicts| SELECT d.oid AS datid,
     pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin,
     pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock
    FROM pg_database d;
+pg_stat_progress_analyze| SELECT s.pid,
+    s.datid,
+    d.datname,
+    s.relid,
+        CASE s.param1
+            WHEN 0 THEN 'initializing'::text
+            WHEN 1 THEN 'collecting sample rows'::text
+            WHEN 2 THEN 'collecting inherited sample rows'::text
+            WHEN 3 THEN 'computing statistics'::text
+            WHEN 4 THEN 'computing inherited statistics'::text
+            ELSE NULL::text
+        END AS phase,
+    s.param2 AS num_target_sample_rows,
+    s.param3 AS num_rows_sampled,
+    s.param4 AS num_rows_processed
+   FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)
+     LEFT JOIN pg_database d ON ((s.datid = d.oid)));
 pg_stat_progress_vacuum| SELECT s.pid,
     s.datid,
     d.datname,
#33Robert Haas
robertmhaas@gmail.com
In reply to: vinayak (#32)
Re: ANALYZE command progress checker

On Fri, Mar 24, 2017 at 3:41 AM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

I have updated the patch.

You can't change the definition of AcquireSampleRowsFunc without
updating the documentation in fdwhandler.sgml, but I think I don't
immediately understand why that's a thing we want to do at all if
neither file_fdw nor postgres_fdw are going to use the additional
argument. It seems to be entirely pointless because the new value
being passed down is always zero and even if the callee were to update
it, do_analyze_rel() would just ignore the change on return. Am I
missing something here? Also, the whitespace-only changes to fdwapi.h
related to AcquireSampleRowsFunc going to get undone by pgindent, so
even if there's some reason to change that you should leave the lines
that don't need changing untouched.

+            /* Reset rows processed to zero for the next column */
+            pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_PROCESSED,
0);

This seems like a bad design. If you see a value other than zero in
this field, you still won't know how much progress analyze has made,
because you don't know which column you're processing. I also feel
like you're not paying enough attention to a key point here that I
made in my last review, which is that you need to look at where
ANALYZE actually spends most of the time. If the process of computing
the per-row statistics consumes significant CPU time, then maybe
there's some point in trying to instrument it, but does it? Unless
you know the answer to that question, you don't know whether there's
even a point to trying to instrument this.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#34vinayak
Pokale_Vinayak_q3@lab.ntt.co.jp
In reply to: Robert Haas (#33)
1 attachment(s)
Re: ANALYZE command progress checker

On 2017/03/25 4:30, Robert Haas wrote:

On Fri, Mar 24, 2017 at 3:41 AM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

I have updated the patch.

You can't change the definition of AcquireSampleRowsFunc without
updating the documentation in fdwhandler.sgml, but I think I don't
immediately understand why that's a thing we want to do at all if
neither file_fdw nor postgres_fdw are going to use the additional
argument. It seems to be entirely pointless because the new value
being passed down is always zero and even if the callee were to update
it, do_analyze_rel() would just ignore the change on return. Am I
missing something here? Also, the whitespace-only changes to fdwapi.h
related to AcquireSampleRowsFunc going to get undone by pgindent, so
even if there's some reason to change that you should leave the lines
that don't need changing untouched.

I Understood that we could not change the definition of
AcquireSampleRowsFunc without updating the documentation in fdwhandler.sgml.
In the last patch I have modified the definition of
AcquireSampleRowsFunc to handle the inheritance case.
If the table being analyzed has one or more children, ANALYZE will
gather statistics twice:
once on the rows of parent table only and second on the rows of the
parent table with all of its children.
So while reporting the progress the value of number of target sample
rows and number of rows sampled is mismatched.
For single relation it works fine.

In the attached patch I have not change the definition of
AcquireSampleRowsFunc.
I have updated inheritance case in the the documentation.

+            /* Reset rows processed to zero for the next column */
+            pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_PROCESSED,
0);

This seems like a bad design. If you see a value other than zero in
this field, you still won't know how much progress analyze has made,
because you don't know which column you're processing. I also feel
like you're not paying enough attention to a key point here that I
made in my last review, which is that you need to look at where
ANALYZE actually spends most of the time. If the process of computing
the per-row statistics consumes significant CPU time, then maybe
there's some point in trying to instrument it, but does it? Unless
you know the answer to that question, you don't know whether there's
even a point to trying to instrument this.

Understood. The computing statistics phase takes long time so I am
looking at the code.

Regards,
Vinayak Pokale
NTT Open Source Software Center

Attachments:

pg_stat_progress_analyze_v7.patchbinary/octet-stream; name=pg_stat_progress_analyze_v7.patchDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 9856968..dbec195 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -521,6 +521,13 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       <command>VACUUM</>, showing current progress.
       See <xref linkend='vacuum-progress-reporting'>.</entry>
      </row>
+
+     <row>
+      <entry><structname>pg_stat_progress_analyze</><indexterm><primary>pg_stat_progress_analyze</primary></indexterm></entry>
+      <entry>One row for each backend running
+      <command>ANALYZE</>, showing current progress.
+      See <xref linkend='analyze-progress-reporting'>.</entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
@@ -3186,9 +3193,8 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
 
   <para>
    <productname>PostgreSQL</> has the ability to report the progress of
-   certain commands during command execution.  Currently, the only command
-   which supports progress reporting is <command>VACUUM</>.  This may be
-   expanded in the future.
+   certain commands during command execution.  Currently, the supported progress 
+   reporting commands are <command>VACUUM</> and <command>ANALYZE</>.
   </para>
 
  <sect2 id="vacuum-progress-reporting">
@@ -3378,7 +3384,143 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
    </tbody>
    </tgroup>
   </table>
+ </sect2>
+
+ <sect2 id="analyze-progress-reporting">
+  <title>ANALYZE Progress Reporting</title>
+
+  <para>
+   Whenever <command>ANALYZE</> is running, the
+   <structname>pg_stat_progress_analyze</structname> view will contain
+   one row for each backend that is currently analyzing.
+   The tables below describe the information that will be reported and
+   provide information about how to interpret it.
+  </para>
+
+  <table id="pg-stat-progress-analyze-view" xreflabel="pg_stat_progress_analyze">
+   <title><structname>pg_stat_progress_analyze</structname> View</title>
+   <tgroup cols="3">
+    <thead>
+    <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><structfield>pid</></entry>
+     <entry><type>integer</></entry>
+     <entry>Process ID of backend.</entry>
+    </row>
+    <row>
+     <entry><structfield>datid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>datname</></entry>
+     <entry><type>name</></entry>
+     <entry>Name of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>relid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the table being analyzed.</entry>
+    </row>
+    <row>
+     <entry><structfield>phase</></entry>
+     <entry><type>text</></entry>
+     <entry>
+       Current processing phase of analyze.  See <xref linkend='analyze-phases'>.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>num_target_sample_rows</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of sample rows of the relation. The sample it reads
+       is taken randomly. Its size depends on the
+       <xref linkend="guc-default-statistics-target"> parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>num_rows_sampled</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of rows sampled so far. If the table being analyzed
+       has one or more children, the target sample rows should be different
+       w.r.t number of rows sampled because <command>ANALYZE</> will gather 
+       sample rows twice. Once on the rows of parent table only and second 
+       time on the rows of parent table with all of its children.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
 
+  <table id="analyze-phases">
+   <title>ANALYZE phases</title>
+   <tgroup cols="2">
+    <thead>
+    <row>
+      <entry>Phase</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><literal>initializing</literal></entry>
+     <entry>
+       <command>ANALYZE</> is preparing to collect sample rows.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>collecting sample rows</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently collecting the sample rows of
+       single relation. The sample it reads is taken randomly. Its size
+       depends on the <xref linkend="guc-default-statistics-target">
+       parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>collecting inherited sample rows</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently collecting inherited sample rows.
+       If the table being analyzed has one or more children, this phase will
+       collect sample rows of parent table and all associated child relation.
+       The sample it reads is taken randomly. Its size depends on
+       the <xref linkend="guc-default-statistics-target"> parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>computing statistics</literal></entry>
+     <entry>
+       On the collected sample <command>ANALYZE</> is currently computing some
+       statistical informations, such as the percentage of NULL values,
+       the average width of a row, the number of distinct values etc.
+       It also stores the most common values and their frequencies.
+       The number of these values depends on the value of the
+       <xref linkend="guc-default-statistics-target"> parameter.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>computing inherited statistics</literal></entry>
+     <entry>
+       On the collected sample <command>ANALYZE</> is currently computing some
+       statistical informations of child tables, such as the percentage of NULL values,
+       the average width of a row, the number of distinct values etc.
+       It also stores the most common values and their frequencies.
+       The number of these values depends on the value of the
+       <xref linkend="guc-default-statistics-target"> parameter.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
  </sect2>
  </sect1>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index d357c8b..a74ea45 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -909,6 +909,21 @@ CREATE VIEW pg_stat_progress_vacuum AS
     FROM pg_stat_get_progress_info('VACUUM') AS S
 		LEFT JOIN pg_database D ON S.datid = D.oid;
 
+CREATE VIEW pg_stat_progress_analyze AS
+	SELECT
+		S.pid AS pid, S.datid AS datid, D.datname AS datname,
+		S.relid AS relid,
+		CASE S.param1 WHEN 0 THEN 'initializing'
+					  WHEN 1 THEN 'collecting sample rows'
+					  WHEN 2 THEN 'collecting inherited sample rows'
+					  WHEN 3 THEN 'computing statistics'
+					  WHEN 4 THEN 'computing inherited statistics'
+					  END AS phase,
+		S.param2 AS num_target_sample_rows,
+		S.param3 AS num_rows_sampled
+    FROM pg_stat_get_progress_info('ANALYZE') AS S
+		LEFT JOIN pg_database D ON S.datid = D.oid;
+
 CREATE VIEW pg_user_mappings AS
     SELECT
         U.oid       AS umid,
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 404acb2..09c3f11 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -33,6 +33,7 @@
 #include "commands/dbcommands.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "commands/progress.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -76,6 +77,7 @@ typedef struct AnlIndexData
 /* Default statistics target (GUC parameter) */
 int			default_statistics_target = 100;
 
+
 /* A few variables that don't seem worth passing around as parameters */
 static MemoryContext anl_context = NULL;
 static BufferAccessStrategy vac_strategy;
@@ -263,10 +265,16 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
 	MyPgXact->vacuumFlags |= PROC_IN_ANALYZE;
 	LWLockRelease(ProcArrayLock);
 
+	/* Report that we are going to start analyzing onerel. */
+	pgstat_progress_start_command(PROGRESS_COMMAND_ANALYZE,
+								  RelationGetRelid(onerel));
+
 	/*
 	 * Do the normal non-recursive ANALYZE.  We can skip this for partitioned
 	 * tables, which don't contain any rows.
 	 */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+								 PROGRESS_ANALYZE_PHASE_COLLECT_SAMPLE_ROWS);
 	if (onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
 		do_analyze_rel(onerel, options, params, va_cols, acquirefunc,
 					   relpages, false, in_outer_xact, elevel);
@@ -275,8 +283,15 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
 	 * If there are child tables, do recursive ANALYZE.
 	 */
 	if (onerel->rd_rel->relhassubclass)
+	{
+		pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+							 PROGRESS_ANALYZE_PHASE_COLLECT_INH_SAMPLE_ROWS);
 		do_analyze_rel(onerel, options, params, va_cols, acquirefunc, relpages,
 					   true, in_outer_xact, elevel);
+	}
+
+	/* We're done analyzing. */
+	pgstat_progress_end_command();
 
 	/*
 	 * Close source relation now, but keep lock so that no one deletes it
@@ -493,6 +508,11 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	/*
 	 * Acquire the sample rows
 	 */
+
+	/* Report the number of target sample rows */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_TARGET_SAMPLE_ROWS,
+								 targrows);
+
 	rows = (HeapTuple *) palloc(targrows * sizeof(HeapTuple));
 	if (inh)
 		numrows = acquire_inherited_sample_rows(onerel, elevel,
@@ -509,6 +529,15 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	 * responsible to make sure that whatever they store into the VacAttrStats
 	 * structure is allocated in anl_context.
 	 */
+
+	/* Report that statistics will now be computed. */
+	if (inh)
+		pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+									 PROGRESS_ANALYZE_PHASE_COMPUTE_INH_STATS);
+	else
+		pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+									 PROGRESS_ANALYZE_PHASE_COMPUTE_STATS);
+
 	if (numrows > 0)
 	{
 		MemoryContext col_context,
@@ -1180,6 +1209,9 @@ acquire_sample_rows(Relation onerel, int elevel,
 				}
 
 				samplerows += 1;
+
+				/* Report number of rows sampled so far */
+				pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_SAMPLED, numrows);
 			}
 		}
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index dd2b924..d389907 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -466,6 +466,8 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 	/* Translate command name into command type code. */
 	if (pg_strcasecmp(cmd, "VACUUM") == 0)
 		cmdtype = PROGRESS_COMMAND_VACUUM;
+	else if (pg_strcasecmp(cmd, "ANALYZE") == 0)
+		cmdtype = PROGRESS_COMMAND_ANALYZE;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h
index 9472ecc..7668c44 100644
--- a/src/include/commands/progress.h
+++ b/src/include/commands/progress.h
@@ -34,4 +34,15 @@
 #define PROGRESS_VACUUM_PHASE_TRUNCATE			5
 #define PROGRESS_VACUUM_PHASE_FINAL_CLEANUP		6
 
+/* Progress parameters for analyze */
+#define PROGRESS_ANALYZE_PHASE					0
+#define PROGRESS_ANALYZE_NUM_TARGET_SAMPLE_ROWS	1
+#define PROGRESS_ANALYZE_NUM_ROWS_SAMPLED		2
+
+/* Phases of analyze (as advertised via PROGRESS_ANALYZE_PHASE) */
+#define PROGRESS_ANALYZE_PHASE_COLLECT_SAMPLE_ROWS		1
+#define PROGRESS_ANALYZE_PHASE_COLLECT_INH_SAMPLE_ROWS	2
+#define PROGRESS_ANALYZE_PHASE_COMPUTE_STATS			3
+#define PROGRESS_ANALYZE_PHASE_COMPUTE_INH_STATS		4
+
 #endif
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index e29397f..2141593 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -911,7 +911,8 @@ typedef enum
 typedef enum ProgressCommandType
 {
 	PROGRESS_COMMAND_INVALID,
-	PROGRESS_COMMAND_VACUUM
+	PROGRESS_COMMAND_VACUUM,
+	PROGRESS_COMMAND_ANALYZE
 } ProgressCommandType;
 
 #define PGSTAT_NUM_PROGRESS_PARAM	10
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index d706f42..3400527 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1819,6 +1819,22 @@ pg_stat_database_conflicts| SELECT d.oid AS datid,
     pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin,
     pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock
    FROM pg_database d;
+pg_stat_progress_analyze| SELECT s.pid,
+    s.datid,
+    d.datname,
+    s.relid,
+        CASE s.param1
+            WHEN 0 THEN 'initializing'::text
+            WHEN 1 THEN 'collecting sample rows'::text
+            WHEN 2 THEN 'collecting inherited sample rows'::text
+            WHEN 3 THEN 'computing statistics'::text
+            WHEN 4 THEN 'computing inherited statistics'::text
+            ELSE NULL::text
+        END AS phase,
+    s.param2 AS num_target_sample_rows,
+    s.param3 AS num_rows_sampled
+   FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)
+     LEFT JOIN pg_database d ON ((s.datid = d.oid)));
 pg_stat_progress_vacuum| SELECT s.pid,
     s.datid,
     d.datname,
#35Masahiko Sawada
sawada.mshk@gmail.com
In reply to: vinayak (#34)
Re: ANALYZE command progress checker

On Wed, Mar 29, 2017 at 5:38 PM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

On 2017/03/25 4:30, Robert Haas wrote:

On Fri, Mar 24, 2017 at 3:41 AM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

I have updated the patch.

You can't change the definition of AcquireSampleRowsFunc without
updating the documentation in fdwhandler.sgml, but I think I don't
immediately understand why that's a thing we want to do at all if
neither file_fdw nor postgres_fdw are going to use the additional
argument. It seems to be entirely pointless because the new value
being passed down is always zero and even if the callee were to update
it, do_analyze_rel() would just ignore the change on return. Am I
missing something here? Also, the whitespace-only changes to fdwapi.h
related to AcquireSampleRowsFunc going to get undone by pgindent, so
even if there's some reason to change that you should leave the lines
that don't need changing untouched.

I reviewed v7 patch.

When I ran ANALYZE command to the table having 5 millions rows with 3
child tables, the progress report I got shows the strange result. The
following result was output after sampled all target rows from child
tables (i.g, after finished acquire_inherited_sample_rows). I think
the progress information should report 100% complete at this point.

#= select * from pg_stat_progress_analyze ;
pid | datid | datname | relid | phase |
num_target_sample_rows | num_rows_sampled
-------+-------+----------+-------+----------------------------------+------------------------+------------------
81719 | 13215 | postgres | 16440 | collecting inherited sample rows |
3000000 | 1800000
(1 row)

I guess the cause of this is that you always count the number of
sampled rows in acquire_sample_rows staring from 0 for each child
table. num_rows_sampled is reset whenever starting collecting sample
rows.
Also, even if table has child table, the total number of sampling row
is not changed. I think that main differences between analyzing on
normal table and on partitioned table is where we fetch the sample row
from. So I'm not sure if adding "computing inherited statistics" phase
is desirable. If you want to report progress of collecting sample rows
on child table I think that you might want to add the information
showing which child table is being analyzed.

--
pg_stat_progress_analyze.num_target_sample_rows is set while ignoring
the number of rows the target table has actually. So If the table has
rows less than target number of rows, the num_rows_sampled never reach
to num_target_sample_rows.

--
@@ -1180,6 +1213,9 @@ acquire_sample_rows(Relation onerel, int elevel,
                                }
                                samplerows += 1;
+
+                               /* Report number of rows sampled so far */
+
pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_SAMPLED,
numrows);
                        }
                }

I think that it's wrong to use numrows variable to report the progress
of the number of rows sampled. As the following comment mentioned, we
first save row into rows[] and increment numrows until numrows <
targrows. And then we could replace stored sample row with new sampled
row. That is, after collecting "numrows" rows, analyze can still take
a long time to get and replace the sample row. To computing progress
of collecting sample row, IMO reporting the number of blocks we
scanned so far is better than number of sample rows. Thought?

/*
* The first targrows sample rows are simply copied into the
* reservoir. Then we start replacing tuples in the sample
* until we reach the end of the relation. This algorithm is
* from Jeff Vitter's paper (see full citation below). It
* works by repeatedly computing the number of tuples to skip
* before selecting a tuple, which replaces a randomly chosen
* element of the reservoir (current set of tuples). At all
* times the reservoir is a true random sample of the tuples
* we've passed over so far, so when we fall off the end of
* the relation we're done.
*/

I Understood that we could not change the definition of
AcquireSampleRowsFunc without updating the documentation in fdwhandler.sgml.
In the last patch I have modified the definition of AcquireSampleRowsFunc to
handle the inheritance case.
If the table being analyzed has one or more children, ANALYZE will gather
statistics twice:
once on the rows of parent table only and second on the rows of the parent
table with all of its children.
So while reporting the progress the value of number of target sample rows
and number of rows sampled is mismatched.
For single relation it works fine.

In the attached patch I have not change the definition of
AcquireSampleRowsFunc.
I have updated inheritance case in the the documentation.

+            /* Reset rows processed to zero for the next column */
+
pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_PROCESSED,
0);

This seems like a bad design. If you see a value other than zero in
this field, you still won't know how much progress analyze has made,
because you don't know which column you're processing. I also feel
like you're not paying enough attention to a key point here that I
made in my last review, which is that you need to look at where
ANALYZE actually spends most of the time. If the process of computing
the per-row statistics consumes significant CPU time, then maybe
there's some point in trying to instrument it, but does it? Unless
you know the answer to that question, you don't know whether there's
even a point to trying to instrument this.

Understood. The computing statistics phase takes long time so I am looking
at the code.

Yes, ANALYZE spends most of time on computing statistics phase. I
measured the duration time for each phases on my environment. I set up
the table by pgbench -i -s 100 (total 10 million rows), and filled the
filler column of pgbench_accounts table with random string. And I set
default_statistics_target to 1000, and ran analyze on that table. The
analyze scanned all blocks of target table and collected 3000000
sample rows. The durations of each phase are,

* Sampling : 7 sec
* Computing statistics : 28 sec
* aid column : 1 sec
* bid column : 1 sec
* abalance column : 1 sec
* filler column : 25 sec

I'm not sure if we can calculate the meaningful progress information
in computing statistics function such as compute_scalar_stats,
compute_trivial_stats. But I think that at least showing which
statistics of column is being computed would be good information for
user.

Regards,

--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#36vinayak
Pokale_Vinayak_q3@lab.ntt.co.jp
In reply to: Masahiko Sawada (#35)
Re: ANALYZE command progress checker

On 2017/03/30 17:39, Masahiko Sawada wrote:

On Wed, Mar 29, 2017 at 5:38 PM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

On 2017/03/25 4:30, Robert Haas wrote:

On Fri, Mar 24, 2017 at 3:41 AM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

I have updated the patch.

You can't change the definition of AcquireSampleRowsFunc without
updating the documentation in fdwhandler.sgml, but I think I don't
immediately understand why that's a thing we want to do at all if
neither file_fdw nor postgres_fdw are going to use the additional
argument. It seems to be entirely pointless because the new value
being passed down is always zero and even if the callee were to update
it, do_analyze_rel() would just ignore the change on return. Am I
missing something here? Also, the whitespace-only changes to fdwapi.h
related to AcquireSampleRowsFunc going to get undone by pgindent, so
even if there's some reason to change that you should leave the lines
that don't need changing untouched.

I reviewed v7 patch.

Thank you for reviewing the patch.

When I ran ANALYZE command to the table having 5 millions rows with 3
child tables, the progress report I got shows the strange result. The
following result was output after sampled all target rows from child
tables (i.g, after finished acquire_inherited_sample_rows). I think
the progress information should report 100% complete at this point.

#= select * from pg_stat_progress_analyze ;
pid | datid | datname | relid | phase |
num_target_sample_rows | num_rows_sampled
-------+-------+----------+-------+----------------------------------+------------------------+------------------
81719 | 13215 | postgres | 16440 | collecting inherited sample rows |
3000000 | 1800000
(1 row)

I guess the cause of this is that you always count the number of
sampled rows in acquire_sample_rows staring from 0 for each child
table. num_rows_sampled is reset whenever starting collecting sample
rows.
Also, even if table has child table, the total number of sampling row
is not changed. I think that main differences between analyzing on
normal table and on partitioned table is where we fetch the sample row
from. So I'm not sure if adding "computing inherited statistics" phase
is desirable. If you want to report progress of collecting sample rows
on child table I think that you might want to add the information
showing which child table is being analyzed.

Yes. I think showing child table information would be good to user/DBA.

--
pg_stat_progress_analyze.num_target_sample_rows is set while ignoring
the number of rows the target table has actually. So If the table has
rows less than target number of rows, the num_rows_sampled never reach
to num_target_sample_rows.

--
@@ -1180,6 +1213,9 @@ acquire_sample_rows(Relation onerel, int elevel,
}
samplerows += 1;
+
+                               /* Report number of rows sampled so far */
+
pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_SAMPLED,
numrows);
}
}

I think that it's wrong to use numrows variable to report the progress
of the number of rows sampled. As the following comment mentioned, we
first save row into rows[] and increment numrows until numrows <
targrows. And then we could replace stored sample row with new sampled
row. That is, after collecting "numrows" rows, analyze can still take
a long time to get and replace the sample row. To computing progress
of collecting sample row, IMO reporting the number of blocks we
scanned so far is better than number of sample rows. Thought?

/*
* The first targrows sample rows are simply copied into the
* reservoir. Then we start replacing tuples in the sample
* until we reach the end of the relation. This algorithm is
* from Jeff Vitter's paper (see full citation below). It
* works by repeatedly computing the number of tuples to skip
* before selecting a tuple, which replaces a randomly chosen
* element of the reservoir (current set of tuples). At all
* times the reservoir is a true random sample of the tuples
* we've passed over so far, so when we fall off the end of
* the relation we're done.
*/

I think we can either report number of blocks scanned so far or number
of sample rows.
But I agree with you that reporting the number of blocks scanned so far
would be better than reporting number of sample rows.

I Understood that we could not change the definition of
AcquireSampleRowsFunc without updating the documentation in fdwhandler.sgml.
In the last patch I have modified the definition of AcquireSampleRowsFunc to
handle the inheritance case.
If the table being analyzed has one or more children, ANALYZE will gather
statistics twice:
once on the rows of parent table only and second on the rows of the parent
table with all of its children.
So while reporting the progress the value of number of target sample rows
and number of rows sampled is mismatched.
For single relation it works fine.

In the attached patch I have not change the definition of
AcquireSampleRowsFunc.
I have updated inheritance case in the the documentation.

+            /* Reset rows processed to zero for the next column */
+
pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_PROCESSED,
0);

This seems like a bad design. If you see a value other than zero in
this field, you still won't know how much progress analyze has made,
because you don't know which column you're processing. I also feel
like you're not paying enough attention to a key point here that I
made in my last review, which is that you need to look at where
ANALYZE actually spends most of the time. If the process of computing
the per-row statistics consumes significant CPU time, then maybe
there's some point in trying to instrument it, but does it? Unless
you know the answer to that question, you don't know whether there's
even a point to trying to instrument this.

Understood. The computing statistics phase takes long time so I am looking
at the code.

Yes, ANALYZE spends most of time on computing statistics phase. I
measured the duration time for each phases on my environment. I set up
the table by pgbench -i -s 100 (total 10 million rows), and filled the
filler column of pgbench_accounts table with random string. And I set
default_statistics_target to 1000, and ran analyze on that table. The
analyze scanned all blocks of target table and collected 3000000
sample rows. The durations of each phase are,

* Sampling : 7 sec
* Computing statistics : 28 sec
* aid column : 1 sec
* bid column : 1 sec
* abalance column : 1 sec
* filler column : 25 sec

I'm not sure if we can calculate the meaningful progress information
in computing statistics function such as compute_scalar_stats,
compute_trivial_stats. But I think that at least showing which
statistics of column is being computed would be good information for
user.

+1.
I think this kind of progress information would be helpful for user.

Regards,
Vinayak Pokale
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#37Masahiko Sawada
sawada.mshk@gmail.com
In reply to: vinayak (#36)
Re: ANALYZE command progress checker

On Thu, Mar 30, 2017 at 6:19 PM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

On 2017/03/30 17:39, Masahiko Sawada wrote:

On Wed, Mar 29, 2017 at 5:38 PM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

On 2017/03/25 4:30, Robert Haas wrote:

On Fri, Mar 24, 2017 at 3:41 AM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

I have updated the patch.

You can't change the definition of AcquireSampleRowsFunc without
updating the documentation in fdwhandler.sgml, but I think I don't
immediately understand why that's a thing we want to do at all if
neither file_fdw nor postgres_fdw are going to use the additional
argument. It seems to be entirely pointless because the new value
being passed down is always zero and even if the callee were to update
it, do_analyze_rel() would just ignore the change on return. Am I
missing something here? Also, the whitespace-only changes to fdwapi.h
related to AcquireSampleRowsFunc going to get undone by pgindent, so
even if there's some reason to change that you should leave the lines
that don't need changing untouched.

I reviewed v7 patch.

Thank you for reviewing the patch.

When I ran ANALYZE command to the table having 5 millions rows with 3
child tables, the progress report I got shows the strange result. The
following result was output after sampled all target rows from child
tables (i.g, after finished acquire_inherited_sample_rows). I think
the progress information should report 100% complete at this point.

#= select * from pg_stat_progress_analyze ;
pid | datid | datname | relid | phase |
num_target_sample_rows | num_rows_sampled

-------+-------+----------+-------+----------------------------------+------------------------+------------------
81719 | 13215 | postgres | 16440 | collecting inherited sample rows |
3000000 | 1800000
(1 row)

I guess the cause of this is that you always count the number of
sampled rows in acquire_sample_rows staring from 0 for each child
table. num_rows_sampled is reset whenever starting collecting sample
rows.
Also, even if table has child table, the total number of sampling row
is not changed. I think that main differences between analyzing on
normal table and on partitioned table is where we fetch the sample row
from. So I'm not sure if adding "computing inherited statistics" phase
is desirable. If you want to report progress of collecting sample rows
on child table I think that you might want to add the information
showing which child table is being analyzed.

Yes. I think showing child table information would be good to user/DBA.

--
pg_stat_progress_analyze.num_target_sample_rows is set while ignoring
the number of rows the target table has actually. So If the table has
rows less than target number of rows, the num_rows_sampled never reach
to num_target_sample_rows.

--
@@ -1180,6 +1213,9 @@ acquire_sample_rows(Relation onerel, int elevel,
}
samplerows += 1;
+
+                               /* Report number of rows sampled so far */
+
pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_SAMPLED,
numrows);
}
}

I think that it's wrong to use numrows variable to report the progress
of the number of rows sampled. As the following comment mentioned, we
first save row into rows[] and increment numrows until numrows <
targrows. And then we could replace stored sample row with new sampled
row. That is, after collecting "numrows" rows, analyze can still take
a long time to get and replace the sample row. To computing progress
of collecting sample row, IMO reporting the number of blocks we
scanned so far is better than number of sample rows. Thought?

/*
* The first targrows sample rows are simply copied into the
* reservoir. Then we start replacing tuples in the sample
* until we reach the end of the relation. This algorithm is
* from Jeff Vitter's paper (see full citation below). It
* works by repeatedly computing the number of tuples to skip
* before selecting a tuple, which replaces a randomly chosen
* element of the reservoir (current set of tuples). At all
* times the reservoir is a true random sample of the tuples
* we've passed over so far, so when we fall off the end of
* the relation we're done.
*/

I think we can either report number of blocks scanned so far or number of
sample rows.
But I agree with you that reporting the number of blocks scanned so far
would be better than reporting number of sample rows.

I Understood that we could not change the definition of
AcquireSampleRowsFunc without updating the documentation in
fdwhandler.sgml.
In the last patch I have modified the definition of AcquireSampleRowsFunc
to
handle the inheritance case.
If the table being analyzed has one or more children, ANALYZE will gather
statistics twice:
once on the rows of parent table only and second on the rows of the
parent
table with all of its children.
So while reporting the progress the value of number of target sample rows
and number of rows sampled is mismatched.
For single relation it works fine.

In the attached patch I have not change the definition of
AcquireSampleRowsFunc.
I have updated inheritance case in the the documentation.

+            /* Reset rows processed to zero for the next column */
+
pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_PROCESSED,
0);

This seems like a bad design. If you see a value other than zero in
this field, you still won't know how much progress analyze has made,
because you don't know which column you're processing. I also feel
like you're not paying enough attention to a key point here that I
made in my last review, which is that you need to look at where
ANALYZE actually spends most of the time. If the process of computing
the per-row statistics consumes significant CPU time, then maybe
there's some point in trying to instrument it, but does it? Unless
you know the answer to that question, you don't know whether there's
even a point to trying to instrument this.

Understood. The computing statistics phase takes long time so I am
looking
at the code.

Yes, ANALYZE spends most of time on computing statistics phase. I
measured the duration time for each phases on my environment. I set up
the table by pgbench -i -s 100 (total 10 million rows), and filled the
filler column of pgbench_accounts table with random string. And I set
default_statistics_target to 1000, and ran analyze on that table. The
analyze scanned all blocks of target table and collected 3000000
sample rows. The durations of each phase are,

* Sampling : 7 sec
* Computing statistics : 28 sec
* aid column : 1 sec
* bid column : 1 sec
* abalance column : 1 sec
* filler column : 25 sec

I'm not sure if we can calculate the meaningful progress information
in computing statistics function such as compute_scalar_stats,
compute_trivial_stats. But I think that at least showing which
statistics of column is being computed would be good information for
user.

+1.
I think this kind of progress information would be helpful for user.

Also, how are you going to support the progress checker for foreign
table? In current design, each FDW has to count and report the
progress in their analyze function (AnalyzeForeignTable API), in order
to support the analyze progress of the foreign table. You can leave it
at this time but I think that we should document about it in either
case.

Regards,

--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#38vinayak
Pokale_Vinayak_q3@lab.ntt.co.jp
In reply to: Masahiko Sawada (#35)
1 attachment(s)
Re: ANALYZE command progress checker

On 2017/03/30 17:39, Masahiko Sawada wrote:

On Wed, Mar 29, 2017 at 5:38 PM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

On 2017/03/25 4:30, Robert Haas wrote:

On Fri, Mar 24, 2017 at 3:41 AM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp> wrote:

I have updated the patch.

You can't change the definition of AcquireSampleRowsFunc without
updating the documentation in fdwhandler.sgml, but I think I don't
immediately understand why that's a thing we want to do at all if
neither file_fdw nor postgres_fdw are going to use the additional
argument. It seems to be entirely pointless because the new value
being passed down is always zero and even if the callee were to update
it, do_analyze_rel() would just ignore the change on return. Am I
missing something here? Also, the whitespace-only changes to fdwapi.h
related to AcquireSampleRowsFunc going to get undone by pgindent, so
even if there's some reason to change that you should leave the lines
that don't need changing untouched.

I reviewed v7 patch.

When I ran ANALYZE command to the table having 5 millions rows with 3
child tables, the progress report I got shows the strange result. The
following result was output after sampled all target rows from child
tables (i.g, after finished acquire_inherited_sample_rows). I think
the progress information should report 100% complete at this point.

#= select * from pg_stat_progress_analyze ;
pid | datid | datname | relid | phase |
num_target_sample_rows | num_rows_sampled
-------+-------+----------+-------+----------------------------------+------------------------+------------------
81719 | 13215 | postgres | 16440 | collecting inherited sample rows |
3000000 | 1800000
(1 row)

I guess the cause of this is that you always count the number of
sampled rows in acquire_sample_rows staring from 0 for each child
table. num_rows_sampled is reset whenever starting collecting sample
rows.
Also, even if table has child table, the total number of sampling row
is not changed. I think that main differences between analyzing on
normal table and on partitioned table is where we fetch the sample row
from. So I'm not sure if adding "computing inherited statistics" phase
is desirable. If you want to report progress of collecting sample rows
on child table I think that you might want to add the information
showing which child table is being analyzed.

--
pg_stat_progress_analyze.num_target_sample_rows is set while ignoring
the number of rows the target table has actually. So If the table has
rows less than target number of rows, the num_rows_sampled never reach
to num_target_sample_rows.

--
@@ -1180,6 +1213,9 @@ acquire_sample_rows(Relation onerel, int elevel,
}
samplerows += 1;
+
+                               /* Report number of rows sampled so far */
+
pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_SAMPLED,
numrows);
}
}

I think that it's wrong to use numrows variable to report the progress
of the number of rows sampled. As the following comment mentioned, we
first save row into rows[] and increment numrows until numrows <
targrows. And then we could replace stored sample row with new sampled
row. That is, after collecting "numrows" rows, analyze can still take
a long time to get and replace the sample row. To computing progress
of collecting sample row, IMO reporting the number of blocks we
scanned so far is better than number of sample rows. Thought?

/*
* The first targrows sample rows are simply copied into the
* reservoir. Then we start replacing tuples in the sample
* until we reach the end of the relation. This algorithm is
* from Jeff Vitter's paper (see full citation below). It
* works by repeatedly computing the number of tuples to skip
* before selecting a tuple, which replaces a randomly chosen
* element of the reservoir (current set of tuples). At all
* times the reservoir is a true random sample of the tuples
* we've passed over so far, so when we fall off the end of
* the relation we're done.
*/

Looks good to me.
In the attached patch I have reported number of blocks scanned so far
instead of number of sample rows.

In the 'collecting inherited sample rows' phase, child_relid is reported
as a separate column.
The child_relid is reported its value only in 'collecting inherited
sample rows' phase. For single relation it return 0.
I am not sure whether this column would helpful or not.
Any suggestions are welcome.

+            /* Reset rows processed to zero for the next column */
+
pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_PROCESSED,
0);

This seems like a bad design. If you see a value other than zero in
this field, you still won't know how much progress analyze has made,
because you don't know which column you're processing. I also feel
like you're not paying enough attention to a key point here that I
made in my last review, which is that you need to look at where
ANALYZE actually spends most of the time. If the process of computing
the per-row statistics consumes significant CPU time, then maybe
there's some point in trying to instrument it, but does it? Unless
you know the answer to that question, you don't know whether there's
even a point to trying to instrument this.

Understood. The computing statistics phase takes long time so I am looking
at the code.

Yes, ANALYZE spends most of time on computing statistics phase. I
measured the duration time for each phases on my environment. I set up
the table by pgbench -i -s 100 (total 10 million rows), and filled the
filler column of pgbench_accounts table with random string. And I set
default_statistics_target to 1000, and ran analyze on that table. The
analyze scanned all blocks of target table and collected 3000000
sample rows. The durations of each phase are,

* Sampling : 7 sec
* Computing statistics : 28 sec
* aid column : 1 sec
* bid column : 1 sec
* abalance column : 1 sec
* filler column : 25 sec

I'm not sure if we can calculate the meaningful progress information
in computing statistics function such as compute_scalar_stats,
compute_trivial_stats. But I think that at least showing which
statistics of column is being computed would be good information for
user.

I agree with you that showing statistics of the column is being computed
would be good information but
progress reporting API is only supported for int64 values.
So I think it would be good if we report such value that will be helpful
for user in computing statistics phase.

Regards,
Vinayak Pokale
NTT Open Source Software Center

Attachments:

pg_stat_progress_analyze_v8.patchbinary/octet-stream; name=pg_stat_progress_analyze_v8.patchDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 9856968..dacf40c 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -521,6 +521,13 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       <command>VACUUM</>, showing current progress.
       See <xref linkend='vacuum-progress-reporting'>.</entry>
      </row>
+
+     <row>
+      <entry><structname>pg_stat_progress_analyze</><indexterm><primary>pg_stat_progress_analyze</primary></indexterm></entry>
+      <entry>One row for each backend running
+      <command>ANALYZE</>, showing current progress.
+      See <xref linkend='analyze-progress-reporting'>.</entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
@@ -3186,9 +3193,8 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
 
   <para>
    <productname>PostgreSQL</> has the ability to report the progress of
-   certain commands during command execution.  Currently, the only command
-   which supports progress reporting is <command>VACUUM</>.  This may be
-   expanded in the future.
+   certain commands during command execution.  Currently, the supported progress 
+   reporting commands are <command>VACUUM</> and <command>ANALYZE</>.
   </para>
 
  <sect2 id="vacuum-progress-reporting">
@@ -3378,7 +3384,139 @@ SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
    </tbody>
    </tgroup>
   </table>
+ </sect2>
+
+ <sect2 id="analyze-progress-reporting">
+  <title>ANALYZE Progress Reporting</title>
+
+  <para>
+   Whenever <command>ANALYZE</> is running, the
+   <structname>pg_stat_progress_analyze</structname> view will contain
+   one row for each backend that is currently analyzing.
+   The tables below describe the information that will be reported and
+   provide information about how to interpret it. Currently the analyze
+   progress reporting facility is not supported for foreign table.
+  </para>
+
+  <table id="pg-stat-progress-analyze-view" xreflabel="pg_stat_progress_analyze">
+   <title><structname>pg_stat_progress_analyze</structname> View</title>
+   <tgroup cols="3">
+    <thead>
+    <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><structfield>pid</></entry>
+     <entry><type>integer</></entry>
+     <entry>Process ID of backend.</entry>
+    </row>
+    <row>
+     <entry><structfield>datid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>datname</></entry>
+     <entry><type>name</></entry>
+     <entry>Name of the database to which this backend is connected.</entry>
+    </row>
+    <row>
+     <entry><structfield>relid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of the table being analyzed.</entry>
+    </row>
+    <row>
+     <entry><structfield>phase</></entry>
+     <entry><type>text</></entry>
+     <entry>
+       Current processing phase of analyze.  See <xref linkend='analyze-phases'>.
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>child_relid</></entry>
+     <entry><type>bigint</></entry>
+     <entry>OID of the child table being sampled. If the table being analyzed has
+       one or more children, it will return child table relation id otherwise it will 
+       return NULL.</entry>
+    </row>
+    <row>
+     <entry><structfield>num_blks_total</></entry>
+     <entry><type>bigint</></entry>
+     <entry>Total number of blocks that will be sampled.</entry>
+    </row>
+    <row>
+     <entry><structfield>num_blks_selected</></entry>
+     <entry><type>bigint</></entry>
+     <entry>Total number of blocks selected for sampling so far.</entry>
+    </row>
+    <row>
+     <entry><structfield>num_target_sample_rows</></entry>
+     <entry><type>bigint</></entry>
+     <entry>
+       Total number of sample rows of the relation. The sample it reads
+       is taken randomly. Its size depends on the
+       <xref linkend="guc-default-statistics-target"> parameter value.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
 
+  <table id="analyze-phases">
+   <title>ANALYZE phases</title>
+   <tgroup cols="2">
+    <thead>
+    <row>
+      <entry>Phase</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><literal>initializing</literal></entry>
+     <entry>
+       <command>ANALYZE</> is preparing to collect sample rows.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>collecting sample rows</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently collecting the sample rows of
+       single relation. The sample it reads is taken randomly. Its size
+       depends on the <xref linkend="guc-default-statistics-target">
+       parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>collecting inherited sample rows</literal></entry>
+     <entry>
+       <command>ANALYZE</> is currently collecting inherited sample rows.
+       If the table being analyzed has one or more children, this phase will
+       collect sample rows of parent table and all associated child relation.
+       The sample it reads is taken randomly. Its size depends on
+       the <xref linkend="guc-default-statistics-target"> parameter value.
+     </entry>
+    </row>
+    <row>
+     <entry><literal>computing statistics</literal></entry>
+     <entry>
+       On the collected sample <command>ANALYZE</> is currently computing some
+       statistical informations, such as the percentage of NULL values,
+       the average width of a row, the number of distinct values etc.
+       It also stores the most common values and their frequencies.
+       The number of these values depends on the value of the
+       <xref linkend="guc-default-statistics-target"> parameter.
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
  </sect2>
  </sect1>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 0217f39..3e2ef59 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -909,6 +909,22 @@ CREATE VIEW pg_stat_progress_vacuum AS
     FROM pg_stat_get_progress_info('VACUUM') AS S
 		LEFT JOIN pg_database D ON S.datid = D.oid;
 
+CREATE VIEW pg_stat_progress_analyze AS
+	SELECT
+		S.pid AS pid, S.datid AS datid, D.datname AS datname,
+		S.relid AS relid,
+		CASE S.param1 WHEN 0 THEN 'initializing'
+					  WHEN 1 THEN 'collecting sample rows'
+					  WHEN 2 THEN 'collecting inherited sample rows'
+					  WHEN 3 THEN 'computing statistics'
+					  END AS phase,
+		S.param2 AS child_relid,
+		S.param3 AS num_blks_total,
+		S.param4 AS num_blks_selected,
+		S.param5 AS num_target_sample_rows
+    FROM pg_stat_get_progress_info('ANALYZE') AS S
+		LEFT JOIN pg_database D ON S.datid = D.oid;
+
 CREATE VIEW pg_user_mappings AS
     SELECT
         U.oid       AS umid,
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 404acb2..587c926 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -33,6 +33,7 @@
 #include "commands/dbcommands.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "commands/progress.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -263,10 +264,16 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
 	MyPgXact->vacuumFlags |= PROC_IN_ANALYZE;
 	LWLockRelease(ProcArrayLock);
 
+	/* Report that we are going to start analyzing onerel. */
+	pgstat_progress_start_command(PROGRESS_COMMAND_ANALYZE,
+								  RelationGetRelid(onerel));
+
 	/*
 	 * Do the normal non-recursive ANALYZE.  We can skip this for partitioned
 	 * tables, which don't contain any rows.
 	 */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+								 PROGRESS_ANALYZE_PHASE_COLLECT_SAMPLE_ROWS);
 	if (onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
 		do_analyze_rel(onerel, options, params, va_cols, acquirefunc,
 					   relpages, false, in_outer_xact, elevel);
@@ -275,8 +282,15 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
 	 * If there are child tables, do recursive ANALYZE.
 	 */
 	if (onerel->rd_rel->relhassubclass)
+	{
+		pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+							 PROGRESS_ANALYZE_PHASE_COLLECT_INH_SAMPLE_ROWS);
 		do_analyze_rel(onerel, options, params, va_cols, acquirefunc, relpages,
 					   true, in_outer_xact, elevel);
+	}
+
+	/* We're done analyzing. */
+	pgstat_progress_end_command();
 
 	/*
 	 * Close source relation now, but keep lock so that no one deletes it
@@ -493,6 +507,11 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	/*
 	 * Acquire the sample rows
 	 */
+
+	/* Report the number of target sample rows */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_TARGET_SAMPLE_ROWS,
+								 targrows);
+
 	rows = (HeapTuple *) palloc(targrows * sizeof(HeapTuple));
 	if (inh)
 		numrows = acquire_inherited_sample_rows(onerel, elevel,
@@ -509,6 +528,11 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 	 * responsible to make sure that whatever they store into the VacAttrStats
 	 * structure is allocated in anl_context.
 	 */
+
+	/* Report that statistics will now be computed. */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_PHASE,
+								 PROGRESS_ANALYZE_PHASE_COMPUTE_STATS);
+
 	if (numrows > 0)
 	{
 		MemoryContext col_context,
@@ -1416,6 +1440,9 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
 		AcquireSampleRowsFunc acquirefunc = acquirefuncs[i];
 		double		childblocks = relblocks[i];
 
+		/* Report child relid which is currently collecting sample rows */
+		pgstat_progress_update_param(PROGRESS_ANALYZE_CHILD_REL, RelationGetRelid(childrel));
+
 		if (childblocks > 0)
 		{
 			int			childtargrows;
@@ -1466,6 +1493,8 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
 				*totaldeadrows += tdrows;
 			}
 		}
+		/* We're done collecting sample rows from child relation */
+		pgstat_progress_update_param(PROGRESS_ANALYZE_CHILD_REL, 0);
 
 		/*
 		 * Note: we cannot release the child-table locks, since we may have
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index e0cae1b..5a2a48e 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -467,6 +467,8 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 	/* Translate command name into command type code. */
 	if (pg_strcasecmp(cmd, "VACUUM") == 0)
 		cmdtype = PROGRESS_COMMAND_VACUUM;
+	else if (pg_strcasecmp(cmd, "ANALYZE") == 0)
+		cmdtype = PROGRESS_COMMAND_ANALYZE;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index c36459e..bb506ba 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -18,6 +18,7 @@
 #include <math.h>
 
 #include "utils/sampling.h"
+#include "commands/progress.h"
 
 
 /*
@@ -39,6 +40,9 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 {
 	bs->N = nblocks;			/* measured table size */
 
+	/* Report total number of blocks taht will be sampled */
+	pgstat_progress_update_param(PROGRESS_ANALYZE_TOTAL_BLOCKS, bs->N);
+
 	/*
 	 * If we decide to reduce samplesize for tables that have less or not much
 	 * more than samplesize blocks, here is the place to do it.
@@ -70,6 +74,10 @@ BlockSampler_Next(BlockSampler bs)
 	{
 		/* need all the rest */
 		bs->m++;
+
+		/* Report blocks selected so far */
+		pgstat_progress_update_param(PROGRESS_ANALYZE_BLOCKS_SELECTED, bs->m);
+
 		return bs->t++;
 	}
 
diff --git a/src/include/commands/progress.h b/src/include/commands/progress.h
index 9472ecc..32ec41d 100644
--- a/src/include/commands/progress.h
+++ b/src/include/commands/progress.h
@@ -34,4 +34,16 @@
 #define PROGRESS_VACUUM_PHASE_TRUNCATE			5
 #define PROGRESS_VACUUM_PHASE_FINAL_CLEANUP		6
 
+/* Progress parameters for analyze */
+#define PROGRESS_ANALYZE_PHASE					0
+#define PROGRESS_ANALYZE_CHILD_REL				1
+#define PROGRESS_ANALYZE_TOTAL_BLOCKS			2
+#define PROGRESS_ANALYZE_BLOCKS_SELECTED		3
+#define PROGRESS_ANALYZE_NUM_TARGET_SAMPLE_ROWS	4
+
+/* Phases of analyze (as advertised via PROGRESS_ANALYZE_PHASE) */
+#define PROGRESS_ANALYZE_PHASE_COLLECT_SAMPLE_ROWS		1
+#define PROGRESS_ANALYZE_PHASE_COLLECT_INH_SAMPLE_ROWS	2
+#define PROGRESS_ANALYZE_PHASE_COMPUTE_STATS			3
+
 #endif
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index e29397f..2141593 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -911,7 +911,8 @@ typedef enum
 typedef enum ProgressCommandType
 {
 	PROGRESS_COMMAND_INVALID,
-	PROGRESS_COMMAND_VACUUM
+	PROGRESS_COMMAND_VACUUM,
+	PROGRESS_COMMAND_ANALYZE
 } ProgressCommandType;
 
 #define PGSTAT_NUM_PROGRESS_PARAM	10
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index d706f42..d84ac8f 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1819,6 +1819,23 @@ pg_stat_database_conflicts| SELECT d.oid AS datid,
     pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin,
     pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock
    FROM pg_database d;
+pg_stat_progress_analyze| SELECT s.pid,
+    s.datid,
+    d.datname,
+    s.relid,
+        CASE s.param1
+            WHEN 0 THEN 'initializing'::text
+            WHEN 1 THEN 'collecting sample rows'::text
+            WHEN 2 THEN 'collecting inherited sample rows'::text
+            WHEN 3 THEN 'computing statistics'::text
+            ELSE NULL::text
+        END AS phase,
+    s.param2 AS child_relid,
+    s.param3 AS num_blks_total,
+    s.param4 AS num_blks_selected,
+    s.param5 AS num_target_sample_rows
+   FROM (pg_stat_get_progress_info('ANALYZE'::text) s(pid, datid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)
+     LEFT JOIN pg_database d ON ((s.datid = d.oid)));
 pg_stat_progress_vacuum| SELECT s.pid,
     s.datid,
     d.datname,
#39Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Masahiko Sawada (#35)
Re: ANALYZE command progress checker

On 2017/03/30 17:39, Masahiko Sawada wrote:

On Wed, Mar 29, 2017 at 5:38 PM, vinayak wrote:

I have updated the patch.

I reviewed v7 patch.

When I ran ANALYZE command to the table having 5 millions rows with 3
child tables, the progress report I got shows the strange result. The
following result was output after sampled all target rows from child
tables (i.g, after finished acquire_inherited_sample_rows). I think
the progress information should report 100% complete at this point.

[...]

I guess the cause of this is that you always count the number of
sampled rows in acquire_sample_rows staring from 0 for each child
table. num_rows_sampled is reset whenever starting collecting sample
rows.
Also, even if table has child table, the total number of sampling row
is not changed. I think that main differences between analyzing on
normal table and on partitioned table is where we fetch the sample row
from. So I'm not sure if adding "computing inherited statistics" phase
is desirable. If you want to report progress of collecting sample rows
on child table I think that you might want to add the information
showing which child table is being analyzed.

Two kinds of statistics are collected if the table is a inheritance parent.

First kind considers the table by itself and calculates regular
single-table statistics using rows sampled from the only available heap
(by calling do_analyze_rel() with inh=false).

The second kind are called inheritance statistics, which consider rows
sampled from the whole inheritance hierarchy (by calling do_analyze_rel()
with inh=true). Note that we are still collecting statistics for the
parent table, so we not really "analyzing" child tables; we just collect
sample rows from them to calculate whole-tree statistics for the root
parent table. It might still be possible to show the child table being
sampled though.

--
pg_stat_progress_analyze.num_target_sample_rows is set while ignoring
the number of rows the target table has actually. So If the table has
rows less than target number of rows, the num_rows_sampled never reach
to num_target_sample_rows.

--
@@ -1180,6 +1213,9 @@ acquire_sample_rows(Relation onerel, int elevel,
}
samplerows += 1;
+
+                               /* Report number of rows sampled so far */
+
pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_SAMPLED,
numrows);
}
}

I think that it's wrong to use numrows variable to report the progress
of the number of rows sampled. As the following comment mentioned, we
first save row into rows[] and increment numrows until numrows <
targrows. And then we could replace stored sample row with new sampled
row. That is, after collecting "numrows" rows, analyze can still take
a long time to get and replace the sample row. To computing progress
of collecting sample row, IMO reporting the number of blocks we
scanned so far is better than number of sample rows. Thought?

We can report progress in terms of individual blocks only inside
acquire_sample_rows(), which seems undesirable when one thinks that we
will be resetting the target for every child table. We should have a
global target that considers all child tables in the inheritance
hierarchy, which maybe is possible if we count them beforehand in
acquire_inheritance_sample_rows(). But why not use target sample rows,
which remains the same for both when we're collecting sample rows from one
table and from the whole inheritance hierarchy. We can keep the count of
already collected rows in a struct that is used across calls for all the
child tables and increment upward from that count when we start collecting
from a new child table.

/*
* The first targrows sample rows are simply copied into the
* reservoir. Then we start replacing tuples in the sample
* until we reach the end of the relation. This algorithm is
* from Jeff Vitter's paper (see full citation below). It
* works by repeatedly computing the number of tuples to skip
* before selecting a tuple, which replaces a randomly chosen
* element of the reservoir (current set of tuples). At all
* times the reservoir is a true random sample of the tuples
* we've passed over so far, so when we fall off the end of
* the relation we're done.
*/

It seems that we could use samplerows instead of numrows to count the
progress (if we choose to count progress in terms of sample rows collected).

Thanks,
Amit

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#40Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Amit Langote (#39)
Re: ANALYZE command progress checker

On 2017/04/04 14:12, Amit Langote wrote:

Two kinds of statistics are collected if the table is a inheritance parent.

First kind considers the table by itself and calculates regular
single-table statistics using rows sampled from the only available heap
(by calling do_analyze_rel() with inh=false).

Oops, should have also mentioned that this part is not true for the new
partitioned tables. There are single-table statistics for partitioned
tables, because it contains no data.

Thanks,
Amit

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#41Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Amit Langote (#40)
Re: ANALYZE command progress checker

On 2017/04/04 14:15, Amit Langote wrote:

On 2017/04/04 14:12, Amit Langote wrote:

Two kinds of statistics are collected if the table is a inheritance parent.

First kind considers the table by itself and calculates regular
single-table statistics using rows sampled from the only available heap
(by calling do_analyze_rel() with inh=false).

Oops, should have also mentioned that this part is not true for the new
partitioned tables. There are single-table statistics for partitioned
tables, because it contains no data.

Sorry again, I meant *no* single-table statistics partitioned tables...

Thanks,
Amit

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#42Masahiko Sawada
sawada.mshk@gmail.com
In reply to: Amit Langote (#39)
Re: ANALYZE command progress checker

On Tue, Apr 4, 2017 at 2:12 PM, Amit Langote
<Langote_Amit_f8@lab.ntt.co.jp> wrote:

On 2017/03/30 17:39, Masahiko Sawada wrote:

On Wed, Mar 29, 2017 at 5:38 PM, vinayak wrote:

I have updated the patch.

I reviewed v7 patch.

When I ran ANALYZE command to the table having 5 millions rows with 3
child tables, the progress report I got shows the strange result. The
following result was output after sampled all target rows from child
tables (i.g, after finished acquire_inherited_sample_rows). I think
the progress information should report 100% complete at this point.

[...]

I guess the cause of this is that you always count the number of
sampled rows in acquire_sample_rows staring from 0 for each child
table. num_rows_sampled is reset whenever starting collecting sample
rows.
Also, even if table has child table, the total number of sampling row
is not changed. I think that main differences between analyzing on
normal table and on partitioned table is where we fetch the sample row
from. So I'm not sure if adding "computing inherited statistics" phase
is desirable. If you want to report progress of collecting sample rows
on child table I think that you might want to add the information
showing which child table is being analyzed.

Two kinds of statistics are collected if the table is a inheritance parent.

First kind considers the table by itself and calculates regular
single-table statistics using rows sampled from the only available heap
(by calling do_analyze_rel() with inh=false).

The second kind are called inheritance statistics, which consider rows
sampled from the whole inheritance hierarchy (by calling do_analyze_rel()
with inh=true). Note that we are still collecting statistics for the
parent table, so we not really "analyzing" child tables; we just collect
sample rows from them to calculate whole-tree statistics for the root
parent table.

Agreed.

It might still be possible to show the child table being
sampled though.

--
pg_stat_progress_analyze.num_target_sample_rows is set while ignoring
the number of rows the target table has actually. So If the table has
rows less than target number of rows, the num_rows_sampled never reach
to num_target_sample_rows.

--
@@ -1180,6 +1213,9 @@ acquire_sample_rows(Relation onerel, int elevel,
}
samplerows += 1;
+
+                               /* Report number of rows sampled so far */
+
pgstat_progress_update_param(PROGRESS_ANALYZE_NUM_ROWS_SAMPLED,
numrows);
}
}

I think that it's wrong to use numrows variable to report the progress
of the number of rows sampled. As the following comment mentioned, we
first save row into rows[] and increment numrows until numrows <
targrows. And then we could replace stored sample row with new sampled
row. That is, after collecting "numrows" rows, analyze can still take
a long time to get and replace the sample row. To computing progress
of collecting sample row, IMO reporting the number of blocks we
scanned so far is better than number of sample rows. Thought?

We can report progress in terms of individual blocks only inside
acquire_sample_rows(), which seems undesirable when one thinks that we
will be resetting the target for every child table. We should have a
global target that considers all child tables in the inheritance
hierarchy, which maybe is possible if we count them beforehand in
acquire_inheritance_sample_rows(). But why not use target sample rows,
which remains the same for both when we're collecting sample rows from one
table and from the whole inheritance hierarchy. We can keep the count of
already collected rows in a struct that is used across calls for all the
child tables and increment upward from that count when we start collecting
from a new child table.

An another option I came up with is that support new pgstat progress
function, say pgstat_progress_incr_param, which increments index'th
member in st_progress_param[]. That way we just need to report a delta
using that function.

/*
* The first targrows sample rows are simply copied into the
* reservoir. Then we start replacing tuples in the sample
* until we reach the end of the relation. This algorithm is
* from Jeff Vitter's paper (see full citation below). It
* works by repeatedly computing the number of tuples to skip
* before selecting a tuple, which replaces a randomly chosen
* element of the reservoir (current set of tuples). At all
* times the reservoir is a true random sample of the tuples
* we've passed over so far, so when we fall off the end of
* the relation we're done.
*/

It seems that we could use samplerows instead of numrows to count the
progress (if we choose to count progress in terms of sample rows collected).

I guess it's hard to count progress in terms of sample rows collected
even if we use samplerows instead, because samplerows can be
incremented independently of the target number of sampling rows. The
samplerows can be incremented up to the total number of rows of
relation.

Regards,

--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#43Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Masahiko Sawada (#42)
Re: ANALYZE command progress checker

On 2017/04/04 15:30, Masahiko Sawada wrote:

We can report progress in terms of individual blocks only inside
acquire_sample_rows(), which seems undesirable when one thinks that we
will be resetting the target for every child table. We should have a
global target that considers all child tables in the inheritance
hierarchy, which maybe is possible if we count them beforehand in
acquire_inheritance_sample_rows(). But why not use target sample rows,
which remains the same for both when we're collecting sample rows from one
table and from the whole inheritance hierarchy. We can keep the count of
already collected rows in a struct that is used across calls for all the
child tables and increment upward from that count when we start collecting
from a new child table.

An another option I came up with is that support new pgstat progress
function, say pgstat_progress_incr_param, which increments index'th
member in st_progress_param[]. That way we just need to report a delta
using that function.

That's an interesting idea. It could be made to work and would not
require changing the interface of AcquireSampleRowsFunc, which seems very
desirable.

/*
* The first targrows sample rows are simply copied into the
* reservoir. Then we start replacing tuples in the sample
* until we reach the end of the relation. This algorithm is
* from Jeff Vitter's paper (see full citation below). It
* works by repeatedly computing the number of tuples to skip
* before selecting a tuple, which replaces a randomly chosen
* element of the reservoir (current set of tuples). At all
* times the reservoir is a true random sample of the tuples
* we've passed over so far, so when we fall off the end of
* the relation we're done.
*/

It seems that we could use samplerows instead of numrows to count the
progress (if we choose to count progress in terms of sample rows collected).

I guess it's hard to count progress in terms of sample rows collected
even if we use samplerows instead, because samplerows can be
incremented independently of the target number of sampling rows. The
samplerows can be incremented up to the total number of rows of
relation.

Hmm, you're right. It could be counted with a separate variable
initialized to 0 and incremented every time we decide to add a row to the
final set of sampled rows, although different implementations of
AcquireSampleRowsFunc have different ways of deciding if a given row will
be part of the final set of sampled rows.

On the other hand, if we decide to count progress in terms of blocks as
you suggested afraid, I'm afraid that FDWs won't be able to report the
progress.

Thanks,
Amit

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#44Robert Haas
robertmhaas@gmail.com
In reply to: Amit Langote (#43)
Re: ANALYZE command progress checker

On Tue, Apr 4, 2017 at 4:57 AM, Amit Langote
<Langote_Amit_f8@lab.ntt.co.jp> wrote:

Hmm, you're right. It could be counted with a separate variable
initialized to 0 and incremented every time we decide to add a row to the
final set of sampled rows, although different implementations of
AcquireSampleRowsFunc have different ways of deciding if a given row will
be part of the final set of sampled rows.

On the other hand, if we decide to count progress in terms of blocks as
you suggested afraid, I'm afraid that FDWs won't be able to report the
progress.

I think it may be time to push this patch out to v11. It was
submitted one day before the start of the last CommitFest, the design
wasn't really right, and it's not clear even now that we know what the
right design is. And we're pretty much out of time.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#45Masahiko Sawada
sawada.mshk@gmail.com
In reply to: Robert Haas (#44)
Re: ANALYZE command progress checker

On Wed, Apr 5, 2017 at 1:49 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Apr 4, 2017 at 4:57 AM, Amit Langote
<Langote_Amit_f8@lab.ntt.co.jp> wrote:

Hmm, you're right. It could be counted with a separate variable
initialized to 0 and incremented every time we decide to add a row to the
final set of sampled rows, although different implementations of
AcquireSampleRowsFunc have different ways of deciding if a given row will
be part of the final set of sampled rows.

On the other hand, if we decide to count progress in terms of blocks as
you suggested afraid, I'm afraid that FDWs won't be able to report the
progress.

I think it may be time to push this patch out to v11. It was
submitted one day before the start of the last CommitFest, the design
wasn't really right, and it's not clear even now that we know what the
right design is. And we're pretty much out of time.

+1
We're encountering the design issue and it takes more time to find out
right design including FDWs support.

Regards,

--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#46Tatsuro Yamada
yamada.tatsuro@lab.ntt.co.jp
In reply to: Masahiko Sawada (#45)
Re: ANALYZE command progress checker

On 2017/04/05 10:17, Masahiko Sawada wrote:

On Wed, Apr 5, 2017 at 1:49 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Apr 4, 2017 at 4:57 AM, Amit Langote
<Langote_Amit_f8@lab.ntt.co.jp> wrote:

Hmm, you're right. It could be counted with a separate variable
initialized to 0 and incremented every time we decide to add a row to the
final set of sampled rows, although different implementations of
AcquireSampleRowsFunc have different ways of deciding if a given row will
be part of the final set of sampled rows.

On the other hand, if we decide to count progress in terms of blocks as
you suggested afraid, I'm afraid that FDWs won't be able to report the
progress.

I think it may be time to push this patch out to v11. It was
submitted one day before the start of the last CommitFest, the design
wasn't really right, and it's not clear even now that we know what the
right design is. And we're pretty much out of time.

+1
We're encountering the design issue and it takes more time to find out
right design including FDWs support.

Regards,

--
Masahiko Sawada
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center

Hi Vinayak,

I found a typo in your patch: s/taht/that/

sampling.c
/* Report total number of blocks taht will be sampled */

Then, regarding FDWs support, I believe it means postgres_fdw support (is my understanding right?).
You might better to put pgstat_progress_update_param() into these functions, maybe.
postgres_fdw.c
- postgresAnalyzeForeignTable
- postgresAcquireSampleRowsFunc

And PgFdwAnalyzeState has these variables, hopefully you can get current number of rows
as a progress indicator.

sturuct PgFdwAnalyzeState
...
/* collected sample rows */
HeapTuple *rows; /* array of size targrows */
int targrows; /* target # of sample rows */
int numrows; /* # of sample rows collected */

/* for random sampling */
double samplerows; /* # of rows fetched */
double rowstoskip; /* # of rows to skip before next sample */
...

I hope it will help you.

Regards,
Tatsuro Yamada

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#47Daniel Gustafsson
daniel@yesql.se
In reply to: Masahiko Sawada (#45)
Re: ANALYZE command progress checker

On 05 Apr 2017, at 03:17, Masahiko Sawada <sawada.mshk@gmail.com> wrote:

On Wed, Apr 5, 2017 at 1:49 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Apr 4, 2017 at 4:57 AM, Amit Langote
<Langote_Amit_f8@lab.ntt.co.jp> wrote:

Hmm, you're right. It could be counted with a separate variable
initialized to 0 and incremented every time we decide to add a row to the
final set of sampled rows, although different implementations of
AcquireSampleRowsFunc have different ways of deciding if a given row will
be part of the final set of sampled rows.

On the other hand, if we decide to count progress in terms of blocks as
you suggested afraid, I'm afraid that FDWs won't be able to report the
progress.

I think it may be time to push this patch out to v11. It was
submitted one day before the start of the last CommitFest, the design
wasn't really right, and it's not clear even now that we know what the
right design is. And we're pretty much out of time.

+1
We're encountering the design issue and it takes more time to find out
right design including FDWs support.

I’m marking this patch Returned with Feedback since it has been Waiting for
author during the commitfest without updates.

cheers ./daniel

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers