On 2016/03/09 0:24, Robert Haas wrote:
> On Tue, Mar 8, 2016 at 3:02 AM, Amit Langote
> <langote_amit...@lab.ntt.co.jp> wrote:
>> Updated versions attached.
>>
>> * changed st_progress_param to int64 and so did the argument of
>> pgstat_progress_update_param().  Likewise changed param1..param10 of
>> pg_stat_get_progress_info()'s output columns to bigint.
>>
>> * Added back the Oid field st_command_target and corresponding function
>> pgstat_progress_set_command_target(Oid).
> 
> What the heck do we have an SQL-visible pg_stat_reset_local_progress()
> for?  Surely if we ever need that, it's a bug.

OK, now I am not sure what I was thinking adding that function. Removed.

> I think pgstat_progress_update_param() should Assert(index >= 0 &&
> index < N_PROGRESS_PARAM).  But I'd rename N_PROGRESS_PARAM to
> PGSTAT_NUM_PROGRESS_PARAM.

Agreed, done.

> Regarding "XXX - privilege check is maybe dubious" - I think the
> privilege check here should match pg_stat_activity.  If it does,
> there's nothing dubious about that IMHO.

OK, done.  So, it shows pid column to all, while rest of the values -
relid, param1..param10 are only shown to role members.  Unlike
pg_stat_activity, there is no text column to stash a "<insufficient
privilege>" message into, so all that's done is to output null values.

The attached revision addresses above and one of Horiguchi-san's comments
in his email yesterday.

Thanks a lot for the review.

Thanks,
Amit
>From 9473230af72e0a0e3b60a8ddf1922698f7f17145 Mon Sep 17 00:00:00 2001
From: Amit <amitlangot...@gmail.com>
Date: Sun, 28 Feb 2016 01:50:07 +0900
Subject: [PATCH 1/3] Provide a way for utility commands to report progress

Commands can update a set of parameters in shared memory using:

  pgstat_progress_update_param(param_index, param_value)

Up to 10 independent 64-bit integer values can be published by commands.
In addition to those, a command should always report its BackendCommandType
and the OID of the relation it is about to begin processing at the beginning
of the processing using:

  pgstat_progress_start_command(cmdtype)
  pgstat_progress_set_command_target(relid)

A view can be defined in system_views.sql that outputs the values returned
by pg_stat_get_progress_info(cmdtype), where 'cmdtype' is numeric value as
mentioned above.  Each such view has columns corresponding to the counters
published by respective commands.
---
 src/backend/postmaster/pgstat.c     |   60 +++++++++++++++++++++++
 src/backend/utils/adt/pgstatfuncs.c |   91 +++++++++++++++++++++++++++++++++++
 src/include/catalog/pg_proc.h       |    2 +
 src/include/pgstat.h                |   25 ++++++++++
 4 files changed, 178 insertions(+), 0 deletions(-)

diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index da768c6..a7d0133 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -2731,6 +2731,7 @@ pgstat_bestart(void)
 	beentry->st_clienthostname[NAMEDATALEN - 1] = '\0';
 	beentry->st_appname[NAMEDATALEN - 1] = '\0';
 	beentry->st_activity[pgstat_track_activity_query_size - 1] = '\0';
+	beentry->st_command = COMMAND_INVALID;
 
 	pgstat_increment_changecount_after(beentry);
 
@@ -2851,6 +2852,65 @@ pgstat_report_activity(BackendState state, const char *cmd_str)
 	pgstat_increment_changecount_after(beentry);
 }
 
+/*-----------
+ * pgstat_progress_update_param() -
+ *
+ * Update index'th member in st_progress_param[] of own backend entry.
+ *-----------
+ */
+void
+pgstat_progress_update_param(int index, int64 val)
+{
+	volatile PgBackendStatus *beentry = MyBEEntry;
+
+	if(!beentry)
+		return;
+
+	if (!pgstat_track_activities)
+		return;
+
+	Assert(index >= 0 && index < PGSTAT_NUM_PROGRESS_PARAM);
+
+	pgstat_increment_changecount_before(beentry);
+	beentry->st_progress_param[index] = val;
+	pgstat_increment_changecount_after(beentry);
+}
+
+/*-----------
+ * pgstat_progress_start_command()-
+ *
+ * Set st_command in own backend entry.  Also, zero-initialize
+ * st_progress_param array.
+ *-----------
+ */
+void
+pgstat_progress_start_command(BackendCommandType cmdtype)
+{
+	volatile PgBackendStatus *beentry = MyBEEntry;
+
+	pgstat_increment_changecount_before(beentry);
+	beentry->st_command = cmdtype;
+	MemSet(&beentry->st_progress_param, 0,
+		   sizeof(beentry->st_progress_param));
+	pgstat_increment_changecount_after(beentry);
+}
+
+/*-----------
+ * pgstat_progress_set_command_target()-
+ *
+ * Set st_command_target in own backend entry.
+ *-----------
+ */
+void
+pgstat_progress_set_command_target(Oid relid)
+{
+	volatile PgBackendStatus *beentry = MyBEEntry;
+
+	pgstat_increment_changecount_before(beentry);
+	beentry->st_command_target = relid;
+	pgstat_increment_changecount_after(beentry);
+}
+
 /* ----------
  * pgstat_report_appname() -
  *
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 1b22fcc..a12310d 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -64,6 +64,7 @@ extern Datum pg_stat_get_backend_xact_start(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_backend_start(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_backend_client_port(PG_FUNCTION_ARGS);
+extern Datum pg_stat_get_progress_info(PG_FUNCTION_ARGS);
 
 extern Datum pg_stat_get_db_numbackends(PG_FUNCTION_ARGS);
 extern Datum pg_stat_get_db_xact_commit(PG_FUNCTION_ARGS);
@@ -525,6 +526,96 @@ pg_stat_get_backend_idset(PG_FUNCTION_ARGS)
 }
 
 /*
+ * Returns progress parameter values of backends running a given command
+ */
+Datum
+pg_stat_get_progress_info(PG_FUNCTION_ARGS)
+{
+#define PG_STAT_GET_PROGRESS_COLS	PGSTAT_NUM_PROGRESS_PARAM + 2
+	int			num_backends = pgstat_fetch_stat_numbackends();
+	int			curr_backend;
+	int32		cmdtype = PG_GETARG_INT32(0);
+	TupleDesc	tupdesc;
+	Tuplestorestate *tupstore;
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
+
+	/* check to see if caller supports us returning a tuplestore */
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not " \
+						"allowed in this context")));
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+	MemoryContextSwitchTo(oldcontext);
+
+	for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
+	{
+		LocalPgBackendStatus   *local_beentry;
+		PgBackendStatus		   *beentry;
+		Datum		values[PG_STAT_GET_PROGRESS_COLS];
+		bool		nulls[PG_STAT_GET_PROGRESS_COLS];
+		int			i;
+
+		MemSet(values, 0, sizeof(values));
+		MemSet(nulls, 0, sizeof(nulls));
+
+		local_beentry = pgstat_fetch_stat_local_beentry(curr_backend);
+
+		if (!local_beentry)
+			continue;
+
+		beentry = &local_beentry->backendStatus;
+
+		/*
+		 * Report values for only those backends which are running the given
+		 * command.
+		 */
+		if (!beentry || beentry->st_command != cmdtype)
+			continue;
+
+		/* Value available to all callers */
+		values[0] = Int32GetDatum(beentry->st_procpid);
+
+		/* show rest of the values including relid only to role members */
+		if (has_privs_of_role(GetUserId(), beentry->st_userid))
+		{
+			values[1] = ObjectIdGetDatum(beentry->st_command_target);
+			for(i = 0; i < PGSTAT_NUM_PROGRESS_PARAM; i++)
+				values[i+2] = UInt32GetDatum(beentry->st_progress_param[i]);
+		}
+		else
+		{
+			for (i = 1; i < PGSTAT_NUM_PROGRESS_PARAM + 1; i++)
+				nulls[i] = true;
+		}
+
+		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+	}
+
+	/* clean up and return the tuplestore */
+	tuplestore_donestoring(tupstore);
+
+	return (Datum) 0;
+}
+
+/*
  * Returns activity of PG backends.
  */
 Datum
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index cbbb883..61a310a 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2710,6 +2710,8 @@ DATA(insert OID = 1936 (  pg_stat_get_backend_idset		PGNSP PGUID 12 1 100 0 0 f
 DESCR("statistics: currently active backend IDs");
 DATA(insert OID = 2022 (  pg_stat_get_activity			PGNSP PGUID 12 1 100 0 0 f f f f f t s r 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23,28,28,16,25,25,23,16,25}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,ssl,sslversion,sslcipher,sslbits,sslcompression,sslclientdn}" _null_ _null_ pg_stat_get_activity _null_ _null_ _null_ ));
 DESCR("statistics: information about currently active backends");
+DATA(insert OID = 3318 (  pg_stat_get_progress_info           PGNSP PGUID 12 1 100 0 0 f f f f f t s r 1 0 2249 "23" "{23,23,26,20,20,20,20,20,20,20,20,20,20}" "{i,o,o,o,o,o,o,o,o,o,o,o,o}" "{cmdtype,pid,relid,param1,param2,param3,param4,param5,param6,param7,param8,param9,param10}" _null_ _null_ pg_stat_get_progress_info _null_ _null_ _null_ ));
+DESCR("statistics: information about progress of backends running maintenance command");
 DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f f t s r 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
 DESCR("statistics: information about currently active replication");
 DATA(insert OID = 3317 (  pg_stat_get_wal_receiver	PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{23,25,3220,23,3220,23,1184,1184,3220,1184,25}" "{o,o,o,o,o,o,o,o,o,o,o}" "{pid,status,receive_start_lsn,receive_start_tli,received_lsn,received_tli,last_msg_send_time,last_msg_receipt_time,latest_end_lsn,latest_end_time,slot_name}" _null_ _null_ pg_stat_get_wal_receiver _null_ _null_ _null_ ));
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 65e968e..c529e66 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -696,6 +696,17 @@ typedef enum BackendState
 } BackendState;
 
 /* ----------
+ * Command type for progress reporting purposes
+ * ----------
+ */
+typedef enum BackendCommandType
+{
+	COMMAND_INVALID = 0,
+} BackendCommandType;
+
+#define PGSTAT_NUM_PROGRESS_PARAM	10
+
+/* ----------
  * Shared-memory data structures
  * ----------
  */
@@ -776,6 +787,17 @@ typedef struct PgBackendStatus
 
 	/* current command string; MUST be null-terminated */
 	char	   *st_activity;
+
+	/*
+	 * Progress parameters of currently running utility command in the backend
+	 * Different commands store different number of up to
+	 * PGSTAT_NUM_PROGRESS_PARAM values in st_progress_param.  However, each
+	 * command must set st_command and st_command_target (OID of the relation
+	 * it is about to begin to process) at the beginning of processing.
+	 */
+	BackendCommandType	st_command;
+	Oid					st_command_target;
+	int64				st_progress_param[PGSTAT_NUM_PROGRESS_PARAM];
 } PgBackendStatus;
 
 /*
@@ -935,6 +957,9 @@ extern void pgstat_report_waiting(bool waiting);
 extern const char *pgstat_get_backend_current_activity(int pid, bool checkUser);
 extern const char *pgstat_get_crashed_backend_activity(int pid, char *buffer,
 									int buflen);
+extern void pgstat_progress_start_command(BackendCommandType cmdtype);
+extern void pgstat_progress_set_command_target(Oid relid);
+extern void pgstat_progress_update_param(int index, int64 val);
 
 extern PgStat_TableStatus *find_tabstat_entry(Oid rel_id);
 extern PgStat_BackendFunctionEntry *find_funcstat_entry(Oid func_id);
-- 
1.7.1

>From 6016d554a5aa269db6916eec7130910eabc85237 Mon Sep 17 00:00:00 2001
From: amit <amitlangot...@gmail.com>
Date: Mon, 7 Mar 2016 14:38:34 +0900
Subject: [PATCH 2/3] WIP: Implement progress reporting for VACUUM command.

This basically utilizes the pgstat_progress* API to report a handful of
paramters to indicate its progress.  lazy_vacuum_rel() and lazy_scan_heap()
have been altered to report command start, command target table, and the
following parameters: processing phase, number of heap blocks, number of
index blocks (all indexes), current heap block number in the main scan loop
(whenever changes), index blocks vacuumed (once per finished index vacuum),
and number of index vacuum passes (every time when all indexes are vacuumed).
Following processing phases are identified and reported whenever one changes
to another: 'scanning heap', 'vacuuming indexes', 'vacuuming heap', 'cleanup',
'done'. The last value is really a misnomer but maybe clearer when someone is
staring at the progress view being polled.

TODO: find a way to report index pages vacuumed in a more granular manner than
the current report per index vacuumed.

A view named pg_stat_vacuum_progress has been added that shows these values.
---
 doc/src/sgml/monitoring.sgml         |  114 ++++++++++++++++++++++++++++++++++
 src/backend/catalog/system_views.sql |   23 +++++++
 src/backend/commands/vacuumlazy.c    |   80 +++++++++++++++++++++++-
 src/include/pgstat.h                 |    1 +
 src/test/regress/expected/rules.out  |   20 ++++++
 5 files changed, 237 insertions(+), 1 deletions(-)

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 85459d0..d60efbe 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -507,6 +507,13 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       yet included in <structname>pg_stat_user_functions</>).</entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_vacuum_progress</><indexterm><primary>pg_stat_vacuum_progress</primary></indexterm></entry>
+      <entry>One row for each backend (including autovacuum worker processes) running
+      <command>VACUUM</>, showing current progress in terms of heap pages it
+      has finished processing.  Note that the backends running
+      <command>VACUUM FULL</> are not included.</entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
@@ -1822,6 +1829,113 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
    controls exactly which functions are tracked.
   </para>
 
+  <table id="pg-stat-vacuum-progress" xreflabel="pg_stat_vacuum_progress">
+   <title><structname>pg_stat_vacuum_progress</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>relid</></entry>
+     <entry><type>oid</></entry>
+     <entry>OID of a table</entry>
+    </row>
+    <row>
+     <entry><structfield>processing_phase</></entry>
+     <entry><type>integer</></entry>
+     <entry>Current phase of vacuum.
+       Possible values are:
+       <itemizedlist>
+        <listitem>
+         <para>
+          <literal>scanning heap</>
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>vacuuming indexes</>
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>vacuuming heap</>
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>cleanup</>
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>done</>
+         </para>
+        </listitem>
+       </itemizedlist>
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>total_heap_blks</></entry>
+     <entry><type>integer</></entry>
+     <entry>Total number of heap blocks in the table</entry>
+    </row>
+    <row>
+     <entry><structfield>current_heap_blkno</></entry>
+     <entry><type>integer</></entry>
+     <entry>Current heap block being processed</entry>
+    </row>
+    <row>
+     <entry><structfield>total_index_blks</></entry>
+     <entry><type>integer</></entry>
+     <entry>Total number of index blocks to be processed</entry>
+    </row>
+    <row>
+     <entry><structfield>index_blks_done</></entry>
+     <entry><type>integer</></entry>
+     <entry>Number of index blocks processed</entry>
+    </row>
+    <row>
+     <entry><structfield>index_scan_count</></entry>
+     <entry><type>integer</></entry>
+     <entry>Number of times index scans has been performed so far</entry>
+    </row>
+    <row>
+     <entry><structfield>percent_done</></entry>
+     <entry><type>numeric</></entry>
+     <entry>
+      Amount of work finished in percent in terms of table blocks processed
+     </entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_vacuum_progress</structname> view will contain
+   one row for each backend (including autovacuum worker processes), showing
+   progress of <command>VACUUM</> running in it. Note that the backends
+   running <command>VACUUM FULL</> are not shown.
+  </para>
+
+  <para>
+   When interpreting the value of the <structfield>percent_done</> column, also
+   note the value of <structfield>processing_phase</>.  It's possible for the
+   former to be <literal>100.00</literal>, while the <command>VACUUM</> still
+   has not returned.  In that case, wait for the latter to turn to the value
+   <literal>done</literal>.
+  </para>
+
  </sect2>
 
  <sect2 id="monitoring-stats-functions">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index abf9a70..f622a4a 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -971,3 +971,26 @@ RETURNS jsonb
 LANGUAGE INTERNAL
 STRICT IMMUTABLE
 AS 'jsonb_set';
+
+CREATE VIEW pg_stat_vacuum_progress AS
+    SELECT
+           S.pid AS pid,
+           S.relid AS relid,
+           CASE S.param1
+               WHEN 1 THEN 'scanning heap'
+               WHEN 2 THEN 'vacuuming indexes'
+               WHEN 3 THEN 'vacuuming heap'
+               WHEN 4 THEN 'cleanup'
+               WHEN 5 THEN 'done'
+               ELSE 'unknown'
+           END AS processing_phase,
+           S.param2 AS total_heap_blks,
+           S.param3 AS current_heap_blkno,
+           S.param4 AS total_index_blks,
+           S.param5 AS index_blks_done,
+           S.param6 AS index_scan_count,
+           CASE S.param2
+			  WHEN 0 THEN 100::numeric(5, 2)
+			  ELSE ((S.param3 + 1)::numeric / S.param2 * 100)::numeric(5, 2)
+		   END AS percent_done
+   FROM pg_stat_get_progress_info(1) AS S;
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 8f7b248..60c3b3b 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -97,6 +97,28 @@
  */
 #define SKIP_PAGES_THRESHOLD	((BlockNumber) 32)
 
+/*
+ * Follwing progress parameters for lazy vacuum are reported to pgstat
+ */
+#define PROG_PAR_VAC_PHASE_ID			0
+#define PROG_PAR_VAC_HEAP_BLKS			1
+#define PROG_PAR_VAC_CUR_HEAP_BLK		2
+#define PROG_PAR_VAC_IDX_BLKS			3
+#define PROG_PAR_VAC_IDX_BLKS_DONE		4
+#define PROG_PAR_VAC_N_IDX_SCAN			5
+
+/*
+ * Following distinct phases of lazy vacuum are identified.  Although #1, #2
+ * and #3 run in a cyclical manner due to possibly limited memory to work
+ * with, wherein #1 is periodically interrupted to run #2 followed by #3
+ * and back, until all the blocks of the relations have been covered by #1.
+ */
+#define LV_PHASE_SCAN_HEAP			1
+#define LV_PHASE_VACUUM_INDEX		2
+#define LV_PHASE_VACUUM_HEAP		3
+#define LV_PHASE_CLEANUP			4
+#define LV_PHASE_DONE				5
+
 typedef struct LVRelStats
 {
 	/* hasindex = true means two-pass strategy; false means one-pass */
@@ -195,6 +217,10 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 
 	Assert(params != NULL);
 
+	/* initialize pgstat progress info */
+	pgstat_progress_start_command(COMMAND_LAZY_VACUUM);
+	pgstat_progress_set_command_target(RelationGetRelid(onerel));
+
 	/* measure elapsed time iff autovacuum logging requires it */
 	if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 	{
@@ -270,6 +296,9 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 	/* Vacuum the Free Space Map */
 	FreeSpaceMapVacuum(onerel);
 
+	/* We're done doing any heavy handling, so report */
+	pgstat_progress_update_param(PROG_PAR_VAC_PHASE_ID, LV_PHASE_DONE);
+
 	/*
 	 * Update statistics in pg_class.
 	 *
@@ -433,7 +462,9 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 			   Relation *Irel, int nindexes, bool scan_all)
 {
 	BlockNumber nblocks,
-				blkno;
+				blkno,
+				total_index_blks,
+			   *current_index_blks;
 	HeapTupleData tuple;
 	char	   *relname;
 	BlockNumber empty_pages,
@@ -474,6 +505,24 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 	lazy_space_alloc(vacrelstats, nblocks);
 	frozen = palloc(sizeof(xl_heap_freeze_tuple) * MaxHeapTuplesPerPage);
 
+	/* about to begin heap scan */
+	pgstat_progress_update_param(PROG_PAR_VAC_PHASE_ID, LV_PHASE_SCAN_HEAP);
+
+	/* total_heap_blks */
+	pgstat_progress_update_param(PROG_PAR_VAC_HEAP_BLKS, nblocks);
+
+	/* total_index_blks */
+	current_index_blks = (BlockNumber *) palloc(nindexes * sizeof(BlockNumber));
+	total_index_blks = 0;
+	for (i = 0; i < nindexes; i++)
+	{
+		BlockNumber		nblocks = RelationGetNumberOfBlocks(Irel[i]);
+
+		current_index_blks[i] = nblocks;
+		total_index_blks += nblocks;
+	}
+	pgstat_progress_update_param(PROG_PAR_VAC_IDX_BLKS, total_index_blks);
+
 	/*
 	 * We want to skip pages that don't require vacuuming according to the
 	 * visibility map, but only when we can skip at least SKIP_PAGES_THRESHOLD
@@ -581,6 +630,9 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 
 		vacuum_delay_point();
 
+		/* current_heap_blkno: 0..nblocks-1 */
+		pgstat_progress_update_param(PROG_PAR_VAC_CUR_HEAP_BLK, blkno);
+
 		/*
 		 * If we are close to overrunning the available space for dead-tuple
 		 * TIDs, pause and do a cycle of vacuuming before we tackle this page.
@@ -604,11 +656,22 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 			vacuum_log_cleanup_info(onerel, vacrelstats);
 
 			/* Remove index entries */
+			pgstat_progress_update_param(PROG_PAR_VAC_PHASE_ID, LV_PHASE_VACUUM_INDEX);
 			for (i = 0; i < nindexes; i++)
+			{
 				lazy_vacuum_index(Irel[i],
 								  &indstats[i],
 								  vacrelstats);
+
+				pgstat_progress_update_param(PROG_PAR_VAC_IDX_BLKS_DONE,
+											 current_index_blks[i]);
+			}
+
+			pgstat_progress_update_param(PROG_PAR_VAC_N_IDX_SCAN,
+										 vacrelstats->num_index_scans+1);
+
 			/* Remove tuples from heap */
+			pgstat_progress_update_param(PROG_PAR_VAC_PHASE_ID, LV_PHASE_VACUUM_HEAP);
 			lazy_vacuum_heap(onerel, vacrelstats);
 
 			/*
@@ -618,6 +681,10 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 			 */
 			vacrelstats->num_dead_tuples = 0;
 			vacrelstats->num_index_scans++;
+
+			/* go back to scanning the heap */
+			pgstat_progress_update_param(PROG_PAR_VAC_PHASE_ID,
+										 LV_PHASE_SCAN_HEAP);
 		}
 
 		/*
@@ -1154,16 +1221,27 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 		vacuum_log_cleanup_info(onerel, vacrelstats);
 
 		/* Remove index entries */
+		pgstat_progress_update_param(PROG_PAR_VAC_PHASE_ID, LV_PHASE_VACUUM_INDEX);
 		for (i = 0; i < nindexes; i++)
+		{
 			lazy_vacuum_index(Irel[i],
 							  &indstats[i],
 							  vacrelstats);
+
+			pgstat_progress_update_param(PROG_PAR_VAC_IDX_BLKS_DONE,
+										 current_index_blks[i]);
+		}
+		pgstat_progress_update_param(PROG_PAR_VAC_N_IDX_SCAN,
+									 vacrelstats->num_index_scans + 1);
+
 		/* Remove tuples from heap */
+		pgstat_progress_update_param(PROG_PAR_VAC_PHASE_ID, LV_PHASE_VACUUM_HEAP);
 		lazy_vacuum_heap(onerel, vacrelstats);
 		vacrelstats->num_index_scans++;
 	}
 
 	/* Do post-vacuum cleanup and statistics update for each index */
+	pgstat_progress_update_param(PROG_PAR_VAC_PHASE_ID, LV_PHASE_CLEANUP);
 	for (i = 0; i < nindexes; i++)
 		lazy_cleanup_index(Irel[i], indstats[i], vacrelstats);
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index c529e66..fecac83 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -702,6 +702,7 @@ typedef enum BackendState
 typedef enum BackendCommandType
 {
 	COMMAND_INVALID = 0,
+	COMMAND_LAZY_VACUUM
 } BackendCommandType;
 
 #define PGSTAT_NUM_PROGRESS_PARAM	10
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 81bc5c9..0ef6c76 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1851,6 +1851,26 @@ pg_stat_user_tables| SELECT pg_stat_all_tables.relid,
     pg_stat_all_tables.autoanalyze_count
    FROM pg_stat_all_tables
   WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text));
+pg_stat_vacuum_progress| SELECT s.pid,
+    s.relid,
+        CASE s.param1
+            WHEN 1 THEN 'scanning heap'::text
+            WHEN 2 THEN 'vacuuming indexes'::text
+            WHEN 3 THEN 'vacuuming heap'::text
+            WHEN 4 THEN 'cleanup'::text
+            WHEN 5 THEN 'done'::text
+            ELSE 'unknown'::text
+        END AS processing_phase,
+    s.param2 AS total_heap_blks,
+    s.param3 AS current_heap_blkno,
+    s.param4 AS total_index_blks,
+    s.param5 AS index_blks_done,
+    s.param6 AS index_scan_count,
+        CASE s.param2
+            WHEN 0 THEN (100)::numeric(5,2)
+            ELSE (((((s.param3 + 1))::numeric / (s.param2)::numeric) * (100)::numeric))::numeric(5,2)
+        END AS percent_done
+   FROM pg_stat_get_progress_info(1) s(pid, relid, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
 pg_stat_wal_receiver| SELECT s.pid,
     s.status,
     s.receive_start_lsn,
-- 
1.7.1

>From a2b820f79f206489f3d1a9f2a97466480699f0a6 Mon Sep 17 00:00:00 2001
From: amit <amitlangot...@gmail.com>
Date: Tue, 8 Mar 2016 15:23:38 +0900
Subject: [PATCH 3/3] WIP: Add a block number argument to index bulk delete callback.

Currently, index AMs can only pass a ItemPointer (heap tid) and a generic
pointer to caller specified struct.  Add a BlockNumber so that the callback
can use it for something.  For example, lazy_tid_reaped() now uses it to
track the progress of AM's bulk delete scan of an index by comparing it with
the last call's value that it stores in its private state on every change.
The private state being the LVRelStats struct that now has the corresponding
field.  On every change, the count of index blocks vacuumed is incremented
which is also a new field in LVRelStats. The count is reset for every index
vacuum round.

Then a pgstat_progress_update_param call has been added to lazy_tid_reaped,
that pushes the count on every increment.

Currently, only btree AM is changed to pass the block number, others pass
InvalidBlockNumber.
---
 src/backend/access/gin/ginvacuum.c    |    2 +-
 src/backend/access/gist/gistvacuum.c  |    2 +-
 src/backend/access/hash/hash.c        |    2 +-
 src/backend/access/nbtree/nbtree.c    |    2 +-
 src/backend/access/spgist/spgvacuum.c |    6 ++--
 src/backend/catalog/index.c           |    5 ++-
 src/backend/commands/vacuumlazy.c     |   36 ++++++++++++++------------------
 src/include/access/genam.h            |    4 ++-
 8 files changed, 29 insertions(+), 30 deletions(-)

diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c
index 6a4b98a..64e69d6 100644
--- a/src/backend/access/gin/ginvacuum.c
+++ b/src/backend/access/gin/ginvacuum.c
@@ -55,7 +55,7 @@ ginVacuumItemPointers(GinVacuumState *gvs, ItemPointerData *items,
 	 */
 	for (i = 0; i < nitem; i++)
 	{
-		if (gvs->callback(items + i, gvs->callback_state))
+		if (gvs->callback(items + i, InvalidBlockNumber, gvs->callback_state))
 		{
 			gvs->result->tuples_removed += 1;
 			if (!tmpitems)
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c
index 7947ff9..96e89c3 100644
--- a/src/backend/access/gist/gistvacuum.c
+++ b/src/backend/access/gist/gistvacuum.c
@@ -202,7 +202,7 @@ gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 				iid = PageGetItemId(page, i);
 				idxtuple = (IndexTuple) PageGetItem(page, iid);
 
-				if (callback(&(idxtuple->t_tid), callback_state))
+				if (callback(&(idxtuple->t_tid), InvalidBlockNumber, callback_state))
 					todelete[ntodelete++] = i;
 				else
 					stats->num_index_tuples += 1;
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 3d48c4f..1530f0b 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -575,7 +575,7 @@ loop_top:
 				itup = (IndexTuple) PageGetItem(page,
 												PageGetItemId(page, offno));
 				htup = &(itup->t_tid);
-				if (callback(htup, callback_state))
+				if (callback(htup, InvalidBlockNumber, callback_state))
 				{
 					/* mark the item for deletion */
 					deletable[ndeletable++] = offno;
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index f2905cb..8c6be30 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -1034,7 +1034,7 @@ restart:
 				 * applies to *any* type of index that marks index tuples as
 				 * killed.
 				 */
-				if (callback(htup, callback_state))
+				if (callback(htup, blkno, callback_state))
 					deletable[ndeletable++] = offnum;
 			}
 		}
diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c
index 15b867f..6c7265b 100644
--- a/src/backend/access/spgist/spgvacuum.c
+++ b/src/backend/access/spgist/spgvacuum.c
@@ -154,7 +154,7 @@ vacuumLeafPage(spgBulkDeleteState *bds, Relation index, Buffer buffer,
 		{
 			Assert(ItemPointerIsValid(&lt->heapPtr));
 
-			if (bds->callback(&lt->heapPtr, bds->callback_state))
+			if (bds->callback(&lt->heapPtr, InvalidBlockNumber, bds->callback_state))
 			{
 				bds->stats->tuples_removed += 1;
 				deletable[i] = true;
@@ -424,7 +424,7 @@ vacuumLeafRoot(spgBulkDeleteState *bds, Relation index, Buffer buffer)
 		{
 			Assert(ItemPointerIsValid(&lt->heapPtr));
 
-			if (bds->callback(&lt->heapPtr, bds->callback_state))
+			if (bds->callback(&lt->heapPtr, InvalidBlockNumber, bds->callback_state))
 			{
 				bds->stats->tuples_removed += 1;
 				toDelete[xlrec.nDelete] = i;
@@ -902,7 +902,7 @@ spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 
 /* Dummy callback to delete no tuples during spgvacuumcleanup */
 static bool
-dummy_callback(ItemPointer itemptr, void *state)
+dummy_callback(ItemPointer itemptr, BlockNumber index_blkno, void *state)
 {
 	return false;
 }
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 31a1438..d792fdb 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -114,7 +114,8 @@ static void IndexCheckExclusion(Relation heapRelation,
 					IndexInfo *indexInfo);
 static inline int64 itemptr_encode(ItemPointer itemptr);
 static inline void itemptr_decode(ItemPointer itemptr, int64 encoded);
-static bool validate_index_callback(ItemPointer itemptr, void *opaque);
+static bool validate_index_callback(ItemPointer itemptr, BlockNumber index_blkno,
+						void *opaque);
 static void validate_index_heapscan(Relation heapRelation,
 						Relation indexRelation,
 						IndexInfo *indexInfo,
@@ -2913,7 +2914,7 @@ itemptr_decode(ItemPointer itemptr, int64 encoded)
  * validate_index_callback - bulkdelete callback to collect the index TIDs
  */
 static bool
-validate_index_callback(ItemPointer itemptr, void *opaque)
+validate_index_callback(ItemPointer itemptr, BlockNumber index_blkno, void *opaque)
 {
 	v_i_state  *state = (v_i_state *) opaque;
 	int64		encoded = itemptr_encode(itemptr);
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 60c3b3b..d628822 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -143,6 +143,8 @@ typedef struct LVRelStats
 	int			num_index_scans;
 	TransactionId latestRemovedXid;
 	bool		lock_waiter_detected;
+	BlockNumber	last_index_blkno;
+	BlockNumber	index_blks_vacuumed;
 } LVRelStats;
 
 
@@ -176,7 +178,7 @@ static BlockNumber count_nondeletable_pages(Relation onerel,
 static void lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks);
 static void lazy_record_dead_tuple(LVRelStats *vacrelstats,
 					   ItemPointer itemptr);
-static bool lazy_tid_reaped(ItemPointer itemptr, void *state);
+static bool lazy_tid_reaped(ItemPointer itemptr, BlockNumber index_blkno, void *state);
 static int	vac_cmp_itemptr(const void *left, const void *right);
 static bool heap_page_is_all_visible(Relation rel, Buffer buf,
 						 TransactionId *visibility_cutoff_xid, bool *all_frozen);
@@ -463,8 +465,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 {
 	BlockNumber nblocks,
 				blkno,
-				total_index_blks,
-			   *current_index_blks;
+				total_index_blks;
 	HeapTupleData tuple;
 	char	   *relname;
 	BlockNumber empty_pages,
@@ -512,15 +513,9 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 	pgstat_progress_update_param(PROG_PAR_VAC_HEAP_BLKS, nblocks);
 
 	/* total_index_blks */
-	current_index_blks = (BlockNumber *) palloc(nindexes * sizeof(BlockNumber));
 	total_index_blks = 0;
 	for (i = 0; i < nindexes; i++)
-	{
-		BlockNumber		nblocks = RelationGetNumberOfBlocks(Irel[i]);
-
-		current_index_blks[i] = nblocks;
-		total_index_blks += nblocks;
-	}
+		total_index_blks += RelationGetNumberOfBlocks(Irel[i]);
 	pgstat_progress_update_param(PROG_PAR_VAC_IDX_BLKS, total_index_blks);
 
 	/*
@@ -657,16 +652,12 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 
 			/* Remove index entries */
 			pgstat_progress_update_param(PROG_PAR_VAC_PHASE_ID, LV_PHASE_VACUUM_INDEX);
+			vacrelstats->index_blks_vacuumed = 0;	/* reset for this round */
 			for (i = 0; i < nindexes; i++)
-			{
 				lazy_vacuum_index(Irel[i],
 								  &indstats[i],
 								  vacrelstats);
 
-				pgstat_progress_update_param(PROG_PAR_VAC_IDX_BLKS_DONE,
-											 current_index_blks[i]);
-			}
-
 			pgstat_progress_update_param(PROG_PAR_VAC_N_IDX_SCAN,
 										 vacrelstats->num_index_scans+1);
 
@@ -1222,15 +1213,12 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
 
 		/* Remove index entries */
 		pgstat_progress_update_param(PROG_PAR_VAC_PHASE_ID, LV_PHASE_VACUUM_INDEX);
+		vacrelstats->index_blks_vacuumed = 0;	/* reset for this round */
 		for (i = 0; i < nindexes; i++)
-		{
 			lazy_vacuum_index(Irel[i],
 							  &indstats[i],
 							  vacrelstats);
 
-			pgstat_progress_update_param(PROG_PAR_VAC_IDX_BLKS_DONE,
-										 current_index_blks[i]);
-		}
 		pgstat_progress_update_param(PROG_PAR_VAC_N_IDX_SCAN,
 									 vacrelstats->num_index_scans + 1);
 
@@ -1922,11 +1910,19 @@ lazy_record_dead_tuple(LVRelStats *vacrelstats,
  *		Assumes dead_tuples array is in sorted order.
  */
 static bool
-lazy_tid_reaped(ItemPointer itemptr, void *state)
+lazy_tid_reaped(ItemPointer itemptr, BlockNumber index_blkno, void *state)
 {
 	LVRelStats *vacrelstats = (LVRelStats *) state;
 	ItemPointer res;
 
+	if (index_blkno != vacrelstats->last_index_blkno)
+	{
+		++vacrelstats->index_blks_vacuumed;
+		pgstat_progress_update_param(PROG_PAR_VAC_IDX_BLKS_DONE,
+									 vacrelstats->index_blks_vacuumed);
+		vacrelstats->last_index_blkno = index_blkno;
+	}
+
 	res = (ItemPointer) bsearch((void *) itemptr,
 								(void *) vacrelstats->dead_tuples,
 								vacrelstats->num_dead_tuples,
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 81907d5..8d67d1c 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -77,7 +77,9 @@ typedef struct IndexBulkDeleteResult
 } IndexBulkDeleteResult;
 
 /* Typedef for callback function to determine if a tuple is bulk-deletable */
-typedef bool (*IndexBulkDeleteCallback) (ItemPointer itemptr, void *state);
+typedef bool (*IndexBulkDeleteCallback) (ItemPointer itemptr,
+										 BlockNumber index_blkno,
+										 void *state);
 
 /* struct definitions appear in relscan.h */
 typedef struct IndexScanDescData *IndexScanDesc;
-- 
1.7.1

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

Reply via email to