pg_stat_wal_write statistics view

Started by Haribabu Kommialmost 9 years ago30 messages
#1Haribabu Kommi
kommi.haribabu@gmail.com

Hi Hackers,

I just want to discuss adding of a new statistics view that provides
the information of wal writing details as follows

postgres=# \d pg_stat_wal_writer
View "pg_catalog.pg_stat_wal_writer"
Column | Type | Collation | Nullable |
Default
-----------------------+--------------------------+-----------+----------+---------
num_backend_writes | bigint | |
|
num_total_writes | bigint | | |
num_blocks | bigint | | |
total_write_time | bigint| | |
stats_reset | timestamp with time zone | | |

The columns of the view are
1. Total number of xlog writes that are called from the backend.
2. Total number of xlog writes that are called from both backend
and background workers. (This column can be changed to just
display on the background writes).
3. The number of the blocks that are written.
4. Total write_time of the IO operation it took, this variable data is
filled only when the track_io_timing GUC is enabled.
5. Last time when the stats are reset.

I feel this view information may be useful in finding out how much
time does backend may spend in writing the xlog, based on this
information, it may be possible to tune wal_writer_delay GUC.

Or it is possible to integrate the new columns into the existing
pg_stat_bgwriter view also.

Opinions?

Regards,
Hari Babu
Fujitsu Australia

#2Amit Kapila
amit.kapila16@gmail.com
In reply to: Haribabu Kommi (#1)
Re: pg_stat_wal_write statistics view

On Tue, Feb 7, 2017 at 11:47 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

Hi Hackers,

I just want to discuss adding of a new statistics view that provides
the information of wal writing details as follows

+1. I think it will be useful to observe WAL activity.

postgres=# \d pg_stat_wal_writer
View "pg_catalog.pg_stat_wal_writer"
Column | Type | Collation | Nullable |
Default
-----------------------+--------------------------+-----------+----------+---------
num_backend_writes | bigint | |
|
num_total_writes | bigint | | |
num_blocks | bigint | | |
total_write_time | bigint| | |
stats_reset | timestamp with time zone | | |

The columns of the view are
1. Total number of xlog writes that are called from the backend.
2. Total number of xlog writes that are called from both backend
and background workers. (This column can be changed to just
display on the background writes).
3. The number of the blocks that are written.
4. Total write_time of the IO operation it took, this variable data is
filled only when the track_io_timing GUC is enabled.

So, here is *write_time* the total time system has spent in WAL
writing before the last reset?

I think there should be a separate column for write and sync time.

Or it is possible to integrate the new columns into the existing
pg_stat_bgwriter view also.

I feel separate view is better.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

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

#3Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Amit Kapila (#2)
1 attachment(s)
Re: pg_stat_wal_write statistics view

On Wed, Feb 8, 2017 at 9:36 PM, Amit Kapila <amit.kapila16@gmail.com> wrote:

On Tue, Feb 7, 2017 at 11:47 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

Hi Hackers,

I just want to discuss adding of a new statistics view that provides
the information of wal writing details as follows

+1. I think it will be useful to observe WAL activity.

Thanks for your opinion.

postgres=# \d pg_stat_wal_writer

View "pg_catalog.pg_stat_wal_writer"
Column | Type | Collation | Nullable

|

Default
-----------------------+--------------------------+---------

--+----------+---------

num_backend_writes | bigint | |
|
num_total_writes | bigint | | |
num_blocks | bigint | | |
total_write_time | bigint| | |
stats_reset | timestamp with time zone | |

|

The columns of the view are
1. Total number of xlog writes that are called from the backend.
2. Total number of xlog writes that are called from both backend
and background workers. (This column can be changed to just
display on the background writes).
3. The number of the blocks that are written.
4. Total write_time of the IO operation it took, this variable data is
filled only when the track_io_timing GUC is enabled.

So, here is *write_time* the total time system has spent in WAL
writing before the last reset?

total write_time spent in WAL writing "after" the last reset in
milliseconds.

I think there should be a separate column for write and sync time.

Added.

Or it is possible to integrate the new columns into the existing
pg_stat_bgwriter view also.

I feel separate view is better.

Ok.

Following the sample out of the view after regress run.

postgres=# select * from pg_stat_walwrites;
-[ RECORD 1 ]--+------------------------------
backend_writes | 19092
writes | 663
write_blocks | 56116
write_time | 0
sync_time | 3064
stats_reset | 2017-02-15 13:37:09.454314+11

Currently, writer, walwriter and checkpointer processes
are considered as background processes that can do
the wal write mainly.

Here I attached patch that implements the view.
I will add this patch to next commitfest.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_stat_walwrites_view_1.patchapplication/octet-stream; name=pg_stat_walwrites_view_1.patchDownload
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 5b67def..ef6bc1c 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -357,6 +357,14 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
      </row>
 
      <row>
+      <entry><structname>pg_stat_walwrites</><indexterm><primary>pg_stat_walwrites</primary></indexterm></entry>
+      <entry>One row only, showing statistics about the
+       wal writing activity. See
+       <xref linkend="pg-stat-walwrites-view"> for details.
+     </entry>
+     </row>
+
+     <row>
       <entry><structname>pg_stat_database</><indexterm><primary>pg_stat_database</primary></indexterm></entry>
       <entry>One row per database, showing database-wide statistics. See
        <xref linkend="pg-stat-database-view"> for details.
@@ -1833,6 +1841,70 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
    single row, containing global data for the cluster.
   </para>
 
+  <table id="pg-stat-walwrites-view" xreflabel="pg_stat_walwrites">
+   <title><structname>pg_stat_walwrites</structname> View</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>backend_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of wal writes that are carried out by the backend process</entry>
+     </row>
+     <row>
+      <entry><structfield>writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of wal writes that are carried out by background workers such as checkpointer, 
+      writer and walwriter. 
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of wal pages written to the disk</entry>
+     </row>
+     <row>
+      <entry><structfield>write_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of wal write processing where files 
+      are written to disk, in milliseconds.
+      This field data will be populated only when the track_io_timing GUC is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>sync_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of wal write processing where files 
+      are synchronized to disk, in milliseconds.
+      This field data will be populated only when the track_io_timing GUC is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>stats_reset</></entry>
+      <entry><type>timestamp with time zone</type></entry>
+      <entry>Time at which these statistics were last reset</entry>
+     </row>
+     </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_walwrites</structname> view will always have a
+   single row, containing data about the wal writing activity of the cluster.
+  </para>
+
+
   <table id="pg-stat-database-view" xreflabel="pg_stat_database">
    <title><structname>pg_stat_database</structname> View</title>
    <tgroup cols="3">
@@ -2612,6 +2684,8 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        counters shown in the <structname>pg_stat_bgwriter</> view.
        Calling <literal>pg_stat_reset_shared('archiver')</> will zero all the
        counters shown in the <structname>pg_stat_archiver</> view.
+       Calling <literal>pg_stat_reset_shared('walwrites')</> will zero all the
+       counters shown in the <structname>pg_stat_walwrites</> view.
       </entry>
      </row>
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index f23e108..2219433 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -2333,6 +2333,8 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 	int			npages;
 	int			startidx;
 	uint32		startoffset;
+	instr_time	io_start,
+				io_time;
 
 	/* We should always be inside a critical section here */
 	Assert(CritSectionCount > 0);
@@ -2450,6 +2452,11 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			/* OK to write the page(s) */
 			from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ;
 			nbytes = npages * (Size) XLOG_BLCKSZ;
+
+			/* Start timer to acquire start time of the wal write */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			nleft = nbytes;
 			do
 			{
@@ -2470,6 +2477,33 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				from += written;
 			} while (nleft > 0);
 
+			/* calculate the total time spent for wal writing */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				LocalWalWritesStats.m_wal_total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				LocalWalWritesStats.m_wal_total_write_time = 0;
+			}
+
+			/* check whether writer is a background process or not? */
+			if (AmBackgroundWriterProcess() ||
+				AmWalWriterProcess() ||
+				AmCheckpointerProcess())
+			{
+				LocalWalWritesStats.m_writes++;
+			}
+			else
+			{
+				LocalWalWritesStats.m_backend_writes++;
+			}
+
+			LocalWalWritesStats.m_write_blocks += npages;
+
 			/* Update state for write */
 			openLogOff += nbytes;
 			npages = 0;
@@ -2489,8 +2523,25 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			 */
 			if (finishing_seg)
 			{
+				/* Start timer to acquire start time of the wal sync */
+				if (track_io_timing)
+					INSTR_TIME_SET_CURRENT(io_start);
+
 				issue_xlog_fsync(openLogFile, openLogSegNo);
 
+				/* calculate the total time spent for wal sync */
+				if (track_io_timing)
+				{
+					INSTR_TIME_SET_CURRENT(io_time);
+					INSTR_TIME_SUBTRACT(io_time, io_start);
+
+					LocalWalWritesStats.m_wal_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+				}
+				else
+				{
+					LocalWalWritesStats.m_wal_total_sync_time = 0;
+				}
+
 				/* signal that we need to wakeup walsenders later */
 				WalSndWakeupRequest();
 
@@ -2558,7 +2609,24 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				openLogOff = 0;
 			}
 
+			/* Start timer to acquire start time of the wal sync */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			issue_xlog_fsync(openLogFile, openLogSegNo);
+
+			/* calculate the total time spent for wal sync */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				LocalWalWritesStats.m_wal_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				LocalWalWritesStats.m_wal_total_sync_time = 0;
+			}
 		}
 
 		/* signal that we need to wakeup walsenders later */
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 38be9cf..27223e2 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -854,6 +854,16 @@ CREATE VIEW pg_stat_bgwriter AS
         pg_stat_get_buf_alloc() AS buffers_alloc,
         pg_stat_get_bgwriter_stat_reset_time() AS stats_reset;
 
+CREATE VIEW pg_stat_walwrites as 
+	SELECT
+	pg_stat_get_wal_backend_writes() AS backend_writes,
+        pg_stat_get_wal_writes() AS writes,
+        pg_stat_get_wal_write_blocks() AS write_blocks,
+        pg_stat_get_wal_write_time() AS write_time,
+        pg_stat_get_wal_sync_time() AS sync_time,
+	pg_stat_get_wal_stat_reset_time() AS stats_reset;
+
+
 CREATE VIEW pg_stat_progress_vacuum AS
 	SELECT
 		S.pid AS pid, S.datid AS datid, D.datname AS datname,
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index dcb4cf2..1cd623d 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -284,6 +284,7 @@ BackgroundWriterMain(void)
 		 * Send off activity statistics to the stats collector
 		 */
 		pgstat_send_bgwriter();
+		pgstat_send_walwrites();
 
 		if (FirstCallSinceLastCheckpoint())
 		{
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index fe9041f..8549f8f 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -538,6 +538,7 @@ CheckpointerMain(void)
 		 * stats message types.)
 		 */
 		pgstat_send_bgwriter();
+		pgstat_send_walwrites();
 
 		/*
 		 * Sleep until we are signaled or it's time for another checkpoint or
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 7176cf1..fe88411 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -126,6 +126,15 @@ char	   *pgstat_stat_tmpname = NULL;
  */
 PgStat_MsgBgWriter BgWriterStats;
 
+/*
+ * WalWrites Local statistics counters.
+ * The statistics data gets populated in XLogWrite function.
+ * Stored directly in a stats message structure so it can be sent
+ * to stats collector process without needing to copy things around.
+ * We assume this inits to zeroes.
+ */
+PgStat_MsgWalWrites LocalWalWritesStats;
+
 /* ----------
  * Local data
  * ----------
@@ -222,6 +231,7 @@ static int	localNumBackends = 0;
  */
 static PgStat_ArchiverStats archiverStats;
 static PgStat_GlobalStats globalStats;
+static PgStat_WalWritesStats walwritesStats;
 
 /*
  * List of OIDs of databases we need to write out.  If an entry is InvalidOid,
@@ -301,6 +311,7 @@ static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
 static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
 static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
+static void pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len);
 
 /* ------------------------------------------------------------
  * Public functions called from postmaster follow
@@ -834,6 +845,9 @@ pgstat_report_stat(bool force)
 
 	/* Now, send function statistics */
 	pgstat_send_funcstats();
+
+	/* Now, send wal writes statistics */
+	pgstat_send_walwrites();
 }
 
 /*
@@ -1260,11 +1274,13 @@ pgstat_reset_shared_counters(const char *target)
 		msg.m_resettarget = RESET_ARCHIVER;
 	else if (strcmp(target, "bgwriter") == 0)
 		msg.m_resettarget = RESET_BGWRITER;
+	else if (strcmp(target, "walwrites") == 0)
+		msg.m_resettarget = RESET_WALWRITES;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("unrecognized reset target: \"%s\"", target),
-				 errhint("Target must be \"archiver\" or \"bgwriter\".")));
+				 errhint("Target must be \"archiver\" or \"bgwriter\" or \"walwrites\".")));
 
 	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER);
 	pgstat_send(&msg, sizeof(msg));
@@ -2478,6 +2494,21 @@ pgstat_fetch_global(void)
 	return &globalStats;
 }
 
+/*
+ * ---------
+ * pgstat_fetch_stat_walwrites() -
+ *
+ *	Support function for the SQL-callable pgstat* functions. Returns
+ *	a pointer to the walwrites statistics struct.
+ * ---------
+ */
+PgStat_WalWritesStats *
+pgstat_fetch_stat_walwrites(void)
+{
+	backend_read_statsfile();
+
+	return &walwritesStats;
+}
 
 /* ------------------------------------------------------------
  * Functions for management of the shared-memory PgBackendStatus array
@@ -3687,6 +3718,39 @@ pgstat_send_bgwriter(void)
 	MemSet(&BgWriterStats, 0, sizeof(BgWriterStats));
 }
 
+/* ----------
+ * pgstat_send_walwrites() -
+ *
+ *		Send wal writes statistics to the collector
+ * ----------
+ */
+
+void
+pgstat_send_walwrites(void)
+{
+	/* We assume this initializes to zeroes */
+	static const PgStat_MsgWalWrites all_zeroes;
+
+	/*
+	 * This function can be called even if nothing at all has happened. In
+	 * this case, avoid sending a completely empty message to the stats
+	 * collector.
+	 */
+	if (memcmp(&LocalWalWritesStats, &all_zeroes, sizeof(PgStat_MsgWalWrites)) == 0)
+		return;
+
+	/*
+	 * Prepare and send the message
+	 */
+	pgstat_setheader(&LocalWalWritesStats.m_hdr, PGSTAT_MTYPE_WALWRITES);
+	pgstat_send(&LocalWalWritesStats, sizeof(LocalWalWritesStats));
+
+	/*
+	 * Clear out the statistics buffer, so it can be re-used.
+	 */
+	MemSet(&LocalWalWritesStats, 0, sizeof(LocalWalWritesStats));
+}
+
 
 /* ----------
  * PgstatCollectorMain() -
@@ -3903,6 +3967,10 @@ PgstatCollectorMain(int argc, char *argv[])
 					pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len);
 					break;
 
+				case PGSTAT_MTYPE_WALWRITES:
+					pgstat_recv_walwrites((PgStat_MsgWalWrites *) &msg, len);
+					break;
+
 				default:
 					break;
 			}
@@ -4169,6 +4237,12 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 	(void) rc;					/* we'll check for error with ferror */
 
 	/*
+	 * Write wal writes stats struct
+	 */
+	rc = fwrite(&walwritesStats, sizeof(walwritesStats), 1, fpout);
+ 	(void) rc;					/* we'll check for error with ferror */
+
+	/*
 	 * Walk through the database table.
 	 */
 	hash_seq_init(&hstat, pgStatDBHash);
@@ -4425,6 +4499,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	memset(&globalStats, 0, sizeof(globalStats));
 	memset(&archiverStats, 0, sizeof(archiverStats));
+	memset(&walwritesStats, 0, sizeof(walwritesStats));
 
 	/*
 	 * Set the current timestamp (will be kept only in case we can't load an
@@ -4432,6 +4507,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	globalStats.stat_reset_timestamp = GetCurrentTimestamp();
 	archiverStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
+	walwritesStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
 
 	/*
 	 * Try to open the stats file. If it doesn't exist, the backends simply
@@ -4484,6 +4560,16 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	}
 
 	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&walwritesStats, 1, sizeof(walwritesStats), fpin) != sizeof(walwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		goto done;
+	}
+
+	/*
 	 * We found an existing collector stats file. Read it and put all the
 	 * hashtable entries into place.
 	 */
@@ -4772,6 +4858,7 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 	PgStat_StatDBEntry dbentry;
 	PgStat_GlobalStats myGlobalStats;
 	PgStat_ArchiverStats myArchiverStats;
+	PgStat_WalWritesStats myWalwritesStats;
 	FILE	   *fpin;
 	int32		format_id;
 	const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
@@ -4826,6 +4913,18 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 		return false;
 	}
 
+	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&myWalwritesStats, 1, sizeof(myWalwritesStats),
+			  fpin) != sizeof(myWalwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		FreeFile(fpin);
+		return false;
+	}
+
 	/* By default, we're going to return the timestamp of the global file. */
 	*ts = myGlobalStats.stats_timestamp;
 
@@ -5388,6 +5487,12 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
 		memset(&archiverStats, 0, sizeof(archiverStats));
 		archiverStats.stat_reset_timestamp = GetCurrentTimestamp();
 	}
+	else if (msg->m_resettarget == RESET_WALWRITES)
+	{
+		/* Reset the wal writes statistics of the cluster. */
+		memset(&walwritesStats, 0, sizeof(walwritesStats));
+		walwritesStats.stat_reset_timestamp = GetCurrentTimestamp();
+	}
 
 	/*
 	 * Presumably the sender of this message validated the target, don't
@@ -5568,6 +5673,22 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
 }
 
 /* ----------
+ * pgstat_recv_walwrites() -
+ *
+ *	Process a WALWRITES message.
+ * ----------
+ */
+static void
+pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len)
+{
+	walwritesStats.backend_writes += msg->m_backend_writes;
+	walwritesStats.writes += msg->m_writes;
+	walwritesStats.write_blocks += msg->m_write_blocks;
+	walwritesStats.total_write_time += msg->m_wal_total_write_time;
+	walwritesStats.total_sync_time += msg->m_wal_total_sync_time;
+}
+
+/* ----------
  * pgstat_recv_recoveryconflict() -
  *
  *	Process a RECOVERYCONFLICT message.
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index a575d8f..cf0c279 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -280,6 +280,8 @@ WalWriterMain(void)
 		else if (left_till_hibernate > 0)
 			left_till_hibernate--;
 
+		pgstat_send_walwrites();
+
 		/*
 		 * Sleep until we are signaled or WalWriterDelay has elapsed.  If we
 		 * haven't done anything useful for quite some time, lengthen the
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index a987d0d..b8dcfc8 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1521,6 +1521,44 @@ pg_stat_get_buf_alloc(PG_FUNCTION_ARGS)
 }
 
 Datum
+pg_stat_get_wal_backend_writes(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_INT64(pgstat_fetch_stat_walwrites()->backend_writes);
+}
+
+Datum
+pg_stat_get_wal_writes(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_INT64(pgstat_fetch_stat_walwrites()->writes);
+}
+
+Datum
+pg_stat_get_wal_write_blocks(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_INT64(pgstat_fetch_stat_walwrites()->write_blocks);
+}
+
+Datum
+pg_stat_get_wal_write_time(PG_FUNCTION_ARGS)
+{
+	/* time is already in msec, just convert to double for presentation */
+	PG_RETURN_FLOAT8((double) pgstat_fetch_stat_walwrites()->total_write_time);
+}
+
+Datum
+pg_stat_get_wal_sync_time(PG_FUNCTION_ARGS)
+{
+	/* time is already in msec, just convert to double for presentation */
+	PG_RETURN_FLOAT8((double) pgstat_fetch_stat_walwrites()->total_sync_time);
+}
+
+Datum
+pg_stat_get_wal_stat_reset_time(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_TIMESTAMPTZ(pgstat_fetch_stat_walwrites()->stat_reset_timestamp);
+}
+
+Datum
 pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 41c12af..722ad53 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2871,6 +2871,18 @@ DESCR("statistics: number of backend buffer writes that did their own fsync");
 DATA(insert OID = 2859 ( pg_stat_get_buf_alloc			PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_buf_alloc _null_ _null_ _null_ ));
 DESCR("statistics: number of buffer allocations");
 
+DATA(insert OID = 3354 ( pg_stat_get_wal_backend_writes PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_wal_backend_writes _null_ _null_ _null_ ));
+DESCR("statistics: number of backend wal writes statistics");
+DATA(insert OID = 3355 ( pg_stat_get_wal_writes PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_wal_writes _null_ _null_ _null_ ));
+DESCR("statistics: number of background process wal writes statistics");
+DATA(insert OID = 3356 ( pg_stat_get_wal_write_blocks PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_wal_write_blocks _null_ _null_ _null_ ));
+DESCR("statistics: number of wal blocks written statistics");
+DATA(insert OID = 3357 ( pg_stat_get_wal_write_time PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 701 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_wal_write_time _null_ _null_ _null_ ));
+DESCR("statistics: get total time spent in writing wal blocks to disk, in milliseconds");
+DATA(insert OID = 3358 ( pg_stat_get_wal_sync_time PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 701 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_wal_sync_time _null_ _null_ _null_ ));
+DESCR("statistics: get total time spent in synchronizing buffers to disk, in milliseconds");
+DATA(insert OID = 3359 ( pg_stat_get_wal_stat_reset_time PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 1184 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_wal_stat_reset_time _null_ _null_ _null_ ));
+DESCR("statistics: last reset for the wal writes statistics");
 DATA(insert OID = 2978 (  pg_stat_get_function_calls		PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_calls _null_ _null_ _null_ ));
 DESCR("statistics: number of function calls");
 DATA(insert OID = 2979 (  pg_stat_get_function_total_time	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 701 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_total_time _null_ _null_ _null_ ));
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index de8225b..1c62e5a 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -64,7 +64,8 @@ typedef enum StatMsgType
 	PGSTAT_MTYPE_FUNCPURGE,
 	PGSTAT_MTYPE_RECOVERYCONFLICT,
 	PGSTAT_MTYPE_TEMPFILE,
-	PGSTAT_MTYPE_DEADLOCK
+	PGSTAT_MTYPE_DEADLOCK,
+	PGSTAT_MTYPE_WALWRITES
 } StatMsgType;
 
 /* ----------
@@ -119,7 +120,8 @@ typedef struct PgStat_TableCounts
 typedef enum PgStat_Shared_Reset_Target
 {
 	RESET_ARCHIVER,
-	RESET_BGWRITER
+	RESET_BGWRITER,
+	RESET_WALWRITES
 } PgStat_Shared_Reset_Target;
 
 /* Possible object types for resetting single counters */
@@ -423,6 +425,22 @@ typedef struct PgStat_MsgBgWriter
 } PgStat_MsgBgWriter;
 
 /* ----------
+ * PgStat_MsgWalWrites			Sent by the backend or walwriter or writer
+ *								or checkpointer process
+ * ----------
+ */
+typedef struct PgStat_MsgWalWrites
+{
+	PgStat_MsgHdr m_hdr;
+
+	PgStat_Counter m_backend_writes;	/* No of writes by backend */
+	PgStat_Counter m_writes;	/* No of writes by background processes */
+	PgStat_Counter m_write_blocks;		/* Total no of pages written */
+	PgStat_Counter m_wal_total_write_time;		/* times in milliseconds */
+	PgStat_Counter m_wal_total_sync_time;		/* times in milliseconds */
+}	PgStat_MsgWalWrites;
+
+/* ----------
  * PgStat_MsgRecoveryConflict	Sent by the backend upon recovery conflict
  * ----------
  */
@@ -555,6 +573,7 @@ typedef union PgStat_Msg
 	PgStat_MsgFuncpurge msg_funcpurge;
 	PgStat_MsgRecoveryConflict msg_recoveryconflict;
 	PgStat_MsgDeadlock msg_deadlock;
+	PgStat_MsgWalWrites msg_walwrites;
 } PgStat_Msg;
 
 
@@ -694,6 +713,18 @@ typedef struct PgStat_GlobalStats
 	TimestampTz stat_reset_timestamp;
 } PgStat_GlobalStats;
 
+/*
+ * Walwrites statistics kept in the stats collector
+ */
+typedef struct PgStat_WalWritesStats
+{
+	PgStat_Counter backend_writes;		/* No of writes by backend */
+	PgStat_Counter writes;		/* No of writes by background processes */
+	PgStat_Counter write_blocks;	/* Total no of pages written */
+	PgStat_Counter total_write_time;	/* Total write time */
+	PgStat_Counter total_sync_time;		/* Total sync time */
+	TimestampTz stat_reset_timestamp;	/* Last time when the stats reset */
+}	PgStat_WalWritesStats;
 
 /* ----------
  * Backend states
@@ -1008,6 +1039,11 @@ extern char *pgstat_stat_filename;
 extern PgStat_MsgBgWriter BgWriterStats;
 
 /*
+ * Wal writes statistics updated in XLogWrite function
+ */
+extern PgStat_MsgWalWrites LocalWalWritesStats;
+
+/*
  * Updated by pgstat_count_buffer_*_time macros
  */
 extern PgStat_Counter pgStatBlockReadTime;
@@ -1200,6 +1236,7 @@ extern void pgstat_twophase_postabort(TransactionId xid, uint16 info,
 
 extern void pgstat_send_archiver(const char *xlog, bool failed);
 extern void pgstat_send_bgwriter(void);
+extern void pgstat_send_walwrites(void);
 
 /* ----------
  * Support functions for the SQL-callable functions to
@@ -1214,5 +1251,7 @@ extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
 extern int	pgstat_fetch_stat_numbackends(void);
 extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
 extern PgStat_GlobalStats *pgstat_fetch_global(void);
+extern PgStat_WalWritesStats *pgstat_fetch_stat_walwrites(void);
+
 
 #endif   /* PGSTAT_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index c661f1d..402a396 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1945,6 +1945,12 @@ pg_stat_wal_receiver| SELECT s.pid,
     s.conninfo
    FROM pg_stat_get_wal_receiver() s(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, conninfo)
   WHERE (s.pid IS NOT NULL);
+pg_stat_walwrites| SELECT pg_stat_get_wal_backend_writes() AS backend_writes,
+    pg_stat_get_wal_writes() AS writes,
+    pg_stat_get_wal_write_blocks() AS write_blocks,
+    pg_stat_get_wal_write_time() AS write_time,
+    pg_stat_get_wal_sync_time() AS sync_time,
+    pg_stat_get_wal_stat_reset_time() AS stats_reset;
 pg_stat_xact_all_tables| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index d48abd7..b852069 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -67,6 +67,13 @@ select count(*) >= 0 as ok from pg_prepared_xacts;
  t
 (1 row)
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+ ok 
+----
+ t
+(1 row)
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 28e412b..3769880 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -32,6 +32,9 @@ select count(*) = 0 as ok from pg_prepared_statements;
 -- See also prepared_xacts.sql
 select count(*) >= 0 as ok from pg_prepared_xacts;
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
#4Julien Rouhaud
julien.rouhaud@dalibo.com
In reply to: Haribabu Kommi (#3)
Re: pg_stat_wal_write statistics view

On Wed, Feb 15, 2017 at 02:53:44PM +1100, Haribabu Kommi wrote:

Here I attached patch that implements the view.
I will add this patch to next commitfest.

Hello,

I just reviewed the patch.

First, there are some whitespace issues that make git-apply complaining (at
least lines 218 and 396).

Patch is rather straightforward and works as expected, doc compiles without
issue.

I only found some minor issues:

+      <entry>One row only, showing statistics about the
+       wal writing activity. See

+ <entry>Number of wal writes that are carried out by the backend process</entry>

WAL should be uppercase (and for some more occurences).

+      <entry>
+      Number of wal writes that are carried out by background workers such as checkpointer,
+      writer and walwriter.

I guess you meant backgroung processes?

+ This field data will be populated only when the track_io_timing GUC is enabled

(and similar occurences)

track_io_timing should link to <xref linkend="guc-track-io-timing"> instead of
mentionning GUC.

I think you also need to update the track_io_timing description in
sgml/config.sgml to mention this new view.

+           else
+           {
+               LocalWalWritesStats.m_wal_total_write_time = 0;
+           }
(and similar ones)

The brackets seem unnecessary.

I marked the commitfest entry as waiting on author.

--
Julien Rouhaud
http://dalibo.com - http://dalibo.org

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

#5Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Julien Rouhaud (#4)
1 attachment(s)
Re: pg_stat_wal_write statistics view

On Thu, Mar 16, 2017 at 9:55 AM, Julien Rouhaud <julien.rouhaud@dalibo.com>
wrote:

On Wed, Feb 15, 2017 at 02:53:44PM +1100, Haribabu Kommi wrote:

Here I attached patch that implements the view.
I will add this patch to next commitfest.

Hello,

I just reviewed the patch.

Thanks for the review.

First, there are some whitespace issues that make git-apply complaining (at

least lines 218 and 396).

Removed.

Patch is rather straightforward and works as expected, doc compiles without

issue.

I only found some minor issues:

+      <entry>One row only, showing statistics about the
+       wal writing activity. See

+ <entry>Number of wal writes that are carried out by the backend
process</entry>

WAL should be uppercase (and for some more occurences).

Fixed.

+      <entry>
+      Number of wal writes that are carried out by background workers
such as checkpointer,
+      writer and walwriter.

I guess you meant backgroung processes?

Yes, it is background processes. Updated.

+ This field data will be populated only when the track_io_timing

GUC is enabled
(and similar occurences)

track_io_timing should link to <xref linkend="guc-track-io-timing">
instead of
mentionning GUC.

Updated accordingly.

I think you also need to update the track_io_timing description in

sgml/config.sgml to mention this new view.

Added the reference of pg_stat_walwrites in the GUC description.

+           else
+           {
+               LocalWalWritesStats.m_wal_total_write_time = 0;
+           }
(and similar ones)

The brackets seem unnecessary.

Corrected.

Updated patch attached.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_stat_walwrites_view_2.patchapplication/octet-stream; name=pg_stat_walwrites_view_2.patchDownload
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index b6daf96..00b2520 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5707,7 +5707,8 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
         platforms.  You can use the <xref linkend="pgtesttiming"> tool to
         measure the overhead of timing on your system.
         I/O timing information is
-        displayed in <xref linkend="pg-stat-database-view">, in the output of
+        displayed in <xref linkend="pg-stat-database-view">, 
+        <xref linkend="pg-stat-walwrites-view">, in the output of
         <xref linkend="sql-explain"> when the <literal>BUFFERS</> option is
         used, and by <xref linkend="pgstatstatements">.  Only superusers can
         change this setting.
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 9eaf43a..cb8b688 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -357,6 +357,14 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
      </row>
 
      <row>
+      <entry><structname>pg_stat_walwrites</><indexterm><primary>pg_stat_walwrites</primary></indexterm></entry>
+      <entry>One row only, showing statistics about the
+       WAL writing activity. See
+       <xref linkend="pg-stat-walwrites-view"> for details.
+     </entry>
+     </row>
+
+     <row>
       <entry><structname>pg_stat_database</><indexterm><primary>pg_stat_database</primary></indexterm></entry>
       <entry>One row per database, showing database-wide statistics. See
        <xref linkend="pg-stat-database-view"> for details.
@@ -1845,6 +1853,70 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
    single row, containing global data for the cluster.
   </para>
 
+  <table id="pg-stat-walwrites-view" xreflabel="pg_stat_walwrites">
+   <title><structname>pg_stat_walwrites</structname> View</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>backend_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL writes that are carried out by the backend process</entry>
+     </row>
+     <row>
+      <entry><structfield>writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of WAL writes that are carried out by background processes such as checkpointer, 
+      writer and walwriter. 
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk</entry>
+     </row>
+     <row>
+      <entry><structfield>write_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are written to disk, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>sync_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are synchronized to disk, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>stats_reset</></entry>
+      <entry><type>timestamp with time zone</type></entry>
+      <entry>Time at which these statistics were last reset</entry>
+     </row>
+     </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_walwrites</structname> view will always have a
+   single row, containing data about the WAL writing activity of the cluster.
+  </para>
+
+
   <table id="pg-stat-database-view" xreflabel="pg_stat_database">
    <title><structname>pg_stat_database</structname> View</title>
    <tgroup cols="3">
@@ -2624,6 +2696,8 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        counters shown in the <structname>pg_stat_bgwriter</> view.
        Calling <literal>pg_stat_reset_shared('archiver')</> will zero all the
        counters shown in the <structname>pg_stat_archiver</> view.
+       Calling <literal>pg_stat_reset_shared('walwrites')</> will zero all the
+       counters shown in the <structname>pg_stat_walwrites</> view.
       </entry>
      </row>
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 64335f9..b635402 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -2335,6 +2335,8 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 	int			npages;
 	int			startidx;
 	uint32		startoffset;
+	instr_time	io_start,
+				io_time;
 
 	/* We should always be inside a critical section here */
 	Assert(CritSectionCount > 0);
@@ -2452,6 +2454,11 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			/* OK to write the page(s) */
 			from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ;
 			nbytes = npages * (Size) XLOG_BLCKSZ;
+
+			/* Start timer to acquire start time of the wal write */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			nleft = nbytes;
 			do
 			{
@@ -2472,6 +2479,29 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				from += written;
 			} while (nleft > 0);
 
+			/* calculate the total time spent for wal writing */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				LocalWalWritesStats.m_wal_total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+				LocalWalWritesStats.m_wal_total_write_time = 0;
+
+			/* check whether writer is a background process or not? */
+			if (AmBackgroundWriterProcess() ||
+				AmWalWriterProcess() ||
+				AmCheckpointerProcess())
+			{
+				LocalWalWritesStats.m_writes++;
+			}
+			else
+				LocalWalWritesStats.m_backend_writes++;
+
+			LocalWalWritesStats.m_write_blocks += npages;
+
 			/* Update state for write */
 			openLogOff += nbytes;
 			npages = 0;
@@ -2491,8 +2521,23 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			 */
 			if (finishing_seg)
 			{
+				/* Start timer to acquire start time of the wal sync */
+				if (track_io_timing)
+					INSTR_TIME_SET_CURRENT(io_start);
+
 				issue_xlog_fsync(openLogFile, openLogSegNo);
 
+				/* calculate the total time spent for wal sync */
+				if (track_io_timing)
+				{
+					INSTR_TIME_SET_CURRENT(io_time);
+					INSTR_TIME_SUBTRACT(io_time, io_start);
+
+					LocalWalWritesStats.m_wal_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+				}
+				else
+					LocalWalWritesStats.m_wal_total_sync_time = 0;
+
 				/* signal that we need to wakeup walsenders later */
 				WalSndWakeupRequest();
 
@@ -2560,7 +2605,22 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				openLogOff = 0;
 			}
 
+			/* Start timer to acquire start time of the wal sync */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			issue_xlog_fsync(openLogFile, openLogSegNo);
+
+			/* calculate the total time spent for wal sync */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				LocalWalWritesStats.m_wal_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+				LocalWalWritesStats.m_wal_total_sync_time = 0;
 		}
 
 		/* signal that we need to wakeup walsenders later */
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 0bce209..c527fbc 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -854,6 +854,16 @@ CREATE VIEW pg_stat_bgwriter AS
         pg_stat_get_buf_alloc() AS buffers_alloc,
         pg_stat_get_bgwriter_stat_reset_time() AS stats_reset;
 
+CREATE VIEW pg_stat_walwrites as
+	SELECT
+	pg_stat_get_wal_backend_writes() AS backend_writes,
+        pg_stat_get_wal_writes() AS writes,
+        pg_stat_get_wal_write_blocks() AS write_blocks,
+        pg_stat_get_wal_write_time() AS write_time,
+        pg_stat_get_wal_sync_time() AS sync_time,
+	pg_stat_get_wal_stat_reset_time() AS stats_reset;
+
+
 CREATE VIEW pg_stat_progress_vacuum AS
 	SELECT
 		S.pid AS pid, S.datid AS datid, D.datname AS datname,
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index dcb4cf2..1cd623d 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -284,6 +284,7 @@ BackgroundWriterMain(void)
 		 * Send off activity statistics to the stats collector
 		 */
 		pgstat_send_bgwriter();
+		pgstat_send_walwrites();
 
 		if (FirstCallSinceLastCheckpoint())
 		{
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index fe9041f..8549f8f 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -538,6 +538,7 @@ CheckpointerMain(void)
 		 * stats message types.)
 		 */
 		pgstat_send_bgwriter();
+		pgstat_send_walwrites();
 
 		/*
 		 * Sleep until we are signaled or it's time for another checkpoint or
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 7cacb1e..95a4d9b 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -126,6 +126,15 @@ char	   *pgstat_stat_tmpname = NULL;
  */
 PgStat_MsgBgWriter BgWriterStats;
 
+/*
+ * WalWrites Local statistics counters.
+ * The statistics data gets populated in XLogWrite function.
+ * Stored directly in a stats message structure so it can be sent
+ * to stats collector process without needing to copy things around.
+ * We assume this inits to zeroes.
+ */
+PgStat_MsgWalWrites LocalWalWritesStats;
+
 /* ----------
  * Local data
  * ----------
@@ -222,6 +231,7 @@ static int	localNumBackends = 0;
  */
 static PgStat_ArchiverStats archiverStats;
 static PgStat_GlobalStats globalStats;
+static PgStat_WalWritesStats walwritesStats;
 
 /*
  * List of OIDs of databases we need to write out.  If an entry is InvalidOid,
@@ -301,6 +311,7 @@ static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
 static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
 static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
+static void pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len);
 
 /* ------------------------------------------------------------
  * Public functions called from postmaster follow
@@ -834,6 +845,9 @@ pgstat_report_stat(bool force)
 
 	/* Now, send function statistics */
 	pgstat_send_funcstats();
+
+	/* Now, send wal writes statistics */
+	pgstat_send_walwrites();
 }
 
 /*
@@ -1260,11 +1274,13 @@ pgstat_reset_shared_counters(const char *target)
 		msg.m_resettarget = RESET_ARCHIVER;
 	else if (strcmp(target, "bgwriter") == 0)
 		msg.m_resettarget = RESET_BGWRITER;
+	else if (strcmp(target, "walwrites") == 0)
+		msg.m_resettarget = RESET_WALWRITES;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("unrecognized reset target: \"%s\"", target),
-				 errhint("Target must be \"archiver\" or \"bgwriter\".")));
+				 errhint("Target must be \"archiver\" or \"bgwriter\" or \"walwrites\".")));
 
 	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER);
 	pgstat_send(&msg, sizeof(msg));
@@ -2478,6 +2494,21 @@ pgstat_fetch_global(void)
 	return &globalStats;
 }
 
+/*
+ * ---------
+ * pgstat_fetch_stat_walwrites() -
+ *
+ *	Support function for the SQL-callable pgstat* functions. Returns
+ *	a pointer to the walwrites statistics struct.
+ * ---------
+ */
+PgStat_WalWritesStats *
+pgstat_fetch_stat_walwrites(void)
+{
+	backend_read_statsfile();
+
+	return &walwritesStats;
+}
 
 /* ------------------------------------------------------------
  * Functions for management of the shared-memory PgBackendStatus array
@@ -3693,6 +3724,39 @@ pgstat_send_bgwriter(void)
 	MemSet(&BgWriterStats, 0, sizeof(BgWriterStats));
 }
 
+/* ----------
+ * pgstat_send_walwrites() -
+ *
+ *		Send wal writes statistics to the collector
+ * ----------
+ */
+
+void
+pgstat_send_walwrites(void)
+{
+	/* We assume this initializes to zeroes */
+	static const PgStat_MsgWalWrites all_zeroes;
+
+	/*
+	 * This function can be called even if nothing at all has happened. In
+	 * this case, avoid sending a completely empty message to the stats
+	 * collector.
+	 */
+	if (memcmp(&LocalWalWritesStats, &all_zeroes, sizeof(PgStat_MsgWalWrites)) == 0)
+		return;
+
+	/*
+	 * Prepare and send the message
+	 */
+	pgstat_setheader(&LocalWalWritesStats.m_hdr, PGSTAT_MTYPE_WALWRITES);
+	pgstat_send(&LocalWalWritesStats, sizeof(LocalWalWritesStats));
+
+	/*
+	 * Clear out the statistics buffer, so it can be re-used.
+	 */
+	MemSet(&LocalWalWritesStats, 0, sizeof(LocalWalWritesStats));
+}
+
 
 /* ----------
  * PgstatCollectorMain() -
@@ -3909,6 +3973,10 @@ PgstatCollectorMain(int argc, char *argv[])
 					pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len);
 					break;
 
+				case PGSTAT_MTYPE_WALWRITES:
+					pgstat_recv_walwrites((PgStat_MsgWalWrites *) &msg, len);
+					break;
+
 				default:
 					break;
 			}
@@ -4175,6 +4243,12 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 	(void) rc;					/* we'll check for error with ferror */
 
 	/*
+	 * Write wal writes stats struct
+	 */
+	rc = fwrite(&walwritesStats, sizeof(walwritesStats), 1, fpout);
+	(void) rc;					/* we'll check for error with ferror */
+
+	/*
 	 * Walk through the database table.
 	 */
 	hash_seq_init(&hstat, pgStatDBHash);
@@ -4431,6 +4505,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	memset(&globalStats, 0, sizeof(globalStats));
 	memset(&archiverStats, 0, sizeof(archiverStats));
+	memset(&walwritesStats, 0, sizeof(walwritesStats));
 
 	/*
 	 * Set the current timestamp (will be kept only in case we can't load an
@@ -4438,6 +4513,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	globalStats.stat_reset_timestamp = GetCurrentTimestamp();
 	archiverStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
+	walwritesStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
 
 	/*
 	 * Try to open the stats file. If it doesn't exist, the backends simply
@@ -4490,6 +4566,16 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	}
 
 	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&walwritesStats, 1, sizeof(walwritesStats), fpin) != sizeof(walwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		goto done;
+	}
+
+	/*
 	 * We found an existing collector stats file. Read it and put all the
 	 * hashtable entries into place.
 	 */
@@ -4778,6 +4864,7 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 	PgStat_StatDBEntry dbentry;
 	PgStat_GlobalStats myGlobalStats;
 	PgStat_ArchiverStats myArchiverStats;
+	PgStat_WalWritesStats myWalwritesStats;
 	FILE	   *fpin;
 	int32		format_id;
 	const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
@@ -4832,6 +4919,18 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 		return false;
 	}
 
+	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&myWalwritesStats, 1, sizeof(myWalwritesStats),
+			  fpin) != sizeof(myWalwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		FreeFile(fpin);
+		return false;
+	}
+
 	/* By default, we're going to return the timestamp of the global file. */
 	*ts = myGlobalStats.stats_timestamp;
 
@@ -5394,6 +5493,12 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
 		memset(&archiverStats, 0, sizeof(archiverStats));
 		archiverStats.stat_reset_timestamp = GetCurrentTimestamp();
 	}
+	else if (msg->m_resettarget == RESET_WALWRITES)
+	{
+		/* Reset the wal writes statistics of the cluster. */
+		memset(&walwritesStats, 0, sizeof(walwritesStats));
+		walwritesStats.stat_reset_timestamp = GetCurrentTimestamp();
+	}
 
 	/*
 	 * Presumably the sender of this message validated the target, don't
@@ -5574,6 +5679,22 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
 }
 
 /* ----------
+ * pgstat_recv_walwrites() -
+ *
+ *	Process a WALWRITES message.
+ * ----------
+ */
+static void
+pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len)
+{
+	walwritesStats.backend_writes += msg->m_backend_writes;
+	walwritesStats.writes += msg->m_writes;
+	walwritesStats.write_blocks += msg->m_write_blocks;
+	walwritesStats.total_write_time += msg->m_wal_total_write_time;
+	walwritesStats.total_sync_time += msg->m_wal_total_sync_time;
+}
+
+/* ----------
  * pgstat_recv_recoveryconflict() -
  *
  *	Process a RECOVERYCONFLICT message.
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index a575d8f..cf0c279 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -280,6 +280,8 @@ WalWriterMain(void)
 		else if (left_till_hibernate > 0)
 			left_till_hibernate--;
 
+		pgstat_send_walwrites();
+
 		/*
 		 * Sleep until we are signaled or WalWriterDelay has elapsed.  If we
 		 * haven't done anything useful for quite some time, lengthen the
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index a987d0d..b8dcfc8 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1521,6 +1521,44 @@ pg_stat_get_buf_alloc(PG_FUNCTION_ARGS)
 }
 
 Datum
+pg_stat_get_wal_backend_writes(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_INT64(pgstat_fetch_stat_walwrites()->backend_writes);
+}
+
+Datum
+pg_stat_get_wal_writes(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_INT64(pgstat_fetch_stat_walwrites()->writes);
+}
+
+Datum
+pg_stat_get_wal_write_blocks(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_INT64(pgstat_fetch_stat_walwrites()->write_blocks);
+}
+
+Datum
+pg_stat_get_wal_write_time(PG_FUNCTION_ARGS)
+{
+	/* time is already in msec, just convert to double for presentation */
+	PG_RETURN_FLOAT8((double) pgstat_fetch_stat_walwrites()->total_write_time);
+}
+
+Datum
+pg_stat_get_wal_sync_time(PG_FUNCTION_ARGS)
+{
+	/* time is already in msec, just convert to double for presentation */
+	PG_RETURN_FLOAT8((double) pgstat_fetch_stat_walwrites()->total_sync_time);
+}
+
+Datum
+pg_stat_get_wal_stat_reset_time(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_TIMESTAMPTZ(pgstat_fetch_stat_walwrites()->stat_reset_timestamp);
+}
+
+Datum
 pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 3d5d866..216c51d 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2900,6 +2900,18 @@ DESCR("statistics: number of backend buffer writes that did their own fsync");
 DATA(insert OID = 2859 ( pg_stat_get_buf_alloc			PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_buf_alloc _null_ _null_ _null_ ));
 DESCR("statistics: number of buffer allocations");
 
+DATA(insert OID = 3354 ( pg_stat_get_wal_backend_writes PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_wal_backend_writes _null_ _null_ _null_ ));
+DESCR("statistics: number of backend wal writes statistics");
+DATA(insert OID = 3355 ( pg_stat_get_wal_writes PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_wal_writes _null_ _null_ _null_ ));
+DESCR("statistics: number of background process wal writes statistics");
+DATA(insert OID = 3356 ( pg_stat_get_wal_write_blocks PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_wal_write_blocks _null_ _null_ _null_ ));
+DESCR("statistics: number of wal blocks written statistics");
+DATA(insert OID = 3357 ( pg_stat_get_wal_write_time PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 701 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_wal_write_time _null_ _null_ _null_ ));
+DESCR("statistics: get total time spent in writing wal blocks to disk, in milliseconds");
+DATA(insert OID = 3358 ( pg_stat_get_wal_sync_time PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 701 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_wal_sync_time _null_ _null_ _null_ ));
+DESCR("statistics: get total time spent in synchronizing buffers to disk, in milliseconds");
+DATA(insert OID = 3359 ( pg_stat_get_wal_stat_reset_time PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 1184 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_wal_stat_reset_time _null_ _null_ _null_ ));
+DESCR("statistics: last reset for the wal writes statistics");
 DATA(insert OID = 2978 (  pg_stat_get_function_calls		PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_calls _null_ _null_ _null_ ));
 DESCR("statistics: number of function calls");
 DATA(insert OID = 2979 (  pg_stat_get_function_total_time	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 701 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_total_time _null_ _null_ _null_ ));
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 60c78d1..c11055a 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -64,7 +64,8 @@ typedef enum StatMsgType
 	PGSTAT_MTYPE_FUNCPURGE,
 	PGSTAT_MTYPE_RECOVERYCONFLICT,
 	PGSTAT_MTYPE_TEMPFILE,
-	PGSTAT_MTYPE_DEADLOCK
+	PGSTAT_MTYPE_DEADLOCK,
+	PGSTAT_MTYPE_WALWRITES
 } StatMsgType;
 
 /* ----------
@@ -119,7 +120,8 @@ typedef struct PgStat_TableCounts
 typedef enum PgStat_Shared_Reset_Target
 {
 	RESET_ARCHIVER,
-	RESET_BGWRITER
+	RESET_BGWRITER,
+	RESET_WALWRITES
 } PgStat_Shared_Reset_Target;
 
 /* Possible object types for resetting single counters */
@@ -423,6 +425,22 @@ typedef struct PgStat_MsgBgWriter
 } PgStat_MsgBgWriter;
 
 /* ----------
+ * PgStat_MsgWalWrites			Sent by the backend or walwriter or writer
+ *								or checkpointer process
+ * ----------
+ */
+typedef struct PgStat_MsgWalWrites
+{
+	PgStat_MsgHdr m_hdr;
+
+	PgStat_Counter m_backend_writes;	/* No of writes by backend */
+	PgStat_Counter m_writes;	/* No of writes by background processes */
+	PgStat_Counter m_write_blocks;		/* Total no of pages written */
+	PgStat_Counter m_wal_total_write_time;		/* times in milliseconds */
+	PgStat_Counter m_wal_total_sync_time;		/* times in milliseconds */
+}	PgStat_MsgWalWrites;
+
+/* ----------
  * PgStat_MsgRecoveryConflict	Sent by the backend upon recovery conflict
  * ----------
  */
@@ -555,6 +573,7 @@ typedef union PgStat_Msg
 	PgStat_MsgFuncpurge msg_funcpurge;
 	PgStat_MsgRecoveryConflict msg_recoveryconflict;
 	PgStat_MsgDeadlock msg_deadlock;
+	PgStat_MsgWalWrites msg_walwrites;
 } PgStat_Msg;
 
 
@@ -694,6 +713,18 @@ typedef struct PgStat_GlobalStats
 	TimestampTz stat_reset_timestamp;
 } PgStat_GlobalStats;
 
+/*
+ * Walwrites statistics kept in the stats collector
+ */
+typedef struct PgStat_WalWritesStats
+{
+	PgStat_Counter backend_writes;		/* No of writes by backend */
+	PgStat_Counter writes;		/* No of writes by background processes */
+	PgStat_Counter write_blocks;	/* Total no of pages written */
+	PgStat_Counter total_write_time;	/* Total write time */
+	PgStat_Counter total_sync_time;		/* Total sync time */
+	TimestampTz stat_reset_timestamp;	/* Last time when the stats reset */
+}	PgStat_WalWritesStats;
 
 /* ----------
  * Backend states
@@ -1010,6 +1041,11 @@ extern char *pgstat_stat_filename;
 extern PgStat_MsgBgWriter BgWriterStats;
 
 /*
+ * Wal writes statistics updated in XLogWrite function
+ */
+extern PgStat_MsgWalWrites LocalWalWritesStats;
+
+/*
  * Updated by pgstat_count_buffer_*_time macros
  */
 extern PgStat_Counter pgStatBlockReadTime;
@@ -1202,6 +1238,7 @@ extern void pgstat_twophase_postabort(TransactionId xid, uint16 info,
 
 extern void pgstat_send_archiver(const char *xlog, bool failed);
 extern void pgstat_send_bgwriter(void);
+extern void pgstat_send_walwrites(void);
 
 /* ----------
  * Support functions for the SQL-callable functions to
@@ -1216,5 +1253,7 @@ extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
 extern int	pgstat_fetch_stat_numbackends(void);
 extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
 extern PgStat_GlobalStats *pgstat_fetch_global(void);
+extern PgStat_WalWritesStats *pgstat_fetch_stat_walwrites(void);
+
 
 #endif   /* PGSTAT_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index bd13ae6..7f23d9a 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1945,6 +1945,12 @@ pg_stat_wal_receiver| SELECT s.pid,
     s.conninfo
    FROM pg_stat_get_wal_receiver() s(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, conninfo)
   WHERE (s.pid IS NOT NULL);
+pg_stat_walwrites| SELECT pg_stat_get_wal_backend_writes() AS backend_writes,
+    pg_stat_get_wal_writes() AS writes,
+    pg_stat_get_wal_write_blocks() AS write_blocks,
+    pg_stat_get_wal_write_time() AS write_time,
+    pg_stat_get_wal_sync_time() AS sync_time,
+    pg_stat_get_wal_stat_reset_time() AS stats_reset;
 pg_stat_xact_all_tables| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 568b783..8448cac 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -67,6 +67,13 @@ select count(*) >= 0 as ok from pg_prepared_xacts;
  t
 (1 row)
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+ ok 
+----
+ t
+(1 row)
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 28e412b..3769880 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -32,6 +32,9 @@ select count(*) = 0 as ok from pg_prepared_statements;
 -- See also prepared_xacts.sql
 select count(*) >= 0 as ok from pg_prepared_xacts;
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
#6vinayak
Pokale_Vinayak_q3@lab.ntt.co.jp
In reply to: Haribabu Kommi (#5)
Re: pg_stat_wal_write statistics view

On 2017/03/16 10:34, Haribabu Kommi wrote:

Updated patch attached.

The patch looks good to me.

How about rename the view as "pg_stat_walwriter"?
The columns of view :
backend_writes -> backend_wal_writes
writes-> background_wal_writes
write_blocks-> wal_write_blocks
write_time->wal_write_time
sync_time->wal_sync_time

Regards,
Vinayak Pokale
NTT Open Source Software Center

#7Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: vinayak (#6)
Re: pg_stat_wal_write statistics view

On Thu, Mar 16, 2017 at 4:15 PM, vinayak <Pokale_Vinayak_q3@lab.ntt.co.jp>
wrote:

On 2017/03/16 10:34, Haribabu Kommi wrote:

Updated patch attached.

The patch looks good to me.

Thanks for the review.

How about rename the view as "pg_stat_walwriter"?

With the use of name "walwriter" instead of "walwrites", the
user may confuse that this view is used for displaying
walwriter processes statistics. But actually it is showing
the WAL writes activity in the instance. Because of this
reason, I went with the name of "walwrites".

The columns of view :
backend_writes -> backend_wal_writes
writes-> background_wal_writes
write_blocks-> wal_write_blocks
write_time->wal_write_time
sync_time->wal_sync_time

As the view name already contains WAL, I am not sure
whether is it required to include WAL in every column?
I am fine to change if others have the same opinion of
adding WAL to column names.

Regards,
Hari Babu
Fujitsu Australia

#8vinayak
Pokale_Vinayak_q3@lab.ntt.co.jp
In reply to: Haribabu Kommi (#7)
Re: pg_stat_wal_write statistics view

On 2017/03/16 14:46, Haribabu Kommi wrote:

On Thu, Mar 16, 2017 at 4:15 PM, vinayak
<Pokale_Vinayak_q3@lab.ntt.co.jp
<mailto:Pokale_Vinayak_q3@lab.ntt.co.jp>> wrote:

On 2017/03/16 10:34, Haribabu Kommi wrote:

Updated patch attached.

The patch looks good to me.

Thanks for the review.

How about rename the view as "pg_stat_walwriter"?

With the use of name "walwriter" instead of "walwrites", the
user may confuse that this view is used for displaying
walwriter processes statistics. But actually it is showing
the WAL writes activity in the instance. Because of this
reason, I went with the name of "walwrites".

Understood. Thanks.

The columns of view :
backend_writes -> backend_wal_writes
writes-> background_wal_writes
write_blocks-> wal_write_blocks
write_time->wal_write_time
sync_time->wal_sync_time

As the view name already contains WAL, I am not sure
whether is it required to include WAL in every column?
I am fine to change if others have the same opinion of
adding WAL to column names.

Ok.

Regards,
Vinayak Pokale
NTT Open Source Software Center

#9David Steele
david@pgmasters.net
In reply to: vinayak (#8)
Re: pg_stat_wal_write statistics view

On 3/16/17 1:54 AM, vinayak wrote:

On 2017/03/16 14:46, Haribabu Kommi wrote:

As the view name already contains WAL, I am not sure
whether is it required to include WAL in every column?
I am fine to change if others have the same opinion of
adding WAL to column names.

Ok.

So what is the status of this patch now? Should it be marked "Ready for
Committer"?

--
-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

#10Fujii Masao
masao.fujii@gmail.com
In reply to: Haribabu Kommi (#3)
Re: pg_stat_wal_write statistics view

On Wed, Feb 15, 2017 at 12:53 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

On Wed, Feb 8, 2017 at 9:36 PM, Amit Kapila <amit.kapila16@gmail.com> wrote:

On Tue, Feb 7, 2017 at 11:47 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

Hi Hackers,

I just want to discuss adding of a new statistics view that provides
the information of wal writing details as follows

+1. I think it will be useful to observe WAL activity.

Thanks for your opinion.

postgres=# \d pg_stat_wal_writer
View "pg_catalog.pg_stat_wal_writer"
Column | Type | Collation | Nullable
|
Default

-----------------------+--------------------------+-----------+----------+---------
num_backend_writes | bigint | |
|
num_total_writes | bigint | | |
num_blocks | bigint | | |
total_write_time | bigint| | |
stats_reset | timestamp with time zone | |
|

The columns of the view are
1. Total number of xlog writes that are called from the backend.
2. Total number of xlog writes that are called from both backend
and background workers. (This column can be changed to just
display on the background writes).
3. The number of the blocks that are written.
4. Total write_time of the IO operation it took, this variable data is
filled only when the track_io_timing GUC is enabled.

So, here is *write_time* the total time system has spent in WAL
writing before the last reset?

total write_time spent in WAL writing "after" the last reset in
milliseconds.

I think there should be a separate column for write and sync time.

Added.

Or it is possible to integrate the new columns into the existing
pg_stat_bgwriter view also.

I feel separate view is better.

Ok.

Following the sample out of the view after regress run.

postgres=# select * from pg_stat_walwrites;
-[ RECORD 1 ]--+------------------------------
backend_writes | 19092
writes | 663
write_blocks | 56116
write_time | 0
sync_time | 3064
stats_reset | 2017-02-15 13:37:09.454314+11

Currently, writer, walwriter and checkpointer processes
are considered as background processes that can do
the wal write mainly.

I'm not sure if this categorization is good. You told that this view is useful
to tune walwriter parameters at the top of this thread. If so, ISTM that
the information about walwriter's activity should be separated from others.

What about other processes which *can* write WAL, for example walsender
(e.g., BASE_BACKUP can cause WAL record), startup process (e.g., end-of-
recovery checkpoint) and logical replication worker (Not sure if it always
works with synchronous_commit=off, though). There might be other processes
which can write WAL.

Why didn't you separate "write_blocks", "write_time" and "sync_time" per
the process category, like "backend_writes" and "writes"?

This view doesn't seem to take into consideration the WAL writing and flushing
during creating new WAL segment file.

I think that it's better to make this view report also the number of WAL pages
which are written when wal_buffer is full. This information is useful to
tune the size of wal_buffers. This was proposed by Nagayasu before.
/messages/by-id/4FF824F3.5090407@uptime.jp

Regards,

--
Fujii Masao

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

#11Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Fujii Masao (#10)
Re: pg_stat_wal_write statistics view

On Sat, Mar 25, 2017 at 6:40 AM, Fujii Masao <masao.fujii@gmail.com> wrote:

On Wed, Feb 15, 2017 at 12:53 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

On Wed, Feb 8, 2017 at 9:36 PM, Amit Kapila <amit.kapila16@gmail.com>

wrote:

On Tue, Feb 7, 2017 at 11:47 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

Hi Hackers,

I just want to discuss adding of a new statistics view that provides
the information of wal writing details as follows

+1. I think it will be useful to observe WAL activity.

Thanks for your opinion.

postgres=# \d pg_stat_wal_writer
View "pg_catalog.pg_stat_wal_writer"
Column | Type | Collation |

Nullable

|
Default

-----------------------+--------------------------+---------

--+----------+---------

num_backend_writes | bigint | |
|
num_total_writes | bigint | | |
num_blocks | bigint | | |
total_write_time | bigint| | |
stats_reset | timestamp with time zone | |
|

The columns of the view are
1. Total number of xlog writes that are called from the backend.
2. Total number of xlog writes that are called from both backend
and background workers. (This column can be changed to just
display on the background writes).
3. The number of the blocks that are written.
4. Total write_time of the IO operation it took, this variable data is
filled only when the track_io_timing GUC is enabled.

So, here is *write_time* the total time system has spent in WAL
writing before the last reset?

total write_time spent in WAL writing "after" the last reset in
milliseconds.

I think there should be a separate column for write and sync time.

Added.

Or it is possible to integrate the new columns into the existing
pg_stat_bgwriter view also.

I feel separate view is better.

Ok.

Following the sample out of the view after regress run.

postgres=# select * from pg_stat_walwrites;
-[ RECORD 1 ]--+------------------------------
backend_writes | 19092
writes | 663
write_blocks | 56116
write_time | 0
sync_time | 3064
stats_reset | 2017-02-15 13:37:09.454314+11

Currently, writer, walwriter and checkpointer processes
are considered as background processes that can do
the wal write mainly.

Thanks for the review.

I'm not sure if this categorization is good. You told that this view is

useful
to tune walwriter parameters at the top of this thread. If so, ISTM that
the information about walwriter's activity should be separated from others.

Yes, that's correct. First I thought of providing the statistics of
walwriter, but
later in development, it turned into showing statistics of all wal write
activity
of background processes also to differentiate the actual write by the
backends.

What about other processes which *can* write WAL, for example walsender
(e.g., BASE_BACKUP can cause WAL record), startup process (e.g., end-of-
recovery checkpoint) and logical replication worker (Not sure if it always
works with synchronous_commit=off, though). There might be other processes
which can write WAL

It is possible to add the walsender, stratup and other processes easily,
but not
background workers that does some wal write operations until unless they
report the stats with pgstat_report_stat(). Is it fine to ignore the
workers that
does not report the stats?

Why didn't you separate "write_blocks", "write_time" and "sync_time" per
the process category, like "backend_writes" and "writes"?

Ok. I will add those columns.

This view doesn't seem to take into consideration the WAL writing and
flushing
during creating new WAL segment file.

I think that it's better to make this view report also the number of WAL
pages
which are written when wal_buffer is full. This information is useful to
tune the size of wal_buffers. This was proposed by Nagayasu before.
/messages/by-id/4FF824F3.5090407@uptime.jp

Ok. But this new column just shows how many times the WAL buffers are
flushed
because of wal buffers are full. Not the WAL pages that are actually
flushed because
of wal buffers full as a separate column.

Regards,
Hari Babu
Fujitsu Australia

#12Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Haribabu Kommi (#11)
1 attachment(s)
Re: pg_stat_wal_write statistics view

On Mon, Mar 27, 2017 at 1:27 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Sat, Mar 25, 2017 at 6:40 AM, Fujii Masao <masao.fujii@gmail.com>
wrote:

On Wed, Feb 15, 2017 at 12:53 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

On Wed, Feb 8, 2017 at 9:36 PM, Amit Kapila <amit.kapila16@gmail.com>

wrote:

On Tue, Feb 7, 2017 at 11:47 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

Hi Hackers,

I just want to discuss adding of a new statistics view that provides
the information of wal writing details as follows

+1. I think it will be useful to observe WAL activity.

Thanks for your opinion.

postgres=# \d pg_stat_wal_writer
View "pg_catalog.pg_stat_wal_writer"
Column | Type | Collation |

Nullable

|
Default

-----------------------+--------------------------+---------

--+----------+---------

num_backend_writes | bigint | |
|
num_total_writes | bigint | |

|

num_blocks | bigint | | |
total_write_time | bigint| | |
stats_reset | timestamp with time zone | |
|

The columns of the view are
1. Total number of xlog writes that are called from the backend.
2. Total number of xlog writes that are called from both backend
and background workers. (This column can be changed to just
display on the background writes).
3. The number of the blocks that are written.
4. Total write_time of the IO operation it took, this variable data

is

filled only when the track_io_timing GUC is enabled.

So, here is *write_time* the total time system has spent in WAL
writing before the last reset?

total write_time spent in WAL writing "after" the last reset in
milliseconds.

I think there should be a separate column for write and sync time.

Added.

Or it is possible to integrate the new columns into the existing
pg_stat_bgwriter view also.

I feel separate view is better.

Ok.

Following the sample out of the view after regress run.

postgres=# select * from pg_stat_walwrites;
-[ RECORD 1 ]--+------------------------------
backend_writes | 19092
writes | 663
write_blocks | 56116
write_time | 0
sync_time | 3064
stats_reset | 2017-02-15 13:37:09.454314+11

Currently, writer, walwriter and checkpointer processes
are considered as background processes that can do
the wal write mainly.

Thanks for the review.

I'm not sure if this categorization is good. You told that this view is

useful
to tune walwriter parameters at the top of this thread. If so, ISTM that
the information about walwriter's activity should be separated from
others.

Yes, that's correct. First I thought of providing the statistics of
walwriter, but
later in development, it turned into showing statistics of all wal write
activity
of background processes also to differentiate the actual write by the
backends.

What about other processes which *can* write WAL, for example walsender
(e.g., BASE_BACKUP can cause WAL record), startup process (e.g., end-of-
recovery checkpoint) and logical replication worker (Not sure if it always
works with synchronous_commit=off, though). There might be other processes
which can write WAL

It is possible to add the walsender, stratup and other processes easily,
but not
background workers that does some wal write operations until unless they
report the stats with pgstat_report_stat(). Is it fine to ignore the
workers that
does not report the stats?

Added stats collection for walsender, statrup and autovacuum processes.
The background workers that call pgstat_report_stat() function will
automatically
included.

Why didn't you separate "write_blocks", "write_time" and "sync_time" per
the process category, like "backend_writes" and "writes"?

Ok. I will add those columns.

Added the new columns to differentiate backend and background processes.

This view doesn't seem to take into consideration the WAL writing and

flushing
during creating new WAL segment file.

I think that it's better to make this view report also the number of WAL
pages
which are written when wal_buffer is full. This information is useful to
tune the size of wal_buffers. This was proposed by Nagayasu before.
/messages/by-id/4FF824F3.5090407@uptime.jp

Ok. But this new column just shows how many times the WAL buffers are
flushed
because of wal buffers are full. Not the WAL pages that are actually
flushed because
of wal buffers full as a separate column.

Added a new column to display the dirty writes from both backend and
background processes.

update patch attached.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_stat_walwrites_view_3.patchapplication/octet-stream; name=pg_stat_walwrites_view_3.patchDownload
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index ac339fb..ed833e0 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5732,7 +5732,8 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
         platforms.  You can use the <xref linkend="pgtesttiming"> tool to
         measure the overhead of timing on your system.
         I/O timing information is
-        displayed in <xref linkend="pg-stat-database-view">, in the output of
+        displayed in <xref linkend="pg-stat-database-view">, 
+        <xref linkend="pg-stat-walwrites-view">, in the output of
         <xref linkend="sql-explain"> when the <literal>BUFFERS</> option is
         used, and by <xref linkend="pgstatstatements">.  Only superusers can
         change this setting.
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 9856968..b5ed688 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -357,6 +357,14 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
      </row>
 
      <row>
+      <entry><structname>pg_stat_walwrites</><indexterm><primary>pg_stat_walwrites</primary></indexterm></entry>
+      <entry>One row only, showing statistics about the
+       WAL writing activity. See
+       <xref linkend="pg-stat-walwrites-view"> for details.
+     </entry>
+     </row>
+
+     <row>
       <entry><structname>pg_stat_database</><indexterm><primary>pg_stat_database</primary></indexterm></entry>
       <entry>One row per database, showing database-wide statistics. See
        <xref linkend="pg-stat-database-view"> for details.
@@ -2207,6 +2215,108 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
    single row, containing global data for the cluster.
   </para>
 
+  <table id="pg-stat-walwrites-view" xreflabel="pg_stat_walwrites">
+   <title><structname>pg_stat_walwrites</structname> View</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of WAL writes that are carried out by background processes and workers. 
+      </entry>
+     </row>
+    <row>
+      <entry><structfield>backend_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL writes that are carried out by the backend process</entry>
+     </row>
+     <row>
+     <entry><structfield>dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by background processes and workers
+      when the <xref linkend="guc-wal-buffers"> are full.
+      </entry>
+     </row>
+    <row>
+      <entry><structfield>backend_dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by the backend processes when 
+      the <xref linkend="guc-wal-buffers"> are full. 
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the background processes/workers</entry>
+     </row>
+     <row>
+      <entry><structfield>backend_write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the backend processes</entry>
+     </row>
+     <row>
+      <entry><structfield>write_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are written to disk by the background processes/workers, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>backend_write_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are written to disk by the backend processes, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>sync_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are synchronized to disk by the background processes/workers, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>backend_sync_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are synchronized to disk by the backend processes, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>stats_reset</></entry>
+      <entry><type>timestamp with time zone</type></entry>
+      <entry>Time at which these statistics were last reset</entry>
+     </row>
+     </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_walwrites</structname> view will always have a
+   single row, containing data about the WAL writing activity of the cluster.
+  </para>
+
+
   <table id="pg-stat-database-view" xreflabel="pg_stat_database">
    <title><structname>pg_stat_database</structname> View</title>
    <tgroup cols="3">
@@ -2986,6 +3096,8 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        counters shown in the <structname>pg_stat_bgwriter</> view.
        Calling <literal>pg_stat_reset_shared('archiver')</> will zero all the
        counters shown in the <structname>pg_stat_archiver</> view.
+       Calling <literal>pg_stat_reset_shared('walwrites')</> will zero all the
+       counters shown in the <structname>pg_stat_walwrites</> view.
       </entry>
      </row>
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 61ca81d..05a6903 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -860,6 +860,7 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void);
 
 static void AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic);
 static bool XLogCheckpointNeeded(XLogSegNo new_segno);
+static bool am_background_process(void);
 static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible);
 static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath,
 					   bool find_free, XLogSegNo max_segno,
@@ -2109,6 +2110,10 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic)
 					WriteRqst.Write = OldPageRqstPtr;
 					WriteRqst.Flush = 0;
 					XLogWrite(WriteRqst, false);
+					if (am_background_process())
+						LocalWalWritesStats.m_dirty_writes++;
+					else
+						LocalWalWritesStats.m_backend_dirty_writes++;
 					LWLockRelease(WALWriteLock);
 					TRACE_POSTGRESQL_WAL_BUFFER_WRITE_DIRTY_DONE();
 				}
@@ -2319,6 +2324,34 @@ XLogCheckpointNeeded(XLogSegNo new_segno)
 }
 
 /*
+ * Check whether the current process is a normal backend or not.
+ * This function checks for the background processes that does
+ * some WAL write activity only and other background processes
+ * are not considered. It considers all the background workers
+ * as WAL write activity workers.
+ *
+ * Returns FALSE - when the current process is a normal backend
+ *		   TRUE - when the current process a background process/worker
+ */
+static bool
+am_background_process()
+{
+	/* check whether current process is a background process/worker? */
+	if (AmBackgroundWriterProcess() ||
+		AmWalWriterProcess() ||
+		AmCheckpointerProcess() ||
+		AmStartupProcess() ||
+		IsBackgroundWorker ||
+		am_walsender ||
+		am_autovacuum_worker)
+	{
+		return true;
+	}
+
+	return false;
+}
+
+/*
  * Write and/or fsync the log at least as far as WriteRqst indicates.
  *
  * If flexible == TRUE, we don't have to write as far as WriteRqst, but
@@ -2341,6 +2374,9 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 	int			npages;
 	int			startidx;
 	uint32		startoffset;
+	instr_time	io_start,
+				io_time;
+	bool		is_background_process = am_background_process();
 
 	/* We should always be inside a critical section here */
 	Assert(CritSectionCount > 0);
@@ -2458,6 +2494,11 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			/* OK to write the page(s) */
 			from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ;
 			nbytes = npages * (Size) XLOG_BLCKSZ;
+
+			/* Start timer to acquire start time of the wal write */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			nleft = nbytes;
 			do
 			{
@@ -2480,6 +2521,34 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				from += written;
 			} while (nleft > 0);
 
+			/* calculate the total time spent for wal writing */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				if (is_background_process)
+					LocalWalWritesStats.m_total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+				else
+					LocalWalWritesStats.m_backend_total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				LocalWalWritesStats.m_total_write_time = 0;
+				LocalWalWritesStats.m_backend_total_write_time = 0;
+			}
+
+			/* check whether writer is a normal backend or not? */
+			if (is_background_process)
+				LocalWalWritesStats.m_writes++;
+			else
+				LocalWalWritesStats.m_backend_writes++;
+
+			if (is_background_process)
+				LocalWalWritesStats.m_write_blocks += npages;
+			else
+				LocalWalWritesStats.m_backend_write_blocks += npages;
+
 			/* Update state for write */
 			openLogOff += nbytes;
 			npages = 0;
@@ -2499,8 +2568,29 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			 */
 			if (finishing_seg)
 			{
+				/* Start timer to acquire start time of the wal sync */
+				if (track_io_timing)
+					INSTR_TIME_SET_CURRENT(io_start);
+
 				issue_xlog_fsync(openLogFile, openLogSegNo);
 
+				/* calculate the total time spent for wal sync */
+				if (track_io_timing)
+				{
+					INSTR_TIME_SET_CURRENT(io_time);
+					INSTR_TIME_SUBTRACT(io_time, io_start);
+
+					if (is_background_process)
+						LocalWalWritesStats.m_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+					else
+						LocalWalWritesStats.m_backend_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+				}
+				else
+				{
+					LocalWalWritesStats.m_total_sync_time = 0;
+					LocalWalWritesStats.m_backend_total_sync_time = 0;
+				}
+
 				/* signal that we need to wakeup walsenders later */
 				WalSndWakeupRequest();
 
@@ -2568,7 +2658,28 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				openLogOff = 0;
 			}
 
+			/* Start timer to acquire start time of the wal sync */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			issue_xlog_fsync(openLogFile, openLogSegNo);
+
+			/* calculate the total time spent for wal sync */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				if (is_background_process)
+					LocalWalWritesStats.m_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+				else
+					LocalWalWritesStats.m_backend_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				LocalWalWritesStats.m_total_sync_time = 0;
+				LocalWalWritesStats.m_backend_total_sync_time = 0;
+			}
 		}
 
 		/* signal that we need to wakeup walsenders later */
@@ -7642,6 +7753,9 @@ StartupXLOG(void)
 		}
 	}
 
+	/* Report WAL write activity now */
+	pgstat_send_walwrites()
+
 	/*
 	 * Preallocate additional log files, if wanted.
 	 */
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index d357c8b..f424c9d 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -891,6 +891,9 @@ CREATE VIEW pg_stat_bgwriter AS
         pg_stat_get_buf_alloc() AS buffers_alloc,
         pg_stat_get_bgwriter_stat_reset_time() AS stats_reset;
 
+CREATE VIEW pg_stat_walwrites AS
+   SELECT * FROM pg_stat_get_walwrites() AS A;
+
 CREATE VIEW pg_stat_progress_vacuum AS
 	SELECT
 		S.pid AS pid, S.datid AS datid, D.datname AS datname,
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 5b43a66..0f3b007 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -344,6 +344,8 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 						 onerel->rd_rel->relisshared,
 						 new_live_tuples,
 						 vacrelstats->new_dead_tuples);
+	/* In case if there is any WAL write activity, send it. */
+	pgstat_send_walwrites();
 	pgstat_progress_end_command();
 
 	/* and log the action if appropriate */
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 33ca749..5a42c69 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -132,7 +132,6 @@ int			Log_autovacuum_min_duration = -1;
 
 /* Flags to tell if we are in an autovacuum process */
 static bool am_autovacuum_launcher = false;
-static bool am_autovacuum_worker = false;
 
 /* Flags set by signal handlers */
 static volatile sig_atomic_t got_SIGHUP = false;
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index dcb4cf2..1cd623d 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -284,6 +284,7 @@ BackgroundWriterMain(void)
 		 * Send off activity statistics to the stats collector
 		 */
 		pgstat_send_bgwriter();
+		pgstat_send_walwrites();
 
 		if (FirstCallSinceLastCheckpoint())
 		{
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index fe9041f..8549f8f 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -538,6 +538,7 @@ CheckpointerMain(void)
 		 * stats message types.)
 		 */
 		pgstat_send_bgwriter();
+		pgstat_send_walwrites();
 
 		/*
 		 * Sleep until we are signaled or it's time for another checkpoint or
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 56a8bf2..5400757 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -139,6 +139,15 @@ char	   *pgstat_stat_tmpname = NULL;
  */
 PgStat_MsgBgWriter BgWriterStats;
 
+/*
+ * WalWrites Local statistics counters.
+ * The statistics data gets populated in XLogWrite function.
+ * Stored directly in a stats message structure so it can be sent
+ * to stats collector process without needing to copy things around.
+ * We assume this inits to zeroes.
+ */
+PgStat_MsgWalWrites LocalWalWritesStats;
+
 /* ----------
  * Local data
  * ----------
@@ -253,6 +262,7 @@ static int	localNumBackends = 0;
  */
 static PgStat_ArchiverStats archiverStats;
 static PgStat_GlobalStats globalStats;
+static PgStat_WalWritesStats walwritesStats;
 
 /*
  * List of OIDs of databases we need to write out.  If an entry is InvalidOid,
@@ -333,6 +343,7 @@ static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
 static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
 static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
+static void pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len);
 
 /* ------------------------------------------------------------
  * Public functions called from postmaster follow
@@ -874,6 +885,9 @@ pgstat_report_stat(bool force)
 
 	/* Now, send function statistics */
 	pgstat_send_funcstats();
+
+	/* Now, send wal writes statistics */
+	pgstat_send_walwrites();
 }
 
 /*
@@ -1300,11 +1314,13 @@ pgstat_reset_shared_counters(const char *target)
 		msg.m_resettarget = RESET_ARCHIVER;
 	else if (strcmp(target, "bgwriter") == 0)
 		msg.m_resettarget = RESET_BGWRITER;
+	else if (strcmp(target, "walwrites") == 0)
+		msg.m_resettarget = RESET_WALWRITES;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("unrecognized reset target: \"%s\"", target),
-				 errhint("Target must be \"archiver\" or \"bgwriter\".")));
+				 errhint("Target must be \"archiver\" or \"bgwriter\" or \"walwrites\".")));
 
 	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER);
 	pgstat_send(&msg, sizeof(msg));
@@ -2543,6 +2559,21 @@ pgstat_fetch_global(void)
 	return &globalStats;
 }
 
+/*
+ * ---------
+ * pgstat_fetch_stat_walwrites() -
+ *
+ *	Support function for the SQL-callable pgstat* functions. Returns
+ *	a pointer to the walwrites statistics struct.
+ * ---------
+ */
+PgStat_WalWritesStats *
+pgstat_fetch_stat_walwrites(void)
+{
+	backend_read_statsfile();
+
+	return &walwritesStats;
+}
 
 /* ------------------------------------------------------------
  * Functions for management of the shared-memory PgBackendStatus array
@@ -4123,6 +4154,39 @@ pgstat_send_bgwriter(void)
 	MemSet(&BgWriterStats, 0, sizeof(BgWriterStats));
 }
 
+/* ----------
+ * pgstat_send_walwrites() -
+ *
+ *		Send wal writes statistics to the collector
+ * ----------
+ */
+
+void
+pgstat_send_walwrites(void)
+{
+	/* We assume this initializes to zeroes */
+	static const PgStat_MsgWalWrites all_zeroes;
+
+	/*
+	 * This function can be called even if nothing at all has happened. In
+	 * this case, avoid sending a completely empty message to the stats
+	 * collector.
+	 */
+	if (memcmp(&LocalWalWritesStats, &all_zeroes, sizeof(PgStat_MsgWalWrites)) == 0)
+		return;
+
+	/*
+	 * Prepare and send the message
+	 */
+	pgstat_setheader(&LocalWalWritesStats.m_hdr, PGSTAT_MTYPE_WALWRITES);
+	pgstat_send(&LocalWalWritesStats, sizeof(LocalWalWritesStats));
+
+	/*
+	 * Clear out the statistics buffer, so it can be re-used.
+	 */
+	MemSet(&LocalWalWritesStats, 0, sizeof(LocalWalWritesStats));
+}
+
 
 /* ----------
  * PgstatCollectorMain() -
@@ -4339,6 +4403,10 @@ PgstatCollectorMain(int argc, char *argv[])
 					pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len);
 					break;
 
+				case PGSTAT_MTYPE_WALWRITES:
+					pgstat_recv_walwrites((PgStat_MsgWalWrites *) & msg, len);
+					break;
+
 				default:
 					break;
 			}
@@ -4605,6 +4673,12 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 	(void) rc;					/* we'll check for error with ferror */
 
 	/*
+	 * Write wal writes stats struct
+	 */
+	rc = fwrite(&walwritesStats, sizeof(walwritesStats), 1, fpout);
+	(void) rc;					/* we'll check for error with ferror */
+
+	/*
 	 * Walk through the database table.
 	 */
 	hash_seq_init(&hstat, pgStatDBHash);
@@ -4861,6 +4935,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	memset(&globalStats, 0, sizeof(globalStats));
 	memset(&archiverStats, 0, sizeof(archiverStats));
+	memset(&walwritesStats, 0, sizeof(walwritesStats));
 
 	/*
 	 * Set the current timestamp (will be kept only in case we can't load an
@@ -4868,6 +4943,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	globalStats.stat_reset_timestamp = GetCurrentTimestamp();
 	archiverStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
+	walwritesStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
 
 	/*
 	 * Try to open the stats file. If it doesn't exist, the backends simply
@@ -4920,6 +4996,16 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	}
 
 	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&walwritesStats, 1, sizeof(walwritesStats), fpin) != sizeof(walwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		goto done;
+	}
+
+	/*
 	 * We found an existing collector stats file. Read it and put all the
 	 * hashtable entries into place.
 	 */
@@ -5208,6 +5294,7 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 	PgStat_StatDBEntry dbentry;
 	PgStat_GlobalStats myGlobalStats;
 	PgStat_ArchiverStats myArchiverStats;
+	PgStat_WalWritesStats myWalwritesStats;
 	FILE	   *fpin;
 	int32		format_id;
 	const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
@@ -5262,6 +5349,18 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 		return false;
 	}
 
+	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&myWalwritesStats, 1, sizeof(myWalwritesStats),
+			  fpin) != sizeof(myWalwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		FreeFile(fpin);
+		return false;
+	}
+
 	/* By default, we're going to return the timestamp of the global file. */
 	*ts = myGlobalStats.stats_timestamp;
 
@@ -5824,6 +5923,12 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
 		memset(&archiverStats, 0, sizeof(archiverStats));
 		archiverStats.stat_reset_timestamp = GetCurrentTimestamp();
 	}
+	else if (msg->m_resettarget == RESET_WALWRITES)
+	{
+		/* Reset the wal writes statistics of the cluster. */
+		memset(&walwritesStats, 0, sizeof(walwritesStats));
+		walwritesStats.stat_reset_timestamp = GetCurrentTimestamp();
+	}
 
 	/*
 	 * Presumably the sender of this message validated the target, don't
@@ -6004,6 +6109,27 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
 }
 
 /* ----------
+ * pgstat_recv_walwrites() -
+ *
+ *	Process a WALWRITES message.
+ * ----------
+ */
+static void
+pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len)
+{
+	walwritesStats.writes += msg->m_writes;
+	walwritesStats.backend_writes += msg->m_backend_writes;
+	walwritesStats.dirty_writes += msg->m_dirty_writes;
+	walwritesStats.backend_dirty_writes += msg->m_backend_dirty_writes;
+	walwritesStats.write_blocks += msg->m_write_blocks;
+	walwritesStats.backend_write_blocks += msg->m_backend_write_blocks;
+	walwritesStats.total_write_time += msg->m_total_write_time;
+	walwritesStats.backend_total_write_time += msg->m_backend_total_write_time;
+	walwritesStats.total_sync_time += msg->m_total_sync_time;
+	walwritesStats.backend_total_sync_time += msg->m_backend_total_sync_time;
+}
+
+/* ----------
  * pgstat_recv_recoveryconflict() -
  *
  *	Process a RECOVERYCONFLICT message.
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index a575d8f..cf0c279 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -280,6 +280,8 @@ WalWriterMain(void)
 		else if (left_till_hibernate > 0)
 			left_till_hibernate--;
 
+		pgstat_send_walwrites();
+
 		/*
 		 * Sleep until we are signaled or WalWriterDelay has elapsed.  If we
 		 * haven't done anything useful for quite some time, lengthen the
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index cfc3fba..677473e 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -2106,6 +2106,9 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		/* Send keepalive if the time has come */
 		WalSndKeepaliveIfNecessary(now);
 
+		/* in case if it has any WAL write activity, send now */
+		pgstat_send_walwrites();
+
 		/*
 		 * We don't block if not caught up, unless there is unsent data
 		 * pending in which case we'd better block until the socket is
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index dd2b924..f2d8d50 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1859,3 +1859,42 @@ pg_stat_get_archiver(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(HeapTupleGetDatum(
 								   heap_form_tuple(tupdesc, values, nulls)));
 }
+
+#define NUM_PG_STAT_WALWRITE_ATTS 11
+
+Datum
+pg_stat_get_walwrites(PG_FUNCTION_ARGS)
+{
+	TupleDesc	tupdesc;
+	Datum		values[NUM_PG_STAT_WALWRITE_ATTS];
+	bool		nulls[NUM_PG_STAT_WALWRITE_ATTS];
+	PgStat_WalWritesStats *walwrite_stats;
+
+	/* Initialize values and NULL flags arrays */
+	MemSet(values, 0, sizeof(values));
+	MemSet(nulls, 0, sizeof(nulls));
+
+	/* 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");
+
+	/* Get statistics about the archiver process */
+	walwrite_stats = pgstat_fetch_stat_walwrites();
+
+	/* Fill values and NULLs */
+	values[0] = Int64GetDatum(walwrite_stats->writes);
+	values[1] = Int64GetDatum(walwrite_stats->backend_writes);
+	values[2] = Int64GetDatum(walwrite_stats->dirty_writes);
+	values[3] = Int64GetDatum(walwrite_stats->backend_dirty_writes);
+	values[4] = Int64GetDatum(walwrite_stats->write_blocks);
+	values[5] = Int64GetDatum(walwrite_stats->backend_write_blocks);
+	values[6] = Float8GetDatum(walwrite_stats->total_write_time);
+	values[7] = Float8GetDatum(walwrite_stats->backend_total_write_time);
+	values[8] = Float8GetDatum(walwrite_stats->total_sync_time);
+	values[9] = Float8GetDatum(walwrite_stats->backend_total_sync_time);
+	values[10] = TimestampTzGetDatum(walwrite_stats->stat_reset_timestamp);
+
+	/* Returns the record as Datum */
+	PG_RETURN_DATUM(HeapTupleGetDatum(
+								   heap_form_tuple(tupdesc, values, nulls)));
+}
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 08b6030..4931559 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -125,6 +125,8 @@ int			max_worker_processes = 8;
 int			max_parallel_workers = 8;
 int			MaxBackends = 0;
 
+bool 		am_autovacuum_worker = false;
+
 int			VacuumCostPageHit = 1;		/* GUC parameters for vacuum */
 int			VacuumCostPageMiss = 10;
 int			VacuumCostPageDirty = 20;
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 79f9b90..2a00d5b 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2914,6 +2914,9 @@ DESCR("statistics: number of backend buffer writes that did their own fsync");
 DATA(insert OID = 2859 ( pg_stat_get_buf_alloc			PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_buf_alloc _null_ _null_ _null_ ));
 DESCR("statistics: number of buffer allocations");
 
+DATA(insert OID = 3373 (  pg_stat_get_walwrites		PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{20,20,20,20,20,20,701,701,701,701,1184}" "{o,o,o,o,o,o,o,o,o,o,o}" "{writes,backend_writes,dirty_writes,backend_dirty_writes,write_blocks,backend_write_blocks,write_time,backend_write_time,sync_time,backend_sync_time,stats_reset}" _null_ _null_ pg_stat_get_walwrites _null_ _null_ _null_ ));
+DESCR("statistics: information about WAL writes activity");
+
 DATA(insert OID = 2978 (  pg_stat_get_function_calls		PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_calls _null_ _null_ _null_ ));
 DESCR("statistics: number of function calls");
 DATA(insert OID = 2979 (  pg_stat_get_function_total_time	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 701 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_total_time _null_ _null_ _null_ ));
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 4c607b2..386ed06 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -242,6 +242,8 @@ extern PGDLLIMPORT int work_mem;
 extern PGDLLIMPORT int maintenance_work_mem;
 extern PGDLLIMPORT int replacement_sort_tuples;
 
+extern bool	am_autovacuum_worker;
+
 extern int	VacuumCostPageHit;
 extern int	VacuumCostPageMiss;
 extern int	VacuumCostPageDirty;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index e29397f..6c025ff 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -64,7 +64,8 @@ typedef enum StatMsgType
 	PGSTAT_MTYPE_FUNCPURGE,
 	PGSTAT_MTYPE_RECOVERYCONFLICT,
 	PGSTAT_MTYPE_TEMPFILE,
-	PGSTAT_MTYPE_DEADLOCK
+	PGSTAT_MTYPE_DEADLOCK,
+	PGSTAT_MTYPE_WALWRITES
 } StatMsgType;
 
 /* ----------
@@ -119,7 +120,8 @@ typedef struct PgStat_TableCounts
 typedef enum PgStat_Shared_Reset_Target
 {
 	RESET_ARCHIVER,
-	RESET_BGWRITER
+	RESET_BGWRITER,
+	RESET_WALWRITES
 } PgStat_Shared_Reset_Target;
 
 /* Possible object types for resetting single counters */
@@ -423,6 +425,38 @@ typedef struct PgStat_MsgBgWriter
 } PgStat_MsgBgWriter;
 
 /* ----------
+ * PgStat_MsgWalWrites			Sent by the backend and all background processes/workers
+ *								that does the WAL write operations.
+ * ----------
+ */
+typedef struct PgStat_MsgWalWrites
+{
+	PgStat_MsgHdr m_hdr;
+
+	PgStat_Counter m_writes;	/* No of writes by background
+								 * processes/workers */
+	PgStat_Counter m_backend_writes;	/* No of writes by backend */
+	PgStat_Counter m_dirty_writes;		/* No of dirty writes by background
+										 * processes/workers when WAL buffers
+										 * full */
+	PgStat_Counter m_backend_dirty_writes;		/* No of dirty writes by
+												 * backend when WAL buffers
+												 * full */
+	PgStat_Counter m_write_blocks;		/* Total no of pages written by
+										 * background processes/workers */
+	PgStat_Counter m_backend_write_blocks;		/* Total no of pages written
+												 * by backend */
+	PgStat_Counter m_total_write_time;	/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter m_backend_total_write_time;	/* Total write time in
+												 * milliseconds by backend */
+	PgStat_Counter m_total_sync_time;	/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter m_backend_total_sync_time;	/* Total write time in
+												 * milliseconds by backend */
+}	PgStat_MsgWalWrites;
+
+/* ----------
  * PgStat_MsgRecoveryConflict	Sent by the backend upon recovery conflict
  * ----------
  */
@@ -555,6 +589,7 @@ typedef union PgStat_Msg
 	PgStat_MsgFuncpurge msg_funcpurge;
 	PgStat_MsgRecoveryConflict msg_recoveryconflict;
 	PgStat_MsgDeadlock msg_deadlock;
+	PgStat_MsgWalWrites msg_walwrites;
 } PgStat_Msg;
 
 
@@ -694,6 +729,33 @@ typedef struct PgStat_GlobalStats
 	TimestampTz stat_reset_timestamp;
 } PgStat_GlobalStats;
 
+/*
+ * Walwrites statistics kept in the stats collector
+ */
+typedef struct PgStat_WalWritesStats
+{
+	PgStat_Counter writes;		/* No of writes by background
+								 * processes/workers */
+	PgStat_Counter backend_writes;		/* No of writes by backend */
+	PgStat_Counter dirty_writes;/* No of dirty writes by background
+								 * processes/workers when WAL buffers full */
+	PgStat_Counter backend_dirty_writes;		/* No of dirty writes by
+												 * backend when WAL buffers
+												 * full */
+	PgStat_Counter write_blocks;/* Total no of pages written by background
+								 * processes/workers */
+	PgStat_Counter backend_write_blocks;		/* Total no of pages written
+												 * by backend */
+	PgStat_Counter total_write_time;	/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter backend_total_write_time;	/* Total write time in
+												 * milliseconds by backend */
+	PgStat_Counter total_sync_time;		/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter backend_total_sync_time;		/* Total write time in
+												 * milliseconds by backend */
+	TimestampTz stat_reset_timestamp;	/* Last time when the stats reset */
+}	PgStat_WalWritesStats;
 
 /* ----------
  * Backend types
@@ -1115,6 +1177,11 @@ extern char *pgstat_stat_filename;
 extern PgStat_MsgBgWriter BgWriterStats;
 
 /*
+ * Wal writes statistics updated in XLogWrite function
+ */
+extern PgStat_MsgWalWrites LocalWalWritesStats;
+
+/*
  * Updated by pgstat_count_buffer_*_time macros
  */
 extern PgStat_Counter pgStatBlockReadTime;
@@ -1308,6 +1375,7 @@ extern void pgstat_twophase_postabort(TransactionId xid, uint16 info,
 
 extern void pgstat_send_archiver(const char *xlog, bool failed);
 extern void pgstat_send_bgwriter(void);
+extern void pgstat_send_walwrites(void);
 
 /* ----------
  * Support functions for the SQL-callable functions to
@@ -1322,5 +1390,7 @@ extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
 extern int	pgstat_fetch_stat_numbackends(void);
 extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
 extern PgStat_GlobalStats *pgstat_fetch_global(void);
+extern PgStat_WalWritesStats *pgstat_fetch_stat_walwrites(void);
+
 
 #endif   /* PGSTAT_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index d706f42..4c0a55b 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1973,6 +1973,18 @@ pg_stat_wal_receiver| SELECT s.pid,
     s.conninfo
    FROM pg_stat_get_wal_receiver() s(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, conninfo)
   WHERE (s.pid IS NOT NULL);
+pg_stat_walwrites| SELECT a.writes,
+    a.backend_writes,
+    a.dirty_writes,
+    a.backend_dirty_writes,
+    a.write_blocks,
+    a.backend_write_blocks,
+    a.write_time,
+    a.backend_write_time,
+    a.sync_time,
+    a.backend_sync_time,
+    a.stats_reset
+   FROM pg_stat_get_walwrites() a(writes, backend_writes, dirty_writes, backend_dirty_writes, write_blocks, backend_write_blocks, write_time, backend_write_time, sync_time, backend_sync_time, stats_reset);
 pg_stat_xact_all_tables| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 568b783..8448cac 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -67,6 +67,13 @@ select count(*) >= 0 as ok from pg_prepared_xacts;
  t
 (1 row)
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+ ok 
+----
+ t
+(1 row)
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 28e412b..3769880 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -32,6 +32,9 @@ select count(*) = 0 as ok from pg_prepared_statements;
 -- See also prepared_xacts.sql
 select count(*) >= 0 as ok from pg_prepared_xacts;
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
#13Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Haribabu Kommi (#12)
1 attachment(s)
Re: pg_stat_wal_write statistics view

On Tue, Mar 28, 2017 at 3:40 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

update patch attached.

Forgot to execute git commit, new patch attached.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_stat_walwrites_view_4.patchapplication/octet-stream; name=pg_stat_walwrites_view_4.patchDownload
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index ac339fb..ed833e0 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5732,7 +5732,8 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
         platforms.  You can use the <xref linkend="pgtesttiming"> tool to
         measure the overhead of timing on your system.
         I/O timing information is
-        displayed in <xref linkend="pg-stat-database-view">, in the output of
+        displayed in <xref linkend="pg-stat-database-view">, 
+        <xref linkend="pg-stat-walwrites-view">, in the output of
         <xref linkend="sql-explain"> when the <literal>BUFFERS</> option is
         used, and by <xref linkend="pgstatstatements">.  Only superusers can
         change this setting.
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 9856968..b5ed688 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -357,6 +357,14 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
      </row>
 
      <row>
+      <entry><structname>pg_stat_walwrites</><indexterm><primary>pg_stat_walwrites</primary></indexterm></entry>
+      <entry>One row only, showing statistics about the
+       WAL writing activity. See
+       <xref linkend="pg-stat-walwrites-view"> for details.
+     </entry>
+     </row>
+
+     <row>
       <entry><structname>pg_stat_database</><indexterm><primary>pg_stat_database</primary></indexterm></entry>
       <entry>One row per database, showing database-wide statistics. See
        <xref linkend="pg-stat-database-view"> for details.
@@ -2207,6 +2215,108 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
    single row, containing global data for the cluster.
   </para>
 
+  <table id="pg-stat-walwrites-view" xreflabel="pg_stat_walwrites">
+   <title><structname>pg_stat_walwrites</structname> View</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of WAL writes that are carried out by background processes and workers. 
+      </entry>
+     </row>
+    <row>
+      <entry><structfield>backend_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL writes that are carried out by the backend process</entry>
+     </row>
+     <row>
+     <entry><structfield>dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by background processes and workers
+      when the <xref linkend="guc-wal-buffers"> are full.
+      </entry>
+     </row>
+    <row>
+      <entry><structfield>backend_dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by the backend processes when 
+      the <xref linkend="guc-wal-buffers"> are full. 
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the background processes/workers</entry>
+     </row>
+     <row>
+      <entry><structfield>backend_write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the backend processes</entry>
+     </row>
+     <row>
+      <entry><structfield>write_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are written to disk by the background processes/workers, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>backend_write_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are written to disk by the backend processes, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>sync_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are synchronized to disk by the background processes/workers, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>backend_sync_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are synchronized to disk by the backend processes, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>stats_reset</></entry>
+      <entry><type>timestamp with time zone</type></entry>
+      <entry>Time at which these statistics were last reset</entry>
+     </row>
+     </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_walwrites</structname> view will always have a
+   single row, containing data about the WAL writing activity of the cluster.
+  </para>
+
+
   <table id="pg-stat-database-view" xreflabel="pg_stat_database">
    <title><structname>pg_stat_database</structname> View</title>
    <tgroup cols="3">
@@ -2986,6 +3096,8 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        counters shown in the <structname>pg_stat_bgwriter</> view.
        Calling <literal>pg_stat_reset_shared('archiver')</> will zero all the
        counters shown in the <structname>pg_stat_archiver</> view.
+       Calling <literal>pg_stat_reset_shared('walwrites')</> will zero all the
+       counters shown in the <structname>pg_stat_walwrites</> view.
       </entry>
      </row>
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 61ca81d..1f474ae 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -860,6 +860,7 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void);
 
 static void AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic);
 static bool XLogCheckpointNeeded(XLogSegNo new_segno);
+static bool am_background_process(void);
 static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible);
 static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath,
 					   bool find_free, XLogSegNo max_segno,
@@ -2109,6 +2110,10 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic)
 					WriteRqst.Write = OldPageRqstPtr;
 					WriteRqst.Flush = 0;
 					XLogWrite(WriteRqst, false);
+					if (am_background_process())
+						LocalWalWritesStats.m_dirty_writes++;
+					else
+						LocalWalWritesStats.m_backend_dirty_writes++;
 					LWLockRelease(WALWriteLock);
 					TRACE_POSTGRESQL_WAL_BUFFER_WRITE_DIRTY_DONE();
 				}
@@ -2319,6 +2324,34 @@ XLogCheckpointNeeded(XLogSegNo new_segno)
 }
 
 /*
+ * Check whether the current process is a normal backend or not.
+ * This function checks for the background processes that does
+ * some WAL write activity only and other background processes
+ * are not considered. It considers all the background workers
+ * as WAL write activity workers.
+ *
+ * Returns FALSE - when the current process is a normal backend
+ *		   TRUE - when the current process a background process/worker
+ */
+static bool
+am_background_process()
+{
+	/* check whether current process is a background process/worker? */
+	if (AmBackgroundWriterProcess() ||
+		AmWalWriterProcess() ||
+		AmCheckpointerProcess() ||
+		AmStartupProcess() ||
+		IsBackgroundWorker ||
+		am_walsender ||
+		am_autovacuum_worker)
+	{
+		return true;
+	}
+
+	return false;
+}
+
+/*
  * Write and/or fsync the log at least as far as WriteRqst indicates.
  *
  * If flexible == TRUE, we don't have to write as far as WriteRqst, but
@@ -2341,6 +2374,9 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 	int			npages;
 	int			startidx;
 	uint32		startoffset;
+	instr_time	io_start,
+				io_time;
+	bool		is_background_process = am_background_process();
 
 	/* We should always be inside a critical section here */
 	Assert(CritSectionCount > 0);
@@ -2458,6 +2494,11 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			/* OK to write the page(s) */
 			from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ;
 			nbytes = npages * (Size) XLOG_BLCKSZ;
+
+			/* Start timer to acquire start time of the wal write */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			nleft = nbytes;
 			do
 			{
@@ -2480,6 +2521,34 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				from += written;
 			} while (nleft > 0);
 
+			/* calculate the total time spent for wal writing */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				if (is_background_process)
+					LocalWalWritesStats.m_total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+				else
+					LocalWalWritesStats.m_backend_total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				LocalWalWritesStats.m_total_write_time = 0;
+				LocalWalWritesStats.m_backend_total_write_time = 0;
+			}
+
+			/* check whether writer is a normal backend or not? */
+			if (is_background_process)
+				LocalWalWritesStats.m_writes++;
+			else
+				LocalWalWritesStats.m_backend_writes++;
+
+			if (is_background_process)
+				LocalWalWritesStats.m_write_blocks += npages;
+			else
+				LocalWalWritesStats.m_backend_write_blocks += npages;
+
 			/* Update state for write */
 			openLogOff += nbytes;
 			npages = 0;
@@ -2499,8 +2568,29 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			 */
 			if (finishing_seg)
 			{
+				/* Start timer to acquire start time of the wal sync */
+				if (track_io_timing)
+					INSTR_TIME_SET_CURRENT(io_start);
+
 				issue_xlog_fsync(openLogFile, openLogSegNo);
 
+				/* calculate the total time spent for wal sync */
+				if (track_io_timing)
+				{
+					INSTR_TIME_SET_CURRENT(io_time);
+					INSTR_TIME_SUBTRACT(io_time, io_start);
+
+					if (is_background_process)
+						LocalWalWritesStats.m_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+					else
+						LocalWalWritesStats.m_backend_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+				}
+				else
+				{
+					LocalWalWritesStats.m_total_sync_time = 0;
+					LocalWalWritesStats.m_backend_total_sync_time = 0;
+				}
+
 				/* signal that we need to wakeup walsenders later */
 				WalSndWakeupRequest();
 
@@ -2568,7 +2658,28 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				openLogOff = 0;
 			}
 
+			/* Start timer to acquire start time of the wal sync */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			issue_xlog_fsync(openLogFile, openLogSegNo);
+
+			/* calculate the total time spent for wal sync */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				if (is_background_process)
+					LocalWalWritesStats.m_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+				else
+					LocalWalWritesStats.m_backend_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				LocalWalWritesStats.m_total_sync_time = 0;
+				LocalWalWritesStats.m_backend_total_sync_time = 0;
+			}
 		}
 
 		/* signal that we need to wakeup walsenders later */
@@ -7642,6 +7753,9 @@ StartupXLOG(void)
 		}
 	}
 
+	/* Report WAL write activity now */
+	pgstat_send_walwrites();
+
 	/*
 	 * Preallocate additional log files, if wanted.
 	 */
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index d357c8b..f424c9d 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -891,6 +891,9 @@ CREATE VIEW pg_stat_bgwriter AS
         pg_stat_get_buf_alloc() AS buffers_alloc,
         pg_stat_get_bgwriter_stat_reset_time() AS stats_reset;
 
+CREATE VIEW pg_stat_walwrites AS
+   SELECT * FROM pg_stat_get_walwrites() AS A;
+
 CREATE VIEW pg_stat_progress_vacuum AS
 	SELECT
 		S.pid AS pid, S.datid AS datid, D.datname AS datname,
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 5b43a66..0f3b007 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -344,6 +344,8 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 						 onerel->rd_rel->relisshared,
 						 new_live_tuples,
 						 vacrelstats->new_dead_tuples);
+	/* In case if there is any WAL write activity, send it. */
+	pgstat_send_walwrites();
 	pgstat_progress_end_command();
 
 	/* and log the action if appropriate */
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 33ca749..5a42c69 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -132,7 +132,6 @@ int			Log_autovacuum_min_duration = -1;
 
 /* Flags to tell if we are in an autovacuum process */
 static bool am_autovacuum_launcher = false;
-static bool am_autovacuum_worker = false;
 
 /* Flags set by signal handlers */
 static volatile sig_atomic_t got_SIGHUP = false;
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index dcb4cf2..1cd623d 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -284,6 +284,7 @@ BackgroundWriterMain(void)
 		 * Send off activity statistics to the stats collector
 		 */
 		pgstat_send_bgwriter();
+		pgstat_send_walwrites();
 
 		if (FirstCallSinceLastCheckpoint())
 		{
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index fe9041f..8549f8f 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -538,6 +538,7 @@ CheckpointerMain(void)
 		 * stats message types.)
 		 */
 		pgstat_send_bgwriter();
+		pgstat_send_walwrites();
 
 		/*
 		 * Sleep until we are signaled or it's time for another checkpoint or
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 56a8bf2..5400757 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -139,6 +139,15 @@ char	   *pgstat_stat_tmpname = NULL;
  */
 PgStat_MsgBgWriter BgWriterStats;
 
+/*
+ * WalWrites Local statistics counters.
+ * The statistics data gets populated in XLogWrite function.
+ * Stored directly in a stats message structure so it can be sent
+ * to stats collector process without needing to copy things around.
+ * We assume this inits to zeroes.
+ */
+PgStat_MsgWalWrites LocalWalWritesStats;
+
 /* ----------
  * Local data
  * ----------
@@ -253,6 +262,7 @@ static int	localNumBackends = 0;
  */
 static PgStat_ArchiverStats archiverStats;
 static PgStat_GlobalStats globalStats;
+static PgStat_WalWritesStats walwritesStats;
 
 /*
  * List of OIDs of databases we need to write out.  If an entry is InvalidOid,
@@ -333,6 +343,7 @@ static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
 static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
 static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
+static void pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len);
 
 /* ------------------------------------------------------------
  * Public functions called from postmaster follow
@@ -874,6 +885,9 @@ pgstat_report_stat(bool force)
 
 	/* Now, send function statistics */
 	pgstat_send_funcstats();
+
+	/* Now, send wal writes statistics */
+	pgstat_send_walwrites();
 }
 
 /*
@@ -1300,11 +1314,13 @@ pgstat_reset_shared_counters(const char *target)
 		msg.m_resettarget = RESET_ARCHIVER;
 	else if (strcmp(target, "bgwriter") == 0)
 		msg.m_resettarget = RESET_BGWRITER;
+	else if (strcmp(target, "walwrites") == 0)
+		msg.m_resettarget = RESET_WALWRITES;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("unrecognized reset target: \"%s\"", target),
-				 errhint("Target must be \"archiver\" or \"bgwriter\".")));
+				 errhint("Target must be \"archiver\" or \"bgwriter\" or \"walwrites\".")));
 
 	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER);
 	pgstat_send(&msg, sizeof(msg));
@@ -2543,6 +2559,21 @@ pgstat_fetch_global(void)
 	return &globalStats;
 }
 
+/*
+ * ---------
+ * pgstat_fetch_stat_walwrites() -
+ *
+ *	Support function for the SQL-callable pgstat* functions. Returns
+ *	a pointer to the walwrites statistics struct.
+ * ---------
+ */
+PgStat_WalWritesStats *
+pgstat_fetch_stat_walwrites(void)
+{
+	backend_read_statsfile();
+
+	return &walwritesStats;
+}
 
 /* ------------------------------------------------------------
  * Functions for management of the shared-memory PgBackendStatus array
@@ -4123,6 +4154,39 @@ pgstat_send_bgwriter(void)
 	MemSet(&BgWriterStats, 0, sizeof(BgWriterStats));
 }
 
+/* ----------
+ * pgstat_send_walwrites() -
+ *
+ *		Send wal writes statistics to the collector
+ * ----------
+ */
+
+void
+pgstat_send_walwrites(void)
+{
+	/* We assume this initializes to zeroes */
+	static const PgStat_MsgWalWrites all_zeroes;
+
+	/*
+	 * This function can be called even if nothing at all has happened. In
+	 * this case, avoid sending a completely empty message to the stats
+	 * collector.
+	 */
+	if (memcmp(&LocalWalWritesStats, &all_zeroes, sizeof(PgStat_MsgWalWrites)) == 0)
+		return;
+
+	/*
+	 * Prepare and send the message
+	 */
+	pgstat_setheader(&LocalWalWritesStats.m_hdr, PGSTAT_MTYPE_WALWRITES);
+	pgstat_send(&LocalWalWritesStats, sizeof(LocalWalWritesStats));
+
+	/*
+	 * Clear out the statistics buffer, so it can be re-used.
+	 */
+	MemSet(&LocalWalWritesStats, 0, sizeof(LocalWalWritesStats));
+}
+
 
 /* ----------
  * PgstatCollectorMain() -
@@ -4339,6 +4403,10 @@ PgstatCollectorMain(int argc, char *argv[])
 					pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len);
 					break;
 
+				case PGSTAT_MTYPE_WALWRITES:
+					pgstat_recv_walwrites((PgStat_MsgWalWrites *) & msg, len);
+					break;
+
 				default:
 					break;
 			}
@@ -4605,6 +4673,12 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 	(void) rc;					/* we'll check for error with ferror */
 
 	/*
+	 * Write wal writes stats struct
+	 */
+	rc = fwrite(&walwritesStats, sizeof(walwritesStats), 1, fpout);
+	(void) rc;					/* we'll check for error with ferror */
+
+	/*
 	 * Walk through the database table.
 	 */
 	hash_seq_init(&hstat, pgStatDBHash);
@@ -4861,6 +4935,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	memset(&globalStats, 0, sizeof(globalStats));
 	memset(&archiverStats, 0, sizeof(archiverStats));
+	memset(&walwritesStats, 0, sizeof(walwritesStats));
 
 	/*
 	 * Set the current timestamp (will be kept only in case we can't load an
@@ -4868,6 +4943,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	globalStats.stat_reset_timestamp = GetCurrentTimestamp();
 	archiverStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
+	walwritesStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
 
 	/*
 	 * Try to open the stats file. If it doesn't exist, the backends simply
@@ -4920,6 +4996,16 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	}
 
 	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&walwritesStats, 1, sizeof(walwritesStats), fpin) != sizeof(walwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		goto done;
+	}
+
+	/*
 	 * We found an existing collector stats file. Read it and put all the
 	 * hashtable entries into place.
 	 */
@@ -5208,6 +5294,7 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 	PgStat_StatDBEntry dbentry;
 	PgStat_GlobalStats myGlobalStats;
 	PgStat_ArchiverStats myArchiverStats;
+	PgStat_WalWritesStats myWalwritesStats;
 	FILE	   *fpin;
 	int32		format_id;
 	const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
@@ -5262,6 +5349,18 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 		return false;
 	}
 
+	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&myWalwritesStats, 1, sizeof(myWalwritesStats),
+			  fpin) != sizeof(myWalwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		FreeFile(fpin);
+		return false;
+	}
+
 	/* By default, we're going to return the timestamp of the global file. */
 	*ts = myGlobalStats.stats_timestamp;
 
@@ -5824,6 +5923,12 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
 		memset(&archiverStats, 0, sizeof(archiverStats));
 		archiverStats.stat_reset_timestamp = GetCurrentTimestamp();
 	}
+	else if (msg->m_resettarget == RESET_WALWRITES)
+	{
+		/* Reset the wal writes statistics of the cluster. */
+		memset(&walwritesStats, 0, sizeof(walwritesStats));
+		walwritesStats.stat_reset_timestamp = GetCurrentTimestamp();
+	}
 
 	/*
 	 * Presumably the sender of this message validated the target, don't
@@ -6004,6 +6109,27 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
 }
 
 /* ----------
+ * pgstat_recv_walwrites() -
+ *
+ *	Process a WALWRITES message.
+ * ----------
+ */
+static void
+pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len)
+{
+	walwritesStats.writes += msg->m_writes;
+	walwritesStats.backend_writes += msg->m_backend_writes;
+	walwritesStats.dirty_writes += msg->m_dirty_writes;
+	walwritesStats.backend_dirty_writes += msg->m_backend_dirty_writes;
+	walwritesStats.write_blocks += msg->m_write_blocks;
+	walwritesStats.backend_write_blocks += msg->m_backend_write_blocks;
+	walwritesStats.total_write_time += msg->m_total_write_time;
+	walwritesStats.backend_total_write_time += msg->m_backend_total_write_time;
+	walwritesStats.total_sync_time += msg->m_total_sync_time;
+	walwritesStats.backend_total_sync_time += msg->m_backend_total_sync_time;
+}
+
+/* ----------
  * pgstat_recv_recoveryconflict() -
  *
  *	Process a RECOVERYCONFLICT message.
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index a575d8f..cf0c279 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -280,6 +280,8 @@ WalWriterMain(void)
 		else if (left_till_hibernate > 0)
 			left_till_hibernate--;
 
+		pgstat_send_walwrites();
+
 		/*
 		 * Sleep until we are signaled or WalWriterDelay has elapsed.  If we
 		 * haven't done anything useful for quite some time, lengthen the
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index cfc3fba..677473e 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -2106,6 +2106,9 @@ WalSndLoop(WalSndSendDataCallback send_data)
 		/* Send keepalive if the time has come */
 		WalSndKeepaliveIfNecessary(now);
 
+		/* in case if it has any WAL write activity, send now */
+		pgstat_send_walwrites();
+
 		/*
 		 * We don't block if not caught up, unless there is unsent data
 		 * pending in which case we'd better block until the socket is
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index dd2b924..f2d8d50 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1859,3 +1859,42 @@ pg_stat_get_archiver(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(HeapTupleGetDatum(
 								   heap_form_tuple(tupdesc, values, nulls)));
 }
+
+#define NUM_PG_STAT_WALWRITE_ATTS 11
+
+Datum
+pg_stat_get_walwrites(PG_FUNCTION_ARGS)
+{
+	TupleDesc	tupdesc;
+	Datum		values[NUM_PG_STAT_WALWRITE_ATTS];
+	bool		nulls[NUM_PG_STAT_WALWRITE_ATTS];
+	PgStat_WalWritesStats *walwrite_stats;
+
+	/* Initialize values and NULL flags arrays */
+	MemSet(values, 0, sizeof(values));
+	MemSet(nulls, 0, sizeof(nulls));
+
+	/* 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");
+
+	/* Get statistics about the archiver process */
+	walwrite_stats = pgstat_fetch_stat_walwrites();
+
+	/* Fill values and NULLs */
+	values[0] = Int64GetDatum(walwrite_stats->writes);
+	values[1] = Int64GetDatum(walwrite_stats->backend_writes);
+	values[2] = Int64GetDatum(walwrite_stats->dirty_writes);
+	values[3] = Int64GetDatum(walwrite_stats->backend_dirty_writes);
+	values[4] = Int64GetDatum(walwrite_stats->write_blocks);
+	values[5] = Int64GetDatum(walwrite_stats->backend_write_blocks);
+	values[6] = Float8GetDatum(walwrite_stats->total_write_time);
+	values[7] = Float8GetDatum(walwrite_stats->backend_total_write_time);
+	values[8] = Float8GetDatum(walwrite_stats->total_sync_time);
+	values[9] = Float8GetDatum(walwrite_stats->backend_total_sync_time);
+	values[10] = TimestampTzGetDatum(walwrite_stats->stat_reset_timestamp);
+
+	/* Returns the record as Datum */
+	PG_RETURN_DATUM(HeapTupleGetDatum(
+								   heap_form_tuple(tupdesc, values, nulls)));
+}
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 08b6030..4931559 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -125,6 +125,8 @@ int			max_worker_processes = 8;
 int			max_parallel_workers = 8;
 int			MaxBackends = 0;
 
+bool 		am_autovacuum_worker = false;
+
 int			VacuumCostPageHit = 1;		/* GUC parameters for vacuum */
 int			VacuumCostPageMiss = 10;
 int			VacuumCostPageDirty = 20;
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 79f9b90..2a00d5b 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2914,6 +2914,9 @@ DESCR("statistics: number of backend buffer writes that did their own fsync");
 DATA(insert OID = 2859 ( pg_stat_get_buf_alloc			PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_buf_alloc _null_ _null_ _null_ ));
 DESCR("statistics: number of buffer allocations");
 
+DATA(insert OID = 3373 (  pg_stat_get_walwrites		PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{20,20,20,20,20,20,701,701,701,701,1184}" "{o,o,o,o,o,o,o,o,o,o,o}" "{writes,backend_writes,dirty_writes,backend_dirty_writes,write_blocks,backend_write_blocks,write_time,backend_write_time,sync_time,backend_sync_time,stats_reset}" _null_ _null_ pg_stat_get_walwrites _null_ _null_ _null_ ));
+DESCR("statistics: information about WAL writes activity");
+
 DATA(insert OID = 2978 (  pg_stat_get_function_calls		PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_calls _null_ _null_ _null_ ));
 DESCR("statistics: number of function calls");
 DATA(insert OID = 2979 (  pg_stat_get_function_total_time	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 701 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_total_time _null_ _null_ _null_ ));
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 4c607b2..386ed06 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -242,6 +242,8 @@ extern PGDLLIMPORT int work_mem;
 extern PGDLLIMPORT int maintenance_work_mem;
 extern PGDLLIMPORT int replacement_sort_tuples;
 
+extern bool	am_autovacuum_worker;
+
 extern int	VacuumCostPageHit;
 extern int	VacuumCostPageMiss;
 extern int	VacuumCostPageDirty;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index e29397f..6c025ff 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -64,7 +64,8 @@ typedef enum StatMsgType
 	PGSTAT_MTYPE_FUNCPURGE,
 	PGSTAT_MTYPE_RECOVERYCONFLICT,
 	PGSTAT_MTYPE_TEMPFILE,
-	PGSTAT_MTYPE_DEADLOCK
+	PGSTAT_MTYPE_DEADLOCK,
+	PGSTAT_MTYPE_WALWRITES
 } StatMsgType;
 
 /* ----------
@@ -119,7 +120,8 @@ typedef struct PgStat_TableCounts
 typedef enum PgStat_Shared_Reset_Target
 {
 	RESET_ARCHIVER,
-	RESET_BGWRITER
+	RESET_BGWRITER,
+	RESET_WALWRITES
 } PgStat_Shared_Reset_Target;
 
 /* Possible object types for resetting single counters */
@@ -423,6 +425,38 @@ typedef struct PgStat_MsgBgWriter
 } PgStat_MsgBgWriter;
 
 /* ----------
+ * PgStat_MsgWalWrites			Sent by the backend and all background processes/workers
+ *								that does the WAL write operations.
+ * ----------
+ */
+typedef struct PgStat_MsgWalWrites
+{
+	PgStat_MsgHdr m_hdr;
+
+	PgStat_Counter m_writes;	/* No of writes by background
+								 * processes/workers */
+	PgStat_Counter m_backend_writes;	/* No of writes by backend */
+	PgStat_Counter m_dirty_writes;		/* No of dirty writes by background
+										 * processes/workers when WAL buffers
+										 * full */
+	PgStat_Counter m_backend_dirty_writes;		/* No of dirty writes by
+												 * backend when WAL buffers
+												 * full */
+	PgStat_Counter m_write_blocks;		/* Total no of pages written by
+										 * background processes/workers */
+	PgStat_Counter m_backend_write_blocks;		/* Total no of pages written
+												 * by backend */
+	PgStat_Counter m_total_write_time;	/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter m_backend_total_write_time;	/* Total write time in
+												 * milliseconds by backend */
+	PgStat_Counter m_total_sync_time;	/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter m_backend_total_sync_time;	/* Total write time in
+												 * milliseconds by backend */
+}	PgStat_MsgWalWrites;
+
+/* ----------
  * PgStat_MsgRecoveryConflict	Sent by the backend upon recovery conflict
  * ----------
  */
@@ -555,6 +589,7 @@ typedef union PgStat_Msg
 	PgStat_MsgFuncpurge msg_funcpurge;
 	PgStat_MsgRecoveryConflict msg_recoveryconflict;
 	PgStat_MsgDeadlock msg_deadlock;
+	PgStat_MsgWalWrites msg_walwrites;
 } PgStat_Msg;
 
 
@@ -694,6 +729,33 @@ typedef struct PgStat_GlobalStats
 	TimestampTz stat_reset_timestamp;
 } PgStat_GlobalStats;
 
+/*
+ * Walwrites statistics kept in the stats collector
+ */
+typedef struct PgStat_WalWritesStats
+{
+	PgStat_Counter writes;		/* No of writes by background
+								 * processes/workers */
+	PgStat_Counter backend_writes;		/* No of writes by backend */
+	PgStat_Counter dirty_writes;/* No of dirty writes by background
+								 * processes/workers when WAL buffers full */
+	PgStat_Counter backend_dirty_writes;		/* No of dirty writes by
+												 * backend when WAL buffers
+												 * full */
+	PgStat_Counter write_blocks;/* Total no of pages written by background
+								 * processes/workers */
+	PgStat_Counter backend_write_blocks;		/* Total no of pages written
+												 * by backend */
+	PgStat_Counter total_write_time;	/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter backend_total_write_time;	/* Total write time in
+												 * milliseconds by backend */
+	PgStat_Counter total_sync_time;		/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter backend_total_sync_time;		/* Total write time in
+												 * milliseconds by backend */
+	TimestampTz stat_reset_timestamp;	/* Last time when the stats reset */
+}	PgStat_WalWritesStats;
 
 /* ----------
  * Backend types
@@ -1115,6 +1177,11 @@ extern char *pgstat_stat_filename;
 extern PgStat_MsgBgWriter BgWriterStats;
 
 /*
+ * Wal writes statistics updated in XLogWrite function
+ */
+extern PgStat_MsgWalWrites LocalWalWritesStats;
+
+/*
  * Updated by pgstat_count_buffer_*_time macros
  */
 extern PgStat_Counter pgStatBlockReadTime;
@@ -1308,6 +1375,7 @@ extern void pgstat_twophase_postabort(TransactionId xid, uint16 info,
 
 extern void pgstat_send_archiver(const char *xlog, bool failed);
 extern void pgstat_send_bgwriter(void);
+extern void pgstat_send_walwrites(void);
 
 /* ----------
  * Support functions for the SQL-callable functions to
@@ -1322,5 +1390,7 @@ extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
 extern int	pgstat_fetch_stat_numbackends(void);
 extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
 extern PgStat_GlobalStats *pgstat_fetch_global(void);
+extern PgStat_WalWritesStats *pgstat_fetch_stat_walwrites(void);
+
 
 #endif   /* PGSTAT_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index d706f42..4c0a55b 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1973,6 +1973,18 @@ pg_stat_wal_receiver| SELECT s.pid,
     s.conninfo
    FROM pg_stat_get_wal_receiver() s(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, conninfo)
   WHERE (s.pid IS NOT NULL);
+pg_stat_walwrites| SELECT a.writes,
+    a.backend_writes,
+    a.dirty_writes,
+    a.backend_dirty_writes,
+    a.write_blocks,
+    a.backend_write_blocks,
+    a.write_time,
+    a.backend_write_time,
+    a.sync_time,
+    a.backend_sync_time,
+    a.stats_reset
+   FROM pg_stat_get_walwrites() a(writes, backend_writes, dirty_writes, backend_dirty_writes, write_blocks, backend_write_blocks, write_time, backend_write_time, sync_time, backend_sync_time, stats_reset);
 pg_stat_xact_all_tables| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 568b783..8448cac 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -67,6 +67,13 @@ select count(*) >= 0 as ok from pg_prepared_xacts;
  t
 (1 row)
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+ ok 
+----
+ t
+(1 row)
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 28e412b..3769880 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -32,6 +32,9 @@ select count(*) = 0 as ok from pg_prepared_statements;
 -- See also prepared_xacts.sql
 select count(*) >= 0 as ok from pg_prepared_xacts;
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
#14Fujii Masao
masao.fujii@gmail.com
In reply to: Haribabu Kommi (#12)
Re: pg_stat_wal_write statistics view

On Tue, Mar 28, 2017 at 1:40 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

On Mon, Mar 27, 2017 at 1:27 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Sat, Mar 25, 2017 at 6:40 AM, Fujii Masao <masao.fujii@gmail.com>
wrote:

On Wed, Feb 15, 2017 at 12:53 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

On Wed, Feb 8, 2017 at 9:36 PM, Amit Kapila <amit.kapila16@gmail.com>
wrote:

On Tue, Feb 7, 2017 at 11:47 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

Hi Hackers,

I just want to discuss adding of a new statistics view that provides
the information of wal writing details as follows

+1. I think it will be useful to observe WAL activity.

Thanks for your opinion.

postgres=# \d pg_stat_wal_writer
View "pg_catalog.pg_stat_wal_writer"
Column | Type | Collation |
Nullable
|
Default

-----------------------+--------------------------+-----------+----------+---------
num_backend_writes | bigint | |
|
num_total_writes | bigint | |
|
num_blocks | bigint | | |
total_write_time | bigint| | |
stats_reset | timestamp with time zone | |
|

The columns of the view are
1. Total number of xlog writes that are called from the backend.
2. Total number of xlog writes that are called from both backend
and background workers. (This column can be changed to just
display on the background writes).
3. The number of the blocks that are written.
4. Total write_time of the IO operation it took, this variable data
is
filled only when the track_io_timing GUC is enabled.

So, here is *write_time* the total time system has spent in WAL
writing before the last reset?

total write_time spent in WAL writing "after" the last reset in
milliseconds.

I think there should be a separate column for write and sync time.

Added.

Or it is possible to integrate the new columns into the existing
pg_stat_bgwriter view also.

I feel separate view is better.

Ok.

Following the sample out of the view after regress run.

postgres=# select * from pg_stat_walwrites;
-[ RECORD 1 ]--+------------------------------
backend_writes | 19092
writes | 663
write_blocks | 56116
write_time | 0
sync_time | 3064
stats_reset | 2017-02-15 13:37:09.454314+11

Currently, writer, walwriter and checkpointer processes
are considered as background processes that can do
the wal write mainly.

Thanks for the review.

I'm not sure if this categorization is good. You told that this view is
useful
to tune walwriter parameters at the top of this thread. If so, ISTM that
the information about walwriter's activity should be separated from
others.

Yes, that's correct. First I thought of providing the statistics of
walwriter, but
later in development, it turned into showing statistics of all wal write
activity
of background processes also to differentiate the actual write by the
backends.

What about other processes which *can* write WAL, for example walsender
(e.g., BASE_BACKUP can cause WAL record), startup process (e.g., end-of-
recovery checkpoint) and logical replication worker (Not sure if it
always
works with synchronous_commit=off, though). There might be other
processes
which can write WAL

It is possible to add the walsender, stratup and other processes easily,
but not
background workers that does some wal write operations until unless they
report the stats with pgstat_report_stat(). Is it fine to ignore the
workers that
does not report the stats?

Added stats collection for walsender, statrup and autovacuum processes.
The background workers that call pgstat_report_stat() function will
automatically
included.

So you added pgstat_send_walwrites() into several places. But WAL activity
by the background workers which don't call pgstat_report_stat() is still not
considered in pg_stat_walwrites view. This design looks fragile to me.
I think that we should capture all the WAL activities (even by such background
workers) in XLogWrite(), instead.

For example, we can add the "shared" counters (protected by WALWriteLock)
into XLogCtl and make XLogWrite() update those counters if the process is
NOT walwriter. OTOH, if it's walwriter, XLogWrite() updates its local counters.
Then walwriter periodically packs those counters in the message and sends
it to the stats collector.

Why didn't you separate "write_blocks", "write_time" and "sync_time" per
the process category, like "backend_writes" and "writes"?

Ok. I will add those columns.

Added the new columns to differentiate backend and background processes.

This view doesn't seem to take into consideration the WAL writing and
flushing
during creating new WAL segment file.

I think that it's better to make this view report also the number of WAL
pages
which are written when wal_buffer is full. This information is useful to
tune the size of wal_buffers. This was proposed by Nagayasu before.
/messages/by-id/4FF824F3.5090407@uptime.jp

Ok. But this new column just shows how many times the WAL buffers are
flushed
because of wal buffers are full. Not the WAL pages that are actually
flushed because
of wal buffers full as a separate column.

Added a new column to display the dirty writes from both backend and
background processes.

update patch attached.

Thanks for updating the patch!

+      <entry><structfield>writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of WAL writes that are carried out by background
processes and workers.
+      </entry>
<snip>
+      <entry><structfield>write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the
background processes/workers</entry>
+     </row>

Could you tell me why both numbers of WAL writes and pages written need to
be reported? How can we tune the system by using those information?

Instead of (or in addition to) the number of WAL pages, we should report
the number of *bytes* of WAL records actually written?

Anyway, we need more and more opinions about what information of WAL activity
should be exposed in the view and how. But we have only a few days left for
this CommitFest...

Regards,

--
Fujii Masao

--
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: Fujii Masao (#14)
1 attachment(s)
Re: pg_stat_wal_write statistics view

On Wed, Mar 29, 2017 at 5:10 AM, Fujii Masao <masao.fujii@gmail.com> wrote:

On Tue, Mar 28, 2017 at 1:40 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

Added stats collection for walsender, statrup and autovacuum processes.
The background workers that call pgstat_report_stat() function will
automatically
included.

So you added pgstat_send_walwrites() into several places. But WAL activity
by the background workers which don't call pgstat_report_stat() is still
not
considered in pg_stat_walwrites view. This design looks fragile to me.
I think that we should capture all the WAL activities (even by such
background
workers) in XLogWrite(), instead.

For example, we can add the "shared" counters (protected by WALWriteLock)
into XLogCtl and make XLogWrite() update those counters if the process is
NOT walwriter. OTOH, if it's walwriter, XLogWrite() updates its local
counters.
Then walwriter periodically packs those counters in the message and sends
it to the stats collector.

Updated patch to use shared counter instead of adding pg_stat_ calls to send
the statistics from each background process/worker.

All the processes updates the shared counters, the walwriter process fill
the
local structure with the shared counters and send it to the stats collector.

update patch attached.

Thanks for updating the patch!

+      <entry><structfield>writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of WAL writes that are carried out by background
processes and workers.
+      </entry>
<snip>
+      <entry><structfield>write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the
background processes/workers</entry>
+     </row>

Could you tell me why both numbers of WAL writes and pages written need to
be reported? How can we tune the system by using those information?

With the column of write_blocks, it is useful to find out how many number
of blocks
that gets written from backend along with the number of writes. I feel
1. If we only get the count of number of writes and the write amount is
very less,
so it may not need any tuning.
2. If the amount of write is good even for less number of writes, then it
needs
some tuning.

Updated patch attached.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_stat_walwrites_view_5.patchapplication/octet-stream; name=pg_stat_walwrites_view_5.patchDownload
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index ac339fb..ed833e0 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5732,7 +5732,8 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
         platforms.  You can use the <xref linkend="pgtesttiming"> tool to
         measure the overhead of timing on your system.
         I/O timing information is
-        displayed in <xref linkend="pg-stat-database-view">, in the output of
+        displayed in <xref linkend="pg-stat-database-view">, 
+        <xref linkend="pg-stat-walwrites-view">, in the output of
         <xref linkend="sql-explain"> when the <literal>BUFFERS</> option is
         used, and by <xref linkend="pgstatstatements">.  Only superusers can
         change this setting.
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 9856968..b5ed688 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -357,6 +357,14 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
      </row>
 
      <row>
+      <entry><structname>pg_stat_walwrites</><indexterm><primary>pg_stat_walwrites</primary></indexterm></entry>
+      <entry>One row only, showing statistics about the
+       WAL writing activity. See
+       <xref linkend="pg-stat-walwrites-view"> for details.
+     </entry>
+     </row>
+
+     <row>
       <entry><structname>pg_stat_database</><indexterm><primary>pg_stat_database</primary></indexterm></entry>
       <entry>One row per database, showing database-wide statistics. See
        <xref linkend="pg-stat-database-view"> for details.
@@ -2207,6 +2215,108 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
    single row, containing global data for the cluster.
   </para>
 
+  <table id="pg-stat-walwrites-view" xreflabel="pg_stat_walwrites">
+   <title><structname>pg_stat_walwrites</structname> View</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of WAL writes that are carried out by background processes and workers. 
+      </entry>
+     </row>
+    <row>
+      <entry><structfield>backend_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL writes that are carried out by the backend process</entry>
+     </row>
+     <row>
+     <entry><structfield>dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by background processes and workers
+      when the <xref linkend="guc-wal-buffers"> are full.
+      </entry>
+     </row>
+    <row>
+      <entry><structfield>backend_dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by the backend processes when 
+      the <xref linkend="guc-wal-buffers"> are full. 
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the background processes/workers</entry>
+     </row>
+     <row>
+      <entry><structfield>backend_write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the backend processes</entry>
+     </row>
+     <row>
+      <entry><structfield>write_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are written to disk by the background processes/workers, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>backend_write_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are written to disk by the backend processes, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>sync_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are synchronized to disk by the background processes/workers, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>backend_sync_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are synchronized to disk by the backend processes, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>stats_reset</></entry>
+      <entry><type>timestamp with time zone</type></entry>
+      <entry>Time at which these statistics were last reset</entry>
+     </row>
+     </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_walwrites</structname> view will always have a
+   single row, containing data about the WAL writing activity of the cluster.
+  </para>
+
+
   <table id="pg-stat-database-view" xreflabel="pg_stat_database">
    <title><structname>pg_stat_database</structname> View</title>
    <tgroup cols="3">
@@ -2986,6 +3096,8 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        counters shown in the <structname>pg_stat_bgwriter</> view.
        Calling <literal>pg_stat_reset_shared('archiver')</> will zero all the
        counters shown in the <structname>pg_stat_archiver</> view.
+       Calling <literal>pg_stat_reset_shared('walwrites')</> will zero all the
+       counters shown in the <structname>pg_stat_walwrites</> view.
       </entry>
      </row>
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 5d58f09..a29c108 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -600,6 +600,9 @@ typedef struct XLogCtlData
 	 */
 	XLogwrtResult LogwrtResult;
 
+	/* Protected by WALWriteLock */
+	PgStat_WalWritesCounts stats;
+
 	/*
 	 * Latest initialized page in the cache (last byte position + 1).
 	 *
@@ -860,6 +863,7 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void);
 
 static void AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic);
 static bool XLogCheckpointNeeded(XLogSegNo new_segno);
+static bool am_background_process(void);
 static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible);
 static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath,
 					   bool find_free, XLogSegNo max_segno,
@@ -2109,6 +2113,10 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic)
 					WriteRqst.Write = OldPageRqstPtr;
 					WriteRqst.Flush = 0;
 					XLogWrite(WriteRqst, false);
+					if (am_background_process())
+						XLogCtl->stats.dirty_writes++;
+					else
+						XLogCtl->stats.backend_dirty_writes++;
 					LWLockRelease(WALWriteLock);
 					TRACE_POSTGRESQL_WAL_BUFFER_WRITE_DIRTY_DONE();
 				}
@@ -2319,6 +2327,34 @@ XLogCheckpointNeeded(XLogSegNo new_segno)
 }
 
 /*
+ * Check whether the current process is a normal backend or not.
+ * This function checks for the background processes that does
+ * some WAL write activity only and other background processes
+ * are not considered. It considers all the background workers
+ * as WAL write activity workers.
+ *
+ * Returns FALSE - when the current process is a normal backend
+ *		   TRUE - when the current process a background process/worker
+ */
+static bool
+am_background_process()
+{
+	/* check whether current process is a background process/worker? */
+	if (AmBackgroundWriterProcess() ||
+		AmWalWriterProcess() ||
+		AmCheckpointerProcess() ||
+		AmStartupProcess() ||
+		IsBackgroundWorker ||
+		am_walsender ||
+		am_autovacuum_worker)
+	{
+		return true;
+	}
+
+	return false;
+}
+
+/*
  * Write and/or fsync the log at least as far as WriteRqst indicates.
  *
  * If flexible == TRUE, we don't have to write as far as WriteRqst, but
@@ -2341,6 +2377,9 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 	int			npages;
 	int			startidx;
 	uint32		startoffset;
+	instr_time	io_start,
+				io_time;
+	bool		is_background_process = am_background_process();
 
 	/* We should always be inside a critical section here */
 	Assert(CritSectionCount > 0);
@@ -2458,6 +2497,11 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			/* OK to write the page(s) */
 			from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ;
 			nbytes = npages * (Size) XLOG_BLCKSZ;
+
+			/* Start timer to acquire start time of the wal write */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			nleft = nbytes;
 			do
 			{
@@ -2480,6 +2524,34 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				from += written;
 			} while (nleft > 0);
 
+			/* calculate the total time spent for wal writing */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				if (is_background_process)
+					XLogCtl->stats.total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+				else
+					XLogCtl->stats.backend_total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				XLogCtl->stats.total_write_time = 0;
+				XLogCtl->stats.backend_total_write_time = 0;
+			}
+
+			/* check whether writer is a normal backend or not? */
+			if (is_background_process)
+				XLogCtl->stats.writes++;
+			else
+				XLogCtl->stats.backend_writes++;
+
+			if (is_background_process)
+				XLogCtl->stats.write_blocks += npages;
+			else
+				XLogCtl->stats.backend_write_blocks += npages;
+
 			/* Update state for write */
 			openLogOff += nbytes;
 			npages = 0;
@@ -2499,8 +2571,29 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			 */
 			if (finishing_seg)
 			{
+				/* Start timer to acquire start time of the wal sync */
+				if (track_io_timing)
+					INSTR_TIME_SET_CURRENT(io_start);
+
 				issue_xlog_fsync(openLogFile, openLogSegNo);
 
+				/* calculate the total time spent for wal sync */
+				if (track_io_timing)
+				{
+					INSTR_TIME_SET_CURRENT(io_time);
+					INSTR_TIME_SUBTRACT(io_time, io_start);
+
+					if (is_background_process)
+						XLogCtl->stats.total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+					else
+						XLogCtl->stats.backend_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+				}
+				else
+				{
+					XLogCtl->stats.total_sync_time = 0;
+					XLogCtl->stats.backend_total_sync_time = 0;
+				}
+
 				/* signal that we need to wakeup walsenders later */
 				WalSndWakeupRequest();
 
@@ -2568,7 +2661,28 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				openLogOff = 0;
 			}
 
+			/* Start timer to acquire start time of the wal sync */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			issue_xlog_fsync(openLogFile, openLogSegNo);
+
+			/* calculate the total time spent for wal sync */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				if (is_background_process)
+					XLogCtl->stats.total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+				else
+					XLogCtl->stats.backend_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				XLogCtl->stats.total_sync_time = 0;
+				XLogCtl->stats.backend_total_sync_time = 0;
+			}
 		}
 
 		/* signal that we need to wakeup walsenders later */
@@ -3043,6 +3157,9 @@ XLogBackgroundFlush(void)
 	{
 		XLogWrite(WriteRqst, flexible);
 	}
+
+	/* Collect all the wal write shared counters into local, and report it to stats collector */
+	memcpy(&LocalWalWritesStats.stats, &XLogCtl->stats, sizeof(PgStat_WalWritesCounts));
 	LWLockRelease(WALWriteLock);
 
 	END_CRIT_SECTION();
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index d357c8b..f424c9d 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -891,6 +891,9 @@ CREATE VIEW pg_stat_bgwriter AS
         pg_stat_get_buf_alloc() AS buffers_alloc,
         pg_stat_get_bgwriter_stat_reset_time() AS stats_reset;
 
+CREATE VIEW pg_stat_walwrites AS
+   SELECT * FROM pg_stat_get_walwrites() AS A;
+
 CREATE VIEW pg_stat_progress_vacuum AS
 	SELECT
 		S.pid AS pid, S.datid AS datid, D.datname AS datname,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 33ca749..5a42c69 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -132,7 +132,6 @@ int			Log_autovacuum_min_duration = -1;
 
 /* Flags to tell if we are in an autovacuum process */
 static bool am_autovacuum_launcher = false;
-static bool am_autovacuum_worker = false;
 
 /* Flags set by signal handlers */
 static volatile sig_atomic_t got_SIGHUP = false;
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 56a8bf2..cdb1e9f 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -139,6 +139,15 @@ char	   *pgstat_stat_tmpname = NULL;
  */
 PgStat_MsgBgWriter BgWriterStats;
 
+/*
+ * WalWrites Local statistics counters.
+ * The statistics data gets populated in XLogWrite function.
+ * Stored directly in a stats message structure so it can be sent
+ * to stats collector process without needing to copy things around.
+ * We assume this inits to zeroes.
+ */
+PgStat_MsgWalWrites LocalWalWritesStats;
+
 /* ----------
  * Local data
  * ----------
@@ -253,6 +262,7 @@ static int	localNumBackends = 0;
  */
 static PgStat_ArchiverStats archiverStats;
 static PgStat_GlobalStats globalStats;
+static PgStat_WalWritesStats walwritesStats;
 
 /*
  * List of OIDs of databases we need to write out.  If an entry is InvalidOid,
@@ -333,6 +343,7 @@ static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
 static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
 static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
+static void pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len);
 
 /* ------------------------------------------------------------
  * Public functions called from postmaster follow
@@ -1300,11 +1311,13 @@ pgstat_reset_shared_counters(const char *target)
 		msg.m_resettarget = RESET_ARCHIVER;
 	else if (strcmp(target, "bgwriter") == 0)
 		msg.m_resettarget = RESET_BGWRITER;
+	else if (strcmp(target, "walwrites") == 0)
+		msg.m_resettarget = RESET_WALWRITES;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("unrecognized reset target: \"%s\"", target),
-				 errhint("Target must be \"archiver\" or \"bgwriter\".")));
+				 errhint("Target must be \"archiver\" or \"bgwriter\" or \"walwrites\".")));
 
 	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER);
 	pgstat_send(&msg, sizeof(msg));
@@ -2543,6 +2556,21 @@ pgstat_fetch_global(void)
 	return &globalStats;
 }
 
+/*
+ * ---------
+ * pgstat_fetch_stat_walwrites() -
+ *
+ *	Support function for the SQL-callable pgstat* functions. Returns
+ *	a pointer to the walwrites statistics struct.
+ * ---------
+ */
+PgStat_WalWritesStats *
+pgstat_fetch_stat_walwrites(void)
+{
+	backend_read_statsfile();
+
+	return &walwritesStats;
+}
 
 /* ------------------------------------------------------------
  * Functions for management of the shared-memory PgBackendStatus array
@@ -4123,6 +4151,39 @@ pgstat_send_bgwriter(void)
 	MemSet(&BgWriterStats, 0, sizeof(BgWriterStats));
 }
 
+/* ----------
+ * pgstat_send_walwrites() -
+ *
+ *		Send wal writes statistics to the collector
+ * ----------
+ */
+
+void
+pgstat_send_walwrites(void)
+{
+	/* We assume this initializes to zeroes */
+	static const PgStat_MsgWalWrites all_zeroes;
+
+	/*
+	 * This function can be called even if nothing at all has happened. In
+	 * this case, avoid sending a completely empty message to the stats
+	 * collector.
+	 */
+	if (memcmp(&LocalWalWritesStats, &all_zeroes, sizeof(PgStat_MsgWalWrites)) == 0)
+		return;
+
+	/*
+	 * Prepare and send the message
+	 */
+	pgstat_setheader(&LocalWalWritesStats.m_hdr, PGSTAT_MTYPE_WALWRITES);
+	pgstat_send(&LocalWalWritesStats, sizeof(LocalWalWritesStats));
+
+	/*
+	 * Clear out the statistics buffer, so it can be re-used.
+	 */
+	MemSet(&LocalWalWritesStats, 0, sizeof(LocalWalWritesStats));
+}
+
 
 /* ----------
  * PgstatCollectorMain() -
@@ -4339,6 +4400,10 @@ PgstatCollectorMain(int argc, char *argv[])
 					pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len);
 					break;
 
+				case PGSTAT_MTYPE_WALWRITES:
+					pgstat_recv_walwrites((PgStat_MsgWalWrites *) & msg, len);
+					break;
+
 				default:
 					break;
 			}
@@ -4605,6 +4670,12 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 	(void) rc;					/* we'll check for error with ferror */
 
 	/*
+	 * Write wal writes stats struct
+	 */
+	rc = fwrite(&walwritesStats, sizeof(walwritesStats), 1, fpout);
+	(void) rc;					/* we'll check for error with ferror */
+
+	/*
 	 * Walk through the database table.
 	 */
 	hash_seq_init(&hstat, pgStatDBHash);
@@ -4861,6 +4932,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	memset(&globalStats, 0, sizeof(globalStats));
 	memset(&archiverStats, 0, sizeof(archiverStats));
+	memset(&walwritesStats, 0, sizeof(walwritesStats));
 
 	/*
 	 * Set the current timestamp (will be kept only in case we can't load an
@@ -4868,6 +4940,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	globalStats.stat_reset_timestamp = GetCurrentTimestamp();
 	archiverStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
+	walwritesStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
 
 	/*
 	 * Try to open the stats file. If it doesn't exist, the backends simply
@@ -4920,6 +4993,16 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	}
 
 	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&walwritesStats, 1, sizeof(walwritesStats), fpin) != sizeof(walwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		goto done;
+	}
+
+	/*
 	 * We found an existing collector stats file. Read it and put all the
 	 * hashtable entries into place.
 	 */
@@ -5208,6 +5291,7 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 	PgStat_StatDBEntry dbentry;
 	PgStat_GlobalStats myGlobalStats;
 	PgStat_ArchiverStats myArchiverStats;
+	PgStat_WalWritesStats myWalwritesStats;
 	FILE	   *fpin;
 	int32		format_id;
 	const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
@@ -5262,6 +5346,18 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 		return false;
 	}
 
+	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&myWalwritesStats, 1, sizeof(myWalwritesStats),
+			  fpin) != sizeof(myWalwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		FreeFile(fpin);
+		return false;
+	}
+
 	/* By default, we're going to return the timestamp of the global file. */
 	*ts = myGlobalStats.stats_timestamp;
 
@@ -5824,6 +5920,12 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
 		memset(&archiverStats, 0, sizeof(archiverStats));
 		archiverStats.stat_reset_timestamp = GetCurrentTimestamp();
 	}
+	else if (msg->m_resettarget == RESET_WALWRITES)
+	{
+		/* Reset the wal writes statistics of the cluster. */
+		memset(&walwritesStats, 0, sizeof(walwritesStats));
+		walwritesStats.stat_reset_timestamp = GetCurrentTimestamp();
+	}
 
 	/*
 	 * Presumably the sender of this message validated the target, don't
@@ -6004,6 +6106,27 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
 }
 
 /* ----------
+ * pgstat_recv_walwrites() -
+ *
+ *	Process a WALWRITES message.
+ * ----------
+ */
+static void
+pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len)
+{
+	walwritesStats.stats.writes += msg->stats.writes;
+	walwritesStats.stats.backend_writes += msg->stats.backend_writes;
+	walwritesStats.stats.dirty_writes += msg->stats.dirty_writes;
+	walwritesStats.stats.backend_dirty_writes += msg->stats.backend_dirty_writes;
+	walwritesStats.stats.write_blocks += msg->stats.write_blocks;
+	walwritesStats.stats.backend_write_blocks += msg->stats.backend_write_blocks;
+	walwritesStats.stats.total_write_time += msg->stats.total_write_time;
+	walwritesStats.stats.backend_total_write_time += msg->stats.backend_total_write_time;
+	walwritesStats.stats.total_sync_time += msg->stats.total_sync_time;
+	walwritesStats.stats.backend_total_sync_time += msg->stats.backend_total_sync_time;
+}
+
+/* ----------
  * pgstat_recv_recoveryconflict() -
  *
  *	Process a RECOVERYCONFLICT message.
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index a575d8f..cf0c279 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -280,6 +280,8 @@ WalWriterMain(void)
 		else if (left_till_hibernate > 0)
 			left_till_hibernate--;
 
+		pgstat_send_walwrites();
+
 		/*
 		 * Sleep until we are signaled or WalWriterDelay has elapsed.  If we
 		 * haven't done anything useful for quite some time, lengthen the
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index dd2b924..6c8c934 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1859,3 +1859,42 @@ pg_stat_get_archiver(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(HeapTupleGetDatum(
 								   heap_form_tuple(tupdesc, values, nulls)));
 }
+
+#define NUM_PG_STAT_WALWRITE_ATTS 11
+
+Datum
+pg_stat_get_walwrites(PG_FUNCTION_ARGS)
+{
+	TupleDesc	tupdesc;
+	Datum		values[NUM_PG_STAT_WALWRITE_ATTS];
+	bool		nulls[NUM_PG_STAT_WALWRITE_ATTS];
+	PgStat_WalWritesStats *walwrite_stats;
+
+	/* Initialize values and NULL flags arrays */
+	MemSet(values, 0, sizeof(values));
+	MemSet(nulls, 0, sizeof(nulls));
+
+	/* 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");
+
+	/* Get statistics about the archiver process */
+	walwrite_stats = pgstat_fetch_stat_walwrites();
+
+	/* Fill values and NULLs */
+	values[0] = Int64GetDatum(walwrite_stats->stats.writes);
+	values[1] = Int64GetDatum(walwrite_stats->stats.backend_writes);
+	values[2] = Int64GetDatum(walwrite_stats->stats.dirty_writes);
+	values[3] = Int64GetDatum(walwrite_stats->stats.backend_dirty_writes);
+	values[4] = Int64GetDatum(walwrite_stats->stats.write_blocks);
+	values[5] = Int64GetDatum(walwrite_stats->stats.backend_write_blocks);
+	values[6] = Float8GetDatum(walwrite_stats->stats.total_write_time);
+	values[7] = Float8GetDatum(walwrite_stats->stats.backend_total_write_time);
+	values[8] = Float8GetDatum(walwrite_stats->stats.total_sync_time);
+	values[9] = Float8GetDatum(walwrite_stats->stats.backend_total_sync_time);
+	values[10] = TimestampTzGetDatum(walwrite_stats->stat_reset_timestamp);
+
+	/* Returns the record as Datum */
+	PG_RETURN_DATUM(HeapTupleGetDatum(
+								   heap_form_tuple(tupdesc, values, nulls)));
+}
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 08b6030..4931559 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -125,6 +125,8 @@ int			max_worker_processes = 8;
 int			max_parallel_workers = 8;
 int			MaxBackends = 0;
 
+bool 		am_autovacuum_worker = false;
+
 int			VacuumCostPageHit = 1;		/* GUC parameters for vacuum */
 int			VacuumCostPageMiss = 10;
 int			VacuumCostPageDirty = 20;
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 220ba7b..04f42b1 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2916,6 +2916,9 @@ DESCR("statistics: number of backend buffer writes that did their own fsync");
 DATA(insert OID = 2859 ( pg_stat_get_buf_alloc			PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_buf_alloc _null_ _null_ _null_ ));
 DESCR("statistics: number of buffer allocations");
 
+DATA(insert OID = 3373 (  pg_stat_get_walwrites		PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{20,20,20,20,20,20,701,701,701,701,1184}" "{o,o,o,o,o,o,o,o,o,o,o}" "{writes,backend_writes,dirty_writes,backend_dirty_writes,write_blocks,backend_write_blocks,write_time,backend_write_time,sync_time,backend_sync_time,stats_reset}" _null_ _null_ pg_stat_get_walwrites _null_ _null_ _null_ ));
+DESCR("statistics: information about WAL writes activity");
+
 DATA(insert OID = 2978 (  pg_stat_get_function_calls		PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_calls _null_ _null_ _null_ ));
 DESCR("statistics: number of function calls");
 DATA(insert OID = 2979 (  pg_stat_get_function_total_time	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 701 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_total_time _null_ _null_ _null_ ));
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 4c607b2..386ed06 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -242,6 +242,8 @@ extern PGDLLIMPORT int work_mem;
 extern PGDLLIMPORT int maintenance_work_mem;
 extern PGDLLIMPORT int replacement_sort_tuples;
 
+extern bool	am_autovacuum_worker;
+
 extern int	VacuumCostPageHit;
 extern int	VacuumCostPageMiss;
 extern int	VacuumCostPageDirty;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index e29397f..5680f23 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -64,7 +64,8 @@ typedef enum StatMsgType
 	PGSTAT_MTYPE_FUNCPURGE,
 	PGSTAT_MTYPE_RECOVERYCONFLICT,
 	PGSTAT_MTYPE_TEMPFILE,
-	PGSTAT_MTYPE_DEADLOCK
+	PGSTAT_MTYPE_DEADLOCK,
+	PGSTAT_MTYPE_WALWRITES
 } StatMsgType;
 
 /* ----------
@@ -119,7 +120,8 @@ typedef struct PgStat_TableCounts
 typedef enum PgStat_Shared_Reset_Target
 {
 	RESET_ARCHIVER,
-	RESET_BGWRITER
+	RESET_BGWRITER,
+	RESET_WALWRITES
 } PgStat_Shared_Reset_Target;
 
 /* Possible object types for resetting single counters */
@@ -422,6 +424,43 @@ typedef struct PgStat_MsgBgWriter
 	PgStat_Counter m_checkpoint_sync_time;
 } PgStat_MsgBgWriter;
 
+/*
+ * Walwrites statistics counters
+ */
+typedef struct PgStat_WalWritesCounts
+{
+	PgStat_Counter writes;		/* No of writes by background
+								 * processes/workers */
+	PgStat_Counter backend_writes;		/* No of writes by backend */
+	PgStat_Counter dirty_writes;/* No of dirty writes by background
+								 * processes/workers when WAL buffers full */
+	PgStat_Counter backend_dirty_writes;		/* No of dirty writes by
+												 * backend when WAL buffers
+												 * full */
+	PgStat_Counter write_blocks;/* Total no of pages written by background
+								 * processes/workers */
+	PgStat_Counter backend_write_blocks;		/* Total no of pages written
+												 * by backend */
+	PgStat_Counter total_write_time;	/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter backend_total_write_time;	/* Total write time in
+												 * milliseconds by backend */
+	PgStat_Counter total_sync_time;		/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter backend_total_sync_time;		/* Total write time in
+												 * milliseconds by backend */
+} PgStat_WalWritesCounts;
+
+/* ----------
+ * PgStat_MsgWalWrites			Sent by the walwriter after collecting all shared stats
+ * ----------
+ */
+typedef struct PgStat_MsgWalWrites
+{
+	PgStat_MsgHdr m_hdr;
+	PgStat_WalWritesCounts stats;
+} PgStat_MsgWalWrites;
+
 /* ----------
  * PgStat_MsgRecoveryConflict	Sent by the backend upon recovery conflict
  * ----------
@@ -555,6 +594,7 @@ typedef union PgStat_Msg
 	PgStat_MsgFuncpurge msg_funcpurge;
 	PgStat_MsgRecoveryConflict msg_recoveryconflict;
 	PgStat_MsgDeadlock msg_deadlock;
+	PgStat_MsgWalWrites msg_walwrites;
 } PgStat_Msg;
 
 
@@ -694,6 +734,14 @@ typedef struct PgStat_GlobalStats
 	TimestampTz stat_reset_timestamp;
 } PgStat_GlobalStats;
 
+/*
+ * Walwrites statistics kept in the stats collector
+ */
+typedef struct PgStat_WalWritesStats
+{
+	PgStat_WalWritesCounts stats;
+	TimestampTz stat_reset_timestamp;	/* Last time when the stats reset */
+} PgStat_WalWritesStats;
 
 /* ----------
  * Backend types
@@ -1115,6 +1163,11 @@ extern char *pgstat_stat_filename;
 extern PgStat_MsgBgWriter BgWriterStats;
 
 /*
+ * Wal writes statistics updated in XLogWrite function
+ */
+extern PgStat_MsgWalWrites LocalWalWritesStats;
+
+/*
  * Updated by pgstat_count_buffer_*_time macros
  */
 extern PgStat_Counter pgStatBlockReadTime;
@@ -1308,6 +1361,7 @@ extern void pgstat_twophase_postabort(TransactionId xid, uint16 info,
 
 extern void pgstat_send_archiver(const char *xlog, bool failed);
 extern void pgstat_send_bgwriter(void);
+extern void pgstat_send_walwrites(void);
 
 /* ----------
  * Support functions for the SQL-callable functions to
@@ -1322,5 +1376,7 @@ extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
 extern int	pgstat_fetch_stat_numbackends(void);
 extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
 extern PgStat_GlobalStats *pgstat_fetch_global(void);
+extern PgStat_WalWritesStats *pgstat_fetch_stat_walwrites(void);
+
 
 #endif   /* PGSTAT_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index d706f42..4c0a55b 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1973,6 +1973,18 @@ pg_stat_wal_receiver| SELECT s.pid,
     s.conninfo
    FROM pg_stat_get_wal_receiver() s(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, conninfo)
   WHERE (s.pid IS NOT NULL);
+pg_stat_walwrites| SELECT a.writes,
+    a.backend_writes,
+    a.dirty_writes,
+    a.backend_dirty_writes,
+    a.write_blocks,
+    a.backend_write_blocks,
+    a.write_time,
+    a.backend_write_time,
+    a.sync_time,
+    a.backend_sync_time,
+    a.stats_reset
+   FROM pg_stat_get_walwrites() a(writes, backend_writes, dirty_writes, backend_dirty_writes, write_blocks, backend_write_blocks, write_time, backend_write_time, sync_time, backend_sync_time, stats_reset);
 pg_stat_xact_all_tables| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 568b783..8448cac 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -67,6 +67,13 @@ select count(*) >= 0 as ok from pg_prepared_xacts;
  t
 (1 row)
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+ ok 
+----
+ t
+(1 row)
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 28e412b..3769880 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -32,6 +32,9 @@ select count(*) = 0 as ok from pg_prepared_statements;
 -- See also prepared_xacts.sql
 select count(*) >= 0 as ok from pg_prepared_xacts;
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
#16Andres Freund
andres@anarazel.de
In reply to: Haribabu Kommi (#15)
Re: pg_stat_wal_write statistics view

Hi,

On 2017-03-30 13:10:41 +1100, Haribabu Kommi wrote:

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 5d58f09..a29c108 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -600,6 +600,9 @@ typedef struct XLogCtlData
*/
XLogwrtResult LogwrtResult;

+ /* Protected by WALWriteLock */
+ PgStat_WalWritesCounts stats;

/*
+ * Check whether the current process is a normal backend or not.
+ * This function checks for the background processes that does
+ * some WAL write activity only and other background processes
+ * are not considered. It considers all the background workers
+ * as WAL write activity workers.
+ *
+ * Returns FALSE - when the current process is a normal backend
+ *		   TRUE - when the current process a background process/worker
+ */

I don't think we commonly capitalize true/false these days.

+static bool
+am_background_process()
+{
+	/* check whether current process is a background process/worker? */
+	if (AmBackgroundWriterProcess() ||
+		AmWalWriterProcess() ||
+		AmCheckpointerProcess() ||
+		AmStartupProcess() ||
+		IsBackgroundWorker ||
+		am_walsender ||
+		am_autovacuum_worker)
+	{
+		return true;
+	}
+
+	return false;
+}

Uhm, wouldn't inverting this be a lot easier and future proof? There's
far fewer non-background processes.

+/*
* Write and/or fsync the log at least as far as WriteRqst indicates.
*
* If flexible == TRUE, we don't have to write as far as WriteRqst, but
@@ -2341,6 +2377,9 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
int			npages;
int			startidx;
uint32		startoffset;
+	instr_time	io_start,
+				io_time;
+	bool		is_background_process = am_background_process();
/* We should always be inside a critical section here */
Assert(CritSectionCount > 0);
@@ -2458,6 +2497,11 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
/* OK to write the page(s) */
from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ;
nbytes = npages * (Size) XLOG_BLCKSZ;
+
+			/* Start timer to acquire start time of the wal write */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+

I'm more than a bit hesitant adding overhead to WAL writing - it's
already quite a bit of a bottleneck. Normally track_io_timing just
makes things a tiny bit slower, but doesn't cause a scalability issue,
here it does. This is all done under an already highly contended lock.

nleft = nbytes;
do
{
@@ -2480,6 +2524,34 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
from += written;
} while (nleft > 0);

+			/* calculate the total time spent for wal writing */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				if (is_background_process)
+					XLogCtl->stats.total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+				else
+					XLogCtl->stats.backend_total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				XLogCtl->stats.total_write_time = 0;
+				XLogCtl->stats.backend_total_write_time = 0;
+			}
+
+			/* check whether writer is a normal backend or not? */
+			if (is_background_process)
+				XLogCtl->stats.writes++;
+			else
+				XLogCtl->stats.backend_writes++;
+
+			if (is_background_process)
+				XLogCtl->stats.write_blocks += npages;
+			else
+				XLogCtl->stats.backend_write_blocks += npages;
+
/* Update state for write */
openLogOff += nbytes;
npages = 0;
@@ -2499,8 +2571,29 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
*/
if (finishing_seg)
{
+				/* Start timer to acquire start time of the wal sync */
+				if (track_io_timing)
+					INSTR_TIME_SET_CURRENT(io_start);
+
issue_xlog_fsync(openLogFile, openLogSegNo);
+				/* calculate the total time spent for wal sync */
+				if (track_io_timing)
+				{
+					INSTR_TIME_SET_CURRENT(io_time);
+					INSTR_TIME_SUBTRACT(io_time, io_start);
+
+					if (is_background_process)
+						XLogCtl->stats.total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+					else
+						XLogCtl->stats.backend_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+				}
+				else
+				{
+					XLogCtl->stats.total_sync_time = 0;
+					XLogCtl->stats.backend_total_sync_time = 0;
+				}
+
/* signal that we need to wakeup walsenders later */
WalSndWakeupRequest();

@@ -2568,7 +2661,28 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
openLogOff = 0;
}

+			/* Start timer to acquire start time of the wal sync */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
issue_xlog_fsync(openLogFile, openLogSegNo);
+
+			/* calculate the total time spent for wal sync */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				if (is_background_process)
+					XLogCtl->stats.total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+				else
+					XLogCtl->stats.backend_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				XLogCtl->stats.total_sync_time = 0;
+				XLogCtl->stats.backend_total_sync_time = 0;
+			}
}

I'm *very* doubtful about this, but even if we do it, this needs careful
benchmarking.

/* signal that we need to wakeup walsenders later */
@@ -3043,6 +3157,9 @@ XLogBackgroundFlush(void)
{
XLogWrite(WriteRqst, flexible);
}
+
+	/* Collect all the wal write shared counters into local, and report it to stats collector */
+	memcpy(&LocalWalWritesStats.stats, &XLogCtl->stats, sizeof(PgStat_WalWritesCounts));
LWLockRelease(WALWriteLock);

Hm. I think in a good number of workloads hti sis never reached, because
we return early.

I think this is an interesting feature, but I don't think it's ready,
and it was submitted very late in the v10 release cycle. Therefore I
think this should be moved to the next CF.

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

#17Fujii Masao
masao.fujii@gmail.com
In reply to: Andres Freund (#16)
Re: pg_stat_wal_write statistics view

On Thu, Apr 6, 2017 at 5:51 AM, Andres Freund <andres@anarazel.de> wrote:

Hi,

On 2017-03-30 13:10:41 +1100, Haribabu Kommi wrote:

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 5d58f09..a29c108 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -600,6 +600,9 @@ typedef struct XLogCtlData
*/
XLogwrtResult LogwrtResult;

+ /* Protected by WALWriteLock */
+ PgStat_WalWritesCounts stats;

/*
+ * Check whether the current process is a normal backend or not.
+ * This function checks for the background processes that does
+ * some WAL write activity only and other background processes
+ * are not considered. It considers all the background workers
+ * as WAL write activity workers.
+ *
+ * Returns FALSE - when the current process is a normal backend
+ *              TRUE - when the current process a background process/worker
+ */

I don't think we commonly capitalize true/false these days.

+static bool
+am_background_process()
+{
+     /* check whether current process is a background process/worker? */
+     if (AmBackgroundWriterProcess() ||
+             AmWalWriterProcess() ||
+             AmCheckpointerProcess() ||
+             AmStartupProcess() ||
+             IsBackgroundWorker ||
+             am_walsender ||
+             am_autovacuum_worker)
+     {
+             return true;
+     }
+
+     return false;
+}

Uhm, wouldn't inverting this be a lot easier and future proof? There's
far fewer non-background processes.

+/*
* Write and/or fsync the log at least as far as WriteRqst indicates.
*
* If flexible == TRUE, we don't have to write as far as WriteRqst, but
@@ -2341,6 +2377,9 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
int                     npages;
int                     startidx;
uint32          startoffset;
+     instr_time      io_start,
+                             io_time;
+     bool            is_background_process = am_background_process();
/* We should always be inside a critical section here */
Assert(CritSectionCount > 0);
@@ -2458,6 +2497,11 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
/* OK to write the page(s) */
from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ;
nbytes = npages * (Size) XLOG_BLCKSZ;
+
+                     /* Start timer to acquire start time of the wal write */
+                     if (track_io_timing)
+                             INSTR_TIME_SET_CURRENT(io_start);
+

I'm more than a bit hesitant adding overhead to WAL writing - it's
already quite a bit of a bottleneck. Normally track_io_timing just
makes things a tiny bit slower, but doesn't cause a scalability issue,
here it does. This is all done under an already highly contended lock.

nleft = nbytes;
do
{
@@ -2480,6 +2524,34 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
from += written;
} while (nleft > 0);

+                     /* calculate the total time spent for wal writing */
+                     if (track_io_timing)
+                     {
+                             INSTR_TIME_SET_CURRENT(io_time);
+                             INSTR_TIME_SUBTRACT(io_time, io_start);
+
+                             if (is_background_process)
+                                     XLogCtl->stats.total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+                             else
+                                     XLogCtl->stats.backend_total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+                     }
+                     else
+                     {
+                             XLogCtl->stats.total_write_time = 0;
+                             XLogCtl->stats.backend_total_write_time = 0;
+                     }
+
+                     /* check whether writer is a normal backend or not? */
+                     if (is_background_process)
+                             XLogCtl->stats.writes++;
+                     else
+                             XLogCtl->stats.backend_writes++;
+
+                     if (is_background_process)
+                             XLogCtl->stats.write_blocks += npages;
+                     else
+                             XLogCtl->stats.backend_write_blocks += npages;
+
/* Update state for write */
openLogOff += nbytes;
npages = 0;
@@ -2499,8 +2571,29 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
*/
if (finishing_seg)
{
+                             /* Start timer to acquire start time of the wal sync */
+                             if (track_io_timing)
+                                     INSTR_TIME_SET_CURRENT(io_start);
+
issue_xlog_fsync(openLogFile, openLogSegNo);
+                             /* calculate the total time spent for wal sync */
+                             if (track_io_timing)
+                             {
+                                     INSTR_TIME_SET_CURRENT(io_time);
+                                     INSTR_TIME_SUBTRACT(io_time, io_start);
+
+                                     if (is_background_process)
+                                             XLogCtl->stats.total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+                                     else
+                                             XLogCtl->stats.backend_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+                             }
+                             else
+                             {
+                                     XLogCtl->stats.total_sync_time = 0;
+                                     XLogCtl->stats.backend_total_sync_time = 0;
+                             }
+
/* signal that we need to wakeup walsenders later */
WalSndWakeupRequest();

@@ -2568,7 +2661,28 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
openLogOff = 0;
}

+                     /* Start timer to acquire start time of the wal sync */
+                     if (track_io_timing)
+                             INSTR_TIME_SET_CURRENT(io_start);
+
issue_xlog_fsync(openLogFile, openLogSegNo);
+
+                     /* calculate the total time spent for wal sync */
+                     if (track_io_timing)
+                     {
+                             INSTR_TIME_SET_CURRENT(io_time);
+                             INSTR_TIME_SUBTRACT(io_time, io_start);
+
+                             if (is_background_process)
+                                     XLogCtl->stats.total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+                             else
+                                     XLogCtl->stats.backend_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+                     }
+                     else
+                     {
+                             XLogCtl->stats.total_sync_time = 0;
+                             XLogCtl->stats.backend_total_sync_time = 0;
+                     }
}

I'm *very* doubtful about this, but even if we do it, this needs careful
benchmarking.

/* signal that we need to wakeup walsenders later */
@@ -3043,6 +3157,9 @@ XLogBackgroundFlush(void)
{
XLogWrite(WriteRqst, flexible);
}
+
+     /* Collect all the wal write shared counters into local, and report it to stats collector */
+     memcpy(&LocalWalWritesStats.stats, &XLogCtl->stats, sizeof(PgStat_WalWritesCounts));
LWLockRelease(WALWriteLock);

Hm. I think in a good number of workloads hti sis never reached, because
we return early.

I think this is an interesting feature, but I don't think it's ready,
and it was submitted very late in the v10 release cycle. Therefore I
think this should be moved to the next CF.

+1

I think that there is no consensus yet about what values should be exposed.
For example, with the patch, WAL writing activity by backends is reported
separately from that by the other processes. But I'm not sure if this grouping
is good. It seems better to report walwriter activity separately from
the others, for tuning of walwriter.

Regards,

--
Fujii Masao

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

#18Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Haribabu Kommi (#15)
Re: pg_stat_wal_write statistics view

On 3/29/17 22:10, Haribabu Kommi wrote:

Updated patch to use shared counter instead of adding pg_stat_ calls to send
the statistics from each background process/worker.

Your patch needs to be rebased and the OID assignments updated.

--
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

#19Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Peter Eisentraut (#18)
2 attachment(s)
Re: pg_stat_wal_write statistics view

On Tue, Aug 15, 2017 at 7:39 AM, Peter Eisentraut <
peter.eisentraut@2ndquadrant.com> wrote:

On 3/29/17 22:10, Haribabu Kommi wrote:

Updated patch to use shared counter instead of adding pg_stat_ calls to

send

the statistics from each background process/worker.

Your patch needs to be rebased and the OID assignments updated.

Rebased patch is attached. And also it has taken care of the comments
provided
by Andres and Fuji Masao in the upthread.

I separated the walwriter statistics from the other background processes to
check
how much load does the other processes are really contributing to wal
writing.

Following is the test results during my performance test.

writes | walwriter_writes | backend_writes | dirty_writes |
walwriter_dirty_writes | backend_dirty_writes | write_blocks |
walwriter_write_blocks | backend_write_blocks | write_time |
walwriter_write_time | backend_write_time | sync_time | walwriter_sync_time
| backend_sync_time | stats_reset
--------+------------------+----------------+--------------+------------------------+----------------------+--------------+------------------------+----------------------+------------+----------------------+--------------------+-----------+---------------------+-------------------+-------------------------------
0 | 17748394 | 1383789657 | 0 |
0 | 0 | 0 | 21153194 |
3039806652 | 0 | 0 | 0
| 0 | 259250230 | 17262560725 | 2017-09-05
18:22:41.087955+10
(1 row)

I didn't find any other background processes contribution to the WAL
writing activity.
May be we can combine them with backend stats?

I ran the performance test on this patch with pgbench to find out the
impact of these
changes. Because of my low end machine, the performance results are varying
too
much, I will try to get the performance figures from an high end machine by
that time.

Attached the latest patch and performance report.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

0001-pg_stat_walwrites-statistics-view_v6.patchapplication/octet-stream; name=0001-pg_stat_walwrites-statistics-view_v6.patchDownload
From bf121d038d97bcd53344f5757f533aa3ad716e73 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Tue, 5 Sep 2017 11:17:47 +1000
Subject: [PATCH] pg_stat_walwrites statistics view

This view will provide the details of how many blocks,
writes and the time taken to write the WAL data to disk
by the backend, walwriter and other background process.
Based on this data, it is configure walwriter to reduce
the load on the backend processes, thus it should improve
the performance.

The time fields data gets populated only when the
track_io_timing GUC is enabled. This is to avoid adding
some overhead in finding out the time calculation that
is required to report in the view.
---
 doc/src/sgml/config.sgml               |   3 +-
 doc/src/sgml/monitoring.sgml           | 112 ++++++++++++++++++++++++++++
 src/backend/access/transam/xlog.c      | 131 +++++++++++++++++++++++++++++++++
 src/backend/catalog/system_views.sql   |   3 +
 src/backend/postmaster/autovacuum.c    |   1 -
 src/backend/postmaster/pgstat.c        | 130 +++++++++++++++++++++++++++++++-
 src/backend/postmaster/walwriter.c     |   2 +
 src/backend/utils/adt/pgstatfuncs.c    |  44 +++++++++++
 src/backend/utils/init/globals.c       |   1 +
 src/include/catalog/pg_proc.h          |   3 +
 src/include/miscadmin.h                |   2 +
 src/include/pgstat.h                   |  67 ++++++++++++++++-
 src/test/regress/expected/rules.out    |  17 +++++
 src/test/regress/expected/sysviews.out |   7 ++
 src/test/regress/sql/sysviews.sql      |   3 +
 15 files changed, 521 insertions(+), 5 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 5f59a38..85e545d 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5761,7 +5761,8 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
         platforms.  You can use the <xref linkend="pgtesttiming"> tool to
         measure the overhead of timing on your system.
         I/O timing information is
-        displayed in <xref linkend="pg-stat-database-view">, in the output of
+        displayed in <xref linkend="pg-stat-database-view">, 
+        <xref linkend="pg-stat-walwrites-view">, in the output of
         <xref linkend="sql-explain"> when the <literal>BUFFERS</> option is
         used, and by <xref linkend="pgstatstatements">.  Only superusers can
         change this setting.
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 38bf636..8d79a05 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -365,6 +365,14 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
      </row>
 
      <row>
+      <entry><structname>pg_stat_walwrites</><indexterm><primary>pg_stat_walwrites</primary></indexterm></entry>
+      <entry>One row only, showing statistics about the
+       WAL writing activity. See
+       <xref linkend="pg-stat-walwrites-view"> for details.
+     </entry>
+     </row>
+
+     <row>
       <entry><structname>pg_stat_database</><indexterm><primary>pg_stat_database</primary></indexterm></entry>
       <entry>One row per database, showing database-wide statistics. See
        <xref linkend="pg-stat-database-view"> for details.
@@ -2254,6 +2262,108 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
    single row, containing global data for the cluster.
   </para>
 
+  <table id="pg-stat-walwrites-view" xreflabel="pg_stat_walwrites">
+   <title><structname>pg_stat_walwrites</structname> View</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of WAL writes that are carried out by background processes and workers. 
+      </entry>
+     </row>
+    <row>
+      <entry><structfield>backend_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL writes that are carried out by the backend process</entry>
+     </row>
+     <row>
+     <entry><structfield>dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by background processes and workers
+      when the <xref linkend="guc-wal-buffers"> are full.
+      </entry>
+     </row>
+    <row>
+      <entry><structfield>backend_dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by the backend processes when 
+      the <xref linkend="guc-wal-buffers"> are full. 
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the background processes/workers</entry>
+     </row>
+     <row>
+      <entry><structfield>backend_write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the backend processes</entry>
+     </row>
+     <row>
+      <entry><structfield>write_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are written to disk by the background processes/workers, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>backend_write_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are written to disk by the backend processes, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>sync_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are synchronized to disk by the background processes/workers, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>backend_sync_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are synchronized to disk by the backend processes, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>stats_reset</></entry>
+      <entry><type>timestamp with time zone</type></entry>
+      <entry>Time at which these statistics were last reset</entry>
+     </row>
+     </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_walwrites</structname> view will always have a
+   single row, containing data about the WAL writing activity of the cluster.
+  </para>
+
+
   <table id="pg-stat-database-view" xreflabel="pg_stat_database">
    <title><structname>pg_stat_database</structname> View</title>
    <tgroup cols="3">
@@ -3033,6 +3143,8 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        counters shown in the <structname>pg_stat_bgwriter</> view.
        Calling <literal>pg_stat_reset_shared('archiver')</> will zero all the
        counters shown in the <structname>pg_stat_archiver</> view.
+       Calling <literal>pg_stat_reset_shared('walwrites')</> will zero all the
+       counters shown in the <structname>pg_stat_walwrites</> view.
       </entry>
      </row>
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index df4843f..b612c33 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -598,6 +598,9 @@ typedef struct XLogCtlData
 	 */
 	XLogwrtResult LogwrtResult;
 
+	/* Protected by WALWriteLock */
+	PgStat_WalWritesCounts stats;
+
 	/*
 	 * Latest initialized page in the cache (last byte position + 1).
 	 *
@@ -862,6 +865,7 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void);
 
 static void AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic);
 static bool XLogCheckpointNeeded(XLogSegNo new_segno);
+static bool am_background_process(void);
 static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible);
 static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath,
 					   bool find_free, XLogSegNo max_segno,
@@ -2112,6 +2116,12 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic)
 					WriteRqst.Write = OldPageRqstPtr;
 					WriteRqst.Flush = 0;
 					XLogWrite(WriteRqst, false);
+					if (am_background_process())
+						XLogCtl->stats.dirty_writes++;
+					else if (AmWalWriterProcess())
+						XLogCtl->stats.walwriter_dirty_writes++;
+					else
+						XLogCtl->stats.backend_dirty_writes++;
 					LWLockRelease(WALWriteLock);
 					TRACE_POSTGRESQL_WAL_BUFFER_WRITE_DIRTY_DONE();
 				}
@@ -2322,6 +2332,33 @@ XLogCheckpointNeeded(XLogSegNo new_segno)
 }
 
 /*
+ * Check whether the current process is a normal backend or not.
+ * This function checks for the background processes that does
+ * some WAL write activity only and other background processes
+ * are not considered. It considers all the background workers
+ * as WAL write activity workers.
+ *
+ * Returns false - when the current process is a normal backend
+ *		   true - when the current process a background process/worker
+ */
+static bool
+am_background_process()
+{
+	/* check whether current process is a background process/worker? */
+	if (!AmBackgroundWriterProcess() ||
+		!AmCheckpointerProcess() ||
+		!AmStartupProcess() ||
+		!IsBackgroundWorker ||
+		!am_walsender ||
+		!am_autovacuum_worker)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+/*
  * Write and/or fsync the log at least as far as WriteRqst indicates.
  *
  * If flexible == TRUE, we don't have to write as far as WriteRqst, but
@@ -2344,6 +2381,9 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 	int			npages;
 	int			startidx;
 	uint32		startoffset;
+	instr_time	io_start,
+				io_time;
+	bool		is_background_process = am_background_process();
 
 	/* We should always be inside a critical section here */
 	Assert(CritSectionCount > 0);
@@ -2461,6 +2501,11 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			/* OK to write the page(s) */
 			from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ;
 			nbytes = npages * (Size) XLOG_BLCKSZ;
+
+			/* Start timer to acquire start time of the wal write */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			nleft = nbytes;
 			do
 			{
@@ -2483,6 +2528,41 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				from += written;
 			} while (nleft > 0);
 
+			/* calculate the total time spent for wal writing */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				if (is_background_process)
+					XLogCtl->stats.total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+				else if (AmWalWriterProcess())
+					XLogCtl->stats.walwriter_total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+				else
+					XLogCtl->stats.backend_total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				XLogCtl->stats.total_write_time = 0;
+				XLogCtl->stats.walwriter_total_write_time = 0;
+				XLogCtl->stats.backend_total_write_time = 0;
+			}
+
+			/* check whether writer is a normal backend or not? */
+			if (is_background_process)
+				XLogCtl->stats.writes++;
+			else if (AmWalWriterProcess())
+				XLogCtl->stats.walwriter_writes++;
+			else
+				XLogCtl->stats.backend_writes++;
+
+			if (is_background_process)
+				XLogCtl->stats.write_blocks += npages;
+			else if (AmWalWriterProcess())
+				XLogCtl->stats.walwriter_write_blocks += npages;
+			else
+				XLogCtl->stats.backend_write_blocks += npages;
+
 			/* Update state for write */
 			openLogOff += nbytes;
 			npages = 0;
@@ -2502,8 +2582,32 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			 */
 			if (finishing_seg)
 			{
+				/* Start timer to acquire start time of the wal sync */
+				if (track_io_timing)
+					INSTR_TIME_SET_CURRENT(io_start);
+
 				issue_xlog_fsync(openLogFile, openLogSegNo);
 
+				/* calculate the total time spent for wal sync */
+				if (track_io_timing)
+				{
+					INSTR_TIME_SET_CURRENT(io_time);
+					INSTR_TIME_SUBTRACT(io_time, io_start);
+
+					if (is_background_process)
+						XLogCtl->stats.total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+					else if (AmWalWriterProcess())
+						XLogCtl->stats.walwriter_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+					else
+						XLogCtl->stats.backend_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+				}
+				else
+				{
+					XLogCtl->stats.total_sync_time = 0;
+					XLogCtl->stats.walwriter_total_sync_time = 0;
+					XLogCtl->stats.backend_total_sync_time = 0;
+				}
+
 				/* signal that we need to wakeup walsenders later */
 				WalSndWakeupRequest();
 
@@ -2571,7 +2675,31 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				openLogOff = 0;
 			}
 
+			/* Start timer to acquire start time of the wal sync */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			issue_xlog_fsync(openLogFile, openLogSegNo);
+
+			/* calculate the total time spent for wal sync */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				if (is_background_process)
+					XLogCtl->stats.total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+				else if (AmWalWriterProcess())
+					XLogCtl->stats.walwriter_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+				else
+					XLogCtl->stats.backend_total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				XLogCtl->stats.total_sync_time = 0;
+				XLogCtl->stats.walwriter_total_sync_time = 0;
+				XLogCtl->stats.backend_total_sync_time = 0;
+			}
 		}
 
 		/* signal that we need to wakeup walsenders later */
@@ -3046,6 +3174,9 @@ XLogBackgroundFlush(void)
 	{
 		XLogWrite(WriteRqst, flexible);
 	}
+
+	/* Collect all the wal write shared counters into local, and report it to stats collector */
+	memcpy(&LocalWalWritesStats.stats, &XLogCtl->stats, sizeof(PgStat_WalWritesCounts));
 	LWLockRelease(WALWriteLock);
 
 	END_CRIT_SECTION();
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index dc40cde..08139a1 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -881,6 +881,9 @@ CREATE VIEW pg_stat_bgwriter AS
         pg_stat_get_buf_alloc() AS buffers_alloc,
         pg_stat_get_bgwriter_stat_reset_time() AS stats_reset;
 
+CREATE VIEW pg_stat_walwrites AS
+   SELECT * FROM pg_stat_get_walwrites() AS A;
+
 CREATE VIEW pg_stat_progress_vacuum AS
 	SELECT
 		S.pid AS pid, S.datid AS datid, D.datname AS datname,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 776b1c0..85e40c8 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -135,7 +135,6 @@ int			Log_autovacuum_min_duration = -1;
 
 /* Flags to tell if we are in an autovacuum process */
 static bool am_autovacuum_launcher = false;
-static bool am_autovacuum_worker = false;
 
 /* Flags set by signal handlers */
 static volatile sig_atomic_t got_SIGHUP = false;
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index accf302..95effbd 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -142,6 +142,15 @@ char	   *pgstat_stat_tmpname = NULL;
  */
 PgStat_MsgBgWriter BgWriterStats;
 
+/*
+ * WalWrites Local statistics counters.
+ * The statistics data gets populated in XLogWrite function.
+ * Stored directly in a stats message structure so it can be sent
+ * to stats collector process without needing to copy things around.
+ * We assume this inits to zeroes.
+ */
+PgStat_MsgWalWrites LocalWalWritesStats;
+
 /* ----------
  * Local data
  * ----------
@@ -256,6 +265,7 @@ static int	localNumBackends = 0;
  */
 static PgStat_ArchiverStats archiverStats;
 static PgStat_GlobalStats globalStats;
+static PgStat_WalWritesStats walwritesStats;
 
 /*
  * List of OIDs of databases we need to write out.  If an entry is InvalidOid,
@@ -336,6 +346,7 @@ static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
 static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
 static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
+static void pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len);
 
 /* ------------------------------------------------------------
  * Public functions called from postmaster follow
@@ -1336,11 +1347,13 @@ pgstat_reset_shared_counters(const char *target)
 		msg.m_resettarget = RESET_ARCHIVER;
 	else if (strcmp(target, "bgwriter") == 0)
 		msg.m_resettarget = RESET_BGWRITER;
+	else if (strcmp(target, "walwrites") == 0)
+		msg.m_resettarget = RESET_WALWRITES;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("unrecognized reset target: \"%s\"", target),
-				 errhint("Target must be \"archiver\" or \"bgwriter\".")));
+				 errhint("Target must be \"archiver\" or \"bgwriter\" or \"walwrites\".")));
 
 	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER);
 	pgstat_send(&msg, sizeof(msg));
@@ -2580,6 +2593,21 @@ pgstat_fetch_global(void)
 	return &globalStats;
 }
 
+/*
+ * ---------
+ * pgstat_fetch_stat_walwrites() -
+ *
+ *	Support function for the SQL-callable pgstat* functions. Returns
+ *	a pointer to the walwrites statistics struct.
+ * ---------
+ */
+PgStat_WalWritesStats *
+pgstat_fetch_stat_walwrites(void)
+{
+	backend_read_statsfile();
+
+	return &walwritesStats;
+}
 
 /* ------------------------------------------------------------
  * Functions for management of the shared-memory PgBackendStatus array
@@ -4176,6 +4204,39 @@ pgstat_send_bgwriter(void)
 	MemSet(&BgWriterStats, 0, sizeof(BgWriterStats));
 }
 
+/* ----------
+ * pgstat_send_walwrites() -
+ *
+ *		Send wal writes statistics to the collector
+ * ----------
+ */
+
+void
+pgstat_send_walwrites(void)
+{
+	/* We assume this initializes to zeroes */
+	static const PgStat_MsgWalWrites all_zeroes;
+
+	/*
+	 * This function can be called even if nothing at all has happened. In
+	 * this case, avoid sending a completely empty message to the stats
+	 * collector.
+	 */
+	if (memcmp(&LocalWalWritesStats, &all_zeroes, sizeof(PgStat_MsgWalWrites)) == 0)
+		return;
+
+	/*
+	 * Prepare and send the message
+	 */
+	pgstat_setheader(&LocalWalWritesStats.m_hdr, PGSTAT_MTYPE_WALWRITES);
+	pgstat_send(&LocalWalWritesStats, sizeof(LocalWalWritesStats));
+
+	/*
+	 * Clear out the statistics buffer, so it can be re-used.
+	 */
+	MemSet(&LocalWalWritesStats, 0, sizeof(LocalWalWritesStats));
+}
+
 
 /* ----------
  * PgstatCollectorMain() -
@@ -4392,6 +4453,10 @@ PgstatCollectorMain(int argc, char *argv[])
 					pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len);
 					break;
 
+				case PGSTAT_MTYPE_WALWRITES:
+					pgstat_recv_walwrites((PgStat_MsgWalWrites *) & msg, len);
+					break;
+
 				default:
 					break;
 			}
@@ -4658,6 +4723,12 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 	(void) rc;					/* we'll check for error with ferror */
 
 	/*
+	 * Write wal writes stats struct
+	 */
+	rc = fwrite(&walwritesStats, sizeof(walwritesStats), 1, fpout);
+	(void) rc;					/* we'll check for error with ferror */
+
+	/*
 	 * Walk through the database table.
 	 */
 	hash_seq_init(&hstat, pgStatDBHash);
@@ -4914,6 +4985,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	memset(&globalStats, 0, sizeof(globalStats));
 	memset(&archiverStats, 0, sizeof(archiverStats));
+	memset(&walwritesStats, 0, sizeof(walwritesStats));
 
 	/*
 	 * Set the current timestamp (will be kept only in case we can't load an
@@ -4921,6 +4993,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	globalStats.stat_reset_timestamp = GetCurrentTimestamp();
 	archiverStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
+	walwritesStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
 
 	/*
 	 * Try to open the stats file. If it doesn't exist, the backends simply
@@ -4985,6 +5058,16 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	}
 
 	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&walwritesStats, 1, sizeof(walwritesStats), fpin) != sizeof(walwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		goto done;
+	}
+
+	/*
 	 * We found an existing collector stats file. Read it and put all the
 	 * hashtable entries into place.
 	 */
@@ -5282,6 +5365,7 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 	PgStat_StatDBEntry dbentry;
 	PgStat_GlobalStats myGlobalStats;
 	PgStat_ArchiverStats myArchiverStats;
+	PgStat_WalWritesStats myWalwritesStats;
 	FILE	   *fpin;
 	int32		format_id;
 	const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
@@ -5336,6 +5420,18 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 		return false;
 	}
 
+	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&myWalwritesStats, 1, sizeof(myWalwritesStats),
+			  fpin) != sizeof(myWalwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		FreeFile(fpin);
+		return false;
+	}
+
 	/* By default, we're going to return the timestamp of the global file. */
 	*ts = myGlobalStats.stats_timestamp;
 
@@ -5898,6 +5994,12 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
 		memset(&archiverStats, 0, sizeof(archiverStats));
 		archiverStats.stat_reset_timestamp = GetCurrentTimestamp();
 	}
+	else if (msg->m_resettarget == RESET_WALWRITES)
+	{
+		/* Reset the wal writes statistics of the cluster. */
+		memset(&walwritesStats, 0, sizeof(walwritesStats));
+		walwritesStats.stat_reset_timestamp = GetCurrentTimestamp();
+	}
 
 	/*
 	 * Presumably the sender of this message validated the target, don't
@@ -6078,6 +6180,32 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
 }
 
 /* ----------
+ * pgstat_recv_walwrites() -
+ *
+ *	Process a WALWRITES message.
+ * ----------
+ */
+static void
+pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len)
+{
+	walwritesStats.stats.writes += msg->stats.writes;
+	walwritesStats.stats.walwriter_writes += msg->stats.walwriter_writes;
+	walwritesStats.stats.backend_writes += msg->stats.backend_writes;
+	walwritesStats.stats.dirty_writes += msg->stats.dirty_writes;
+	walwritesStats.stats.walwriter_dirty_writes += msg->stats.walwriter_dirty_writes;
+	walwritesStats.stats.backend_dirty_writes += msg->stats.backend_dirty_writes;
+	walwritesStats.stats.write_blocks += msg->stats.write_blocks;
+	walwritesStats.stats.walwriter_write_blocks += msg->stats.walwriter_write_blocks;
+	walwritesStats.stats.backend_write_blocks += msg->stats.backend_write_blocks;
+	walwritesStats.stats.total_write_time += msg->stats.total_write_time;
+	walwritesStats.stats.walwriter_total_write_time += msg->stats.walwriter_total_write_time;
+	walwritesStats.stats.backend_total_write_time += msg->stats.backend_total_write_time;
+	walwritesStats.stats.total_sync_time += msg->stats.total_sync_time;
+	walwritesStats.stats.walwriter_total_sync_time += msg->stats.walwriter_total_sync_time;
+	walwritesStats.stats.backend_total_sync_time += msg->stats.backend_total_sync_time;
+}
+
+/* ----------
  * pgstat_recv_recoveryconflict() -
  *
  *	Process a RECOVERYCONFLICT message.
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 7b89e02..4f4dd84 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -280,6 +280,8 @@ WalWriterMain(void)
 		else if (left_till_hibernate > 0)
 			left_till_hibernate--;
 
+		pgstat_send_walwrites();
+
 		/*
 		 * Sleep until we are signaled or WalWriterDelay has elapsed.  If we
 		 * haven't done anything useful for quite some time, lengthen the
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 20ce48b..f71e10d 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1861,3 +1861,47 @@ pg_stat_get_archiver(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(HeapTupleGetDatum(
 									  heap_form_tuple(tupdesc, values, nulls)));
 }
+
+#define NUM_PG_STAT_WALWRITE_ATTS 16
+
+Datum
+pg_stat_get_walwrites(PG_FUNCTION_ARGS)
+{
+	TupleDesc	tupdesc;
+	Datum		values[NUM_PG_STAT_WALWRITE_ATTS];
+	bool		nulls[NUM_PG_STAT_WALWRITE_ATTS];
+	PgStat_WalWritesStats *walwrite_stats;
+
+	/* Initialize values and NULL flags arrays */
+	MemSet(values, 0, sizeof(values));
+	MemSet(nulls, 0, sizeof(nulls));
+
+	/* 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");
+
+	/* Get statistics about the archiver process */
+	walwrite_stats = pgstat_fetch_stat_walwrites();
+
+	/* Fill values and NULLs */
+	values[0] = Int64GetDatum(walwrite_stats->stats.writes);
+	values[1] = Int64GetDatum(walwrite_stats->stats.walwriter_writes);
+	values[2] = Int64GetDatum(walwrite_stats->stats.backend_writes);
+	values[3] = Int64GetDatum(walwrite_stats->stats.dirty_writes);
+	values[4] = Int64GetDatum(walwrite_stats->stats.walwriter_dirty_writes);
+	values[5] = Int64GetDatum(walwrite_stats->stats.backend_dirty_writes);
+	values[6] = Int64GetDatum(walwrite_stats->stats.write_blocks);
+	values[7] = Int64GetDatum(walwrite_stats->stats.walwriter_write_blocks);
+	values[8] = Int64GetDatum(walwrite_stats->stats.backend_write_blocks);
+	values[9] = Float8GetDatum(walwrite_stats->stats.total_write_time);
+	values[10] = Float8GetDatum(walwrite_stats->stats.walwriter_total_write_time);
+	values[11] = Float8GetDatum(walwrite_stats->stats.backend_total_write_time);
+	values[12] = Float8GetDatum(walwrite_stats->stats.total_sync_time);
+	values[13] = Float8GetDatum(walwrite_stats->stats.walwriter_total_sync_time);
+	values[14] = Float8GetDatum(walwrite_stats->stats.backend_total_sync_time);
+	values[15] = TimestampTzGetDatum(walwrite_stats->stat_reset_timestamp);
+
+	/* Returns the record as Datum */
+	PG_RETURN_DATUM(HeapTupleGetDatum(
+								   heap_form_tuple(tupdesc, values, nulls)));
+}
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 7c09498..3cb2b54 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -101,6 +101,7 @@ bool		IsPostmasterEnvironment = false;
 bool		IsUnderPostmaster = false;
 bool		IsBinaryUpgrade = false;
 bool		IsBackgroundWorker = false;
+bool            am_autovacuum_worker = false;
 
 bool		ExitOnAnyError = false;
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d820b56..e1a61bc 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2973,6 +2973,9 @@ DESCR("statistics: number of backend buffer writes that did their own fsync");
 DATA(insert OID = 2859 ( pg_stat_get_buf_alloc			PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_buf_alloc _null_ _null_ _null_ ));
 DESCR("statistics: number of buffer allocations");
 
+DATA(insert OID = 3998 (  pg_stat_get_walwrites		PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{20,20,20,20,20,20,20,20,20,701,701,701,701,701,701,1184}" "{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{writes,walwriter_writes,backend_writes,dirty_writes,walwriter_dirty_writes,backend_dirty_writes,write_blocks,walwriter_write_blocks,backend_write_blocks,write_time,walwriter_write_time,backend_write_time,sync_time,walwriter_sync_time,backend_sync_time,stats_reset}" _null_ _null_ pg_stat_get_walwrites _null_ _null_ _null_ ));
+DESCR("statistics: information about WAL writes activity");
+
 DATA(insert OID = 2978 (  pg_stat_get_function_calls		PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_calls _null_ _null_ _null_ ));
 DESCR("statistics: number of function calls");
 DATA(insert OID = 2979 (  pg_stat_get_function_total_time	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 701 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_total_time _null_ _null_ _null_ ));
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index dad98de..201c8e6 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -243,6 +243,8 @@ extern PGDLLIMPORT int work_mem;
 extern PGDLLIMPORT int maintenance_work_mem;
 extern PGDLLIMPORT int replacement_sort_tuples;
 
+extern bool	am_autovacuum_worker;
+
 extern int	VacuumCostPageHit;
 extern int	VacuumCostPageMiss;
 extern int	VacuumCostPageDirty;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 57ac5d4..b4d1fca 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -64,7 +64,8 @@ typedef enum StatMsgType
 	PGSTAT_MTYPE_FUNCPURGE,
 	PGSTAT_MTYPE_RECOVERYCONFLICT,
 	PGSTAT_MTYPE_TEMPFILE,
-	PGSTAT_MTYPE_DEADLOCK
+	PGSTAT_MTYPE_DEADLOCK,
+	PGSTAT_MTYPE_WALWRITES
 } StatMsgType;
 
 /* ----------
@@ -119,7 +120,8 @@ typedef struct PgStat_TableCounts
 typedef enum PgStat_Shared_Reset_Target
 {
 	RESET_ARCHIVER,
-	RESET_BGWRITER
+	RESET_BGWRITER,
+	RESET_WALWRITES
 } PgStat_Shared_Reset_Target;
 
 /* Possible object types for resetting single counters */
@@ -422,6 +424,50 @@ typedef struct PgStat_MsgBgWriter
 	PgStat_Counter m_checkpoint_sync_time;
 } PgStat_MsgBgWriter;
 
+/*
+ * Walwrites statistics counters
+ */
+typedef struct PgStat_WalWritesCounts
+{
+	PgStat_Counter writes;		/* No of writes by background
+								 * processes/workers */
+	PgStat_Counter walwriter_writes;		/* No of writes by walwriter */
+	PgStat_Counter backend_writes;		/* No of writes by backend */
+	PgStat_Counter dirty_writes;/* No of dirty writes by background
+								 * processes/workers when WAL buffers full */
+	PgStat_Counter walwriter_dirty_writes;/* No of dirty writes by walwriter
+	 	 	 	 	 	 	 	 	 	   * when WAL buffers full */
+	PgStat_Counter backend_dirty_writes;		/* No of dirty writes by
+												 * backend when WAL buffers
+												 * full */
+	PgStat_Counter write_blocks;/* Total no of pages written by background
+								 * processes/workers */
+	PgStat_Counter walwriter_write_blocks;/* Total no of pages written by walwriter */
+	PgStat_Counter backend_write_blocks;	/* Total no of pages written by backend */
+	PgStat_Counter total_write_time;	/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter walwriter_total_write_time;	/* Total write time in milliseconds by
+										 	 	 * walwriter */
+	PgStat_Counter backend_total_write_time;	/* Total write time in
+												 * milliseconds by backend */
+	PgStat_Counter total_sync_time;		/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter walwriter_total_sync_time;		/* Total write time in milliseconds by
+										 	 	 	 * walwriter */
+	PgStat_Counter backend_total_sync_time;		/* Total write time in
+												 * milliseconds by backend */
+} PgStat_WalWritesCounts;
+
+/* ----------
+ * PgStat_MsgWalWrites			Sent by the walwriter after collecting all shared stats
+ * ----------
+ */
+typedef struct PgStat_MsgWalWrites
+{
+	PgStat_MsgHdr m_hdr;
+	PgStat_WalWritesCounts stats;
+} PgStat_MsgWalWrites;
+
 /* ----------
  * PgStat_MsgRecoveryConflict	Sent by the backend upon recovery conflict
  * ----------
@@ -555,6 +601,7 @@ typedef union PgStat_Msg
 	PgStat_MsgFuncpurge msg_funcpurge;
 	PgStat_MsgRecoveryConflict msg_recoveryconflict;
 	PgStat_MsgDeadlock msg_deadlock;
+	PgStat_MsgWalWrites msg_walwrites;
 } PgStat_Msg;
 
 
@@ -694,6 +741,14 @@ typedef struct PgStat_GlobalStats
 	TimestampTz stat_reset_timestamp;
 } PgStat_GlobalStats;
 
+/*
+ * Walwrites statistics kept in the stats collector
+ */
+typedef struct PgStat_WalWritesStats
+{
+	PgStat_WalWritesCounts stats;
+	TimestampTz stat_reset_timestamp;	/* Last time when the stats reset */
+} PgStat_WalWritesStats;
 
 /* ----------
  * Backend types
@@ -1120,6 +1175,11 @@ extern char *pgstat_stat_filename;
 extern PgStat_MsgBgWriter BgWriterStats;
 
 /*
+ * Wal writes statistics updated in XLogWrite function
+ */
+extern PgStat_MsgWalWrites LocalWalWritesStats;
+
+/*
  * Updated by pgstat_count_buffer_*_time macros
  */
 extern PgStat_Counter pgStatBlockReadTime;
@@ -1313,6 +1373,7 @@ extern void pgstat_twophase_postabort(TransactionId xid, uint16 info,
 
 extern void pgstat_send_archiver(const char *xlog, bool failed);
 extern void pgstat_send_bgwriter(void);
+extern void pgstat_send_walwrites(void);
 
 /* ----------
  * Support functions for the SQL-callable functions to
@@ -1327,5 +1388,7 @@ extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
 extern int	pgstat_fetch_stat_numbackends(void);
 extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
 extern PgStat_GlobalStats *pgstat_fetch_global(void);
+extern PgStat_WalWritesStats *pgstat_fetch_stat_walwrites(void);
+
 
 #endif							/* PGSTAT_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index d582bc9..996de8a 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1973,6 +1973,23 @@ pg_stat_wal_receiver| SELECT s.pid,
     s.conninfo
    FROM pg_stat_get_wal_receiver() s(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, conninfo)
   WHERE (s.pid IS NOT NULL);
+pg_stat_walwrites| SELECT a.writes,
+    a.walwriter_writes,
+    a.backend_writes,
+    a.dirty_writes,
+    a.walwriter_dirty_writes,
+    a.backend_dirty_writes,
+    a.write_blocks,
+    a.walwriter_write_blocks,
+    a.backend_write_blocks,
+    a.write_time,
+    a.walwriter_write_time,
+    a.backend_write_time,
+    a.sync_time,
+    a.walwriter_sync_time,
+    a.backend_sync_time,
+    a.stats_reset
+   FROM pg_stat_get_walwrites() a(writes, walwriter_writes, backend_writes, dirty_writes, walwriter_dirty_writes, backend_dirty_writes, write_blocks, walwriter_write_blocks, backend_write_blocks, write_time, walwriter_write_time, backend_write_time, sync_time, walwriter_sync_time, backend_sync_time, stats_reset);
 pg_stat_xact_all_tables| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 568b783..8448cac 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -67,6 +67,13 @@ select count(*) >= 0 as ok from pg_prepared_xacts;
  t
 (1 row)
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+ ok 
+----
+ t
+(1 row)
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 28e412b..3769880 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -32,6 +32,9 @@ select count(*) = 0 as ok from pg_prepared_statements;
 -- See also prepared_xacts.sql
 select count(*) >= 0 as ok from pg_prepared_xacts;
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
-- 
2.7.4.windows.1

pg_stat_walwrites_perf.xlsxapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet; name=pg_stat_walwrites_perf.xlsxDownload
#20Kuntal Ghosh
kuntalghosh.2007@gmail.com
In reply to: Haribabu Kommi (#19)
Re: pg_stat_wal_write statistics view

On Wed, Sep 6, 2017 at 9:16 AM, Haribabu Kommi <kommi.haribabu@gmail.com> wrote:

Attached the latest patch and performance report.

While looking into the patch, I realized that a normal backend has to
check almost 10 if conditions at worst case inside XLogWrite(6 in
am_background_process method, 1 for write, 1 for blocks, 2 for
fsyncs), just to decide whether it is a normal backend or not. The
count is more for walwriter process. Although I've not done any
performance tests, IMHO, it might add further overhead on the
XLogWrite Lock.

I was thinking whether it is possible to collect all the stats in
XLogWrite() irrespective of the type of backend and update the shared
counters at once at the end of the function. Thoughts?

--
Thanks & Regards,
Kuntal Ghosh
EnterpriseDB: http://www.enterprisedb.com

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

#21Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Kuntal Ghosh (#20)
1 attachment(s)
Re: pg_stat_wal_write statistics view

On Tue, Sep 12, 2017 at 2:04 AM, Kuntal Ghosh <kuntalghosh.2007@gmail.com>
wrote:

On Wed, Sep 6, 2017 at 9:16 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

Attached the latest patch and performance report.

While looking into the patch, I realized that a normal backend has to
check almost 10 if conditions at worst case inside XLogWrite(6 in
am_background_process method, 1 for write, 1 for blocks, 2 for
fsyncs), just to decide whether it is a normal backend or not. The
count is more for walwriter process. Although I've not done any
performance tests, IMHO, it might add further overhead on the
XLogWrite Lock.

I was thinking whether it is possible to collect all the stats in
XLogWrite() irrespective of the type of backend and update the shared
counters at once at the end of the function. Thoughts?

Thanks for the review.
Yes, I agree with you that the stats update can be done at the end
of the function to avoid some overhead. Updated patch is attached.

There is an overhead with IO time calculation. Is the view is good
enough without IO columns?

And also during my tests, I didn't observe any other background
processes performing the xlogwrite operation, the values are always
zero. Is it fine to merge them with backend columns?

Regards,
Hari Babu
Fujitsu Australia

Attachments:

0001-pg_stat_walwrites-statistics-view_v7.patchapplication/octet-stream; name=0001-pg_stat_walwrites-statistics-view_v7.patchDownload
From bbd7c4bf2f7327c23ce67b385cd833dba68586a2 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Tue, 5 Sep 2017 11:17:47 +1000
Subject: [PATCH] pg_stat_walwrites statistics view

This view will provide the details of how many blocks,
writes and the time taken to write the WAL data to disk
by the backend, walwriter and other background process.
Based on this data, it is configure walwriter to reduce
the load on the backend processes, thus it should improve
the performance.

The time fields data gets populated only when the
track_io_timing GUC is enabled. This is to avoid adding
some overhead in finding out the time calculation that
is required to report in the view.
---
 doc/src/sgml/config.sgml               |   3 +-
 doc/src/sgml/monitoring.sgml           | 112 ++++++++++++++++++++++++++++
 src/backend/access/transam/xlog.c      | 126 ++++++++++++++++++++++++++++++++
 src/backend/catalog/system_views.sql   |   3 +
 src/backend/postmaster/autovacuum.c    |   1 -
 src/backend/postmaster/pgstat.c        | 130 ++++++++++++++++++++++++++++++++-
 src/backend/postmaster/walwriter.c     |   2 +
 src/backend/utils/adt/pgstatfuncs.c    |  44 +++++++++++
 src/backend/utils/init/globals.c       |   1 +
 src/include/catalog/pg_proc.h          |   3 +
 src/include/miscadmin.h                |   2 +
 src/include/pgstat.h                   |  67 ++++++++++++++++-
 src/test/regress/expected/rules.out    |  17 +++++
 src/test/regress/expected/sysviews.out |   7 ++
 src/test/regress/sql/sysviews.sql      |   3 +
 15 files changed, 516 insertions(+), 5 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 5f59a38..85e545d 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5761,7 +5761,8 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
         platforms.  You can use the <xref linkend="pgtesttiming"> tool to
         measure the overhead of timing on your system.
         I/O timing information is
-        displayed in <xref linkend="pg-stat-database-view">, in the output of
+        displayed in <xref linkend="pg-stat-database-view">, 
+        <xref linkend="pg-stat-walwrites-view">, in the output of
         <xref linkend="sql-explain"> when the <literal>BUFFERS</> option is
         used, and by <xref linkend="pgstatstatements">.  Only superusers can
         change this setting.
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 38bf636..8d79a05 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -365,6 +365,14 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
      </row>
 
      <row>
+      <entry><structname>pg_stat_walwrites</><indexterm><primary>pg_stat_walwrites</primary></indexterm></entry>
+      <entry>One row only, showing statistics about the
+       WAL writing activity. See
+       <xref linkend="pg-stat-walwrites-view"> for details.
+     </entry>
+     </row>
+
+     <row>
       <entry><structname>pg_stat_database</><indexterm><primary>pg_stat_database</primary></indexterm></entry>
       <entry>One row per database, showing database-wide statistics. See
        <xref linkend="pg-stat-database-view"> for details.
@@ -2254,6 +2262,108 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
    single row, containing global data for the cluster.
   </para>
 
+  <table id="pg-stat-walwrites-view" xreflabel="pg_stat_walwrites">
+   <title><structname>pg_stat_walwrites</structname> View</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of WAL writes that are carried out by background processes and workers. 
+      </entry>
+     </row>
+    <row>
+      <entry><structfield>backend_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL writes that are carried out by the backend process</entry>
+     </row>
+     <row>
+     <entry><structfield>dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by background processes and workers
+      when the <xref linkend="guc-wal-buffers"> are full.
+      </entry>
+     </row>
+    <row>
+      <entry><structfield>backend_dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by the backend processes when 
+      the <xref linkend="guc-wal-buffers"> are full. 
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the background processes/workers</entry>
+     </row>
+     <row>
+      <entry><structfield>backend_write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the backend processes</entry>
+     </row>
+     <row>
+      <entry><structfield>write_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are written to disk by the background processes/workers, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>backend_write_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are written to disk by the backend processes, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>sync_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are synchronized to disk by the background processes/workers, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>backend_sync_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are synchronized to disk by the backend processes, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>stats_reset</></entry>
+      <entry><type>timestamp with time zone</type></entry>
+      <entry>Time at which these statistics were last reset</entry>
+     </row>
+     </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_walwrites</structname> view will always have a
+   single row, containing data about the WAL writing activity of the cluster.
+  </para>
+
+
   <table id="pg-stat-database-view" xreflabel="pg_stat_database">
    <title><structname>pg_stat_database</structname> View</title>
    <tgroup cols="3">
@@ -3033,6 +3143,8 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        counters shown in the <structname>pg_stat_bgwriter</> view.
        Calling <literal>pg_stat_reset_shared('archiver')</> will zero all the
        counters shown in the <structname>pg_stat_archiver</> view.
+       Calling <literal>pg_stat_reset_shared('walwrites')</> will zero all the
+       counters shown in the <structname>pg_stat_walwrites</> view.
       </entry>
      </row>
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index a3e8ce0..00814fe 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -598,6 +598,9 @@ typedef struct XLogCtlData
 	 */
 	XLogwrtResult LogwrtResult;
 
+	/* Protected by WALWriteLock */
+	PgStat_WalWritesCounts stats;
+
 	/*
 	 * Latest initialized page in the cache (last byte position + 1).
 	 *
@@ -862,6 +865,7 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void);
 
 static void AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic);
 static bool XLogCheckpointNeeded(XLogSegNo new_segno);
+static bool am_background_process(void);
 static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible);
 static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath,
 					   bool find_free, XLogSegNo max_segno,
@@ -2112,6 +2116,12 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic)
 					WriteRqst.Write = OldPageRqstPtr;
 					WriteRqst.Flush = 0;
 					XLogWrite(WriteRqst, false);
+					if (am_background_process())
+						XLogCtl->stats.dirty_writes++;
+					else if (AmWalWriterProcess())
+						XLogCtl->stats.walwriter_dirty_writes++;
+					else
+						XLogCtl->stats.backend_dirty_writes++;
 					LWLockRelease(WALWriteLock);
 					TRACE_POSTGRESQL_WAL_BUFFER_WRITE_DIRTY_DONE();
 				}
@@ -2322,6 +2332,33 @@ XLogCheckpointNeeded(XLogSegNo new_segno)
 }
 
 /*
+ * Check whether the current process is a normal backend or not.
+ * This function checks for the background processes that does
+ * some WAL write activity only and other background processes
+ * are not considered. It considers all the background workers
+ * as WAL write activity workers.
+ *
+ * Returns false - when the current process is a normal backend
+ *		   true - when the current process a background process/worker
+ */
+static bool
+am_background_process()
+{
+	/* check whether current process is a background process/worker? */
+	if (!AmBackgroundWriterProcess() ||
+		!AmCheckpointerProcess() ||
+		!AmStartupProcess() ||
+		!IsBackgroundWorker ||
+		!am_walsender ||
+		!am_autovacuum_worker)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+/*
  * Write and/or fsync the log at least as far as WriteRqst indicates.
  *
  * If flexible == TRUE, we don't have to write as far as WriteRqst, but
@@ -2344,6 +2381,13 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 	int			npages;
 	int			startidx;
 	uint32		startoffset;
+	instr_time	io_start,
+				io_time;
+	PgStat_Counter writes = 0;
+	PgStat_Counter write_blocks = 0;
+	PgStat_Counter total_write_time = 0;
+	PgStat_Counter total_sync_time = 0;
+	bool		is_background_process = am_background_process();
 
 	/* We should always be inside a critical section here */
 	Assert(CritSectionCount > 0);
@@ -2461,6 +2505,11 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			/* OK to write the page(s) */
 			from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ;
 			nbytes = npages * (Size) XLOG_BLCKSZ;
+
+			/* Start timer to acquire start time of the wal write */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			nleft = nbytes;
 			do
 			{
@@ -2483,6 +2532,24 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				from += written;
 			} while (nleft > 0);
 
+			/* calculate the total time spent for wal writing */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				total_write_time = 0;
+			}
+
+			/* check whether writer is a normal backend or not? */
+			writes++;
+			write_blocks += npages;
+
+
 			/* Update state for write */
 			openLogOff += nbytes;
 			npages = 0;
@@ -2502,8 +2569,25 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			 */
 			if (finishing_seg)
 			{
+				/* Start timer to acquire start time of the wal sync */
+				if (track_io_timing)
+					INSTR_TIME_SET_CURRENT(io_start);
+
 				issue_xlog_fsync(openLogFile, openLogSegNo);
 
+				/* calculate the total time spent for wal sync */
+				if (track_io_timing)
+				{
+					INSTR_TIME_SET_CURRENT(io_time);
+					INSTR_TIME_SUBTRACT(io_time, io_start);
+
+					total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+				}
+				else
+				{
+					total_sync_time = 0;
+				}
+
 				/* signal that we need to wakeup walsenders later */
 				WalSndWakeupRequest();
 
@@ -2571,7 +2655,24 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				openLogOff = 0;
 			}
 
+			/* Start timer to acquire start time of the wal sync */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			issue_xlog_fsync(openLogFile, openLogSegNo);
+
+			/* calculate the total time spent for wal sync */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				total_sync_time = 0;
+			}
 		}
 
 		/* signal that we need to wakeup walsenders later */
@@ -2580,6 +2681,28 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 		LogwrtResult.Flush = LogwrtResult.Write;
 	}
 
+	if (is_background_process)
+	{
+		XLogCtl->stats.writes += writes;
+		XLogCtl->stats.write_blocks += write_blocks;
+		XLogCtl->stats.total_write_time += total_write_time;
+		XLogCtl->stats.total_sync_time += total_sync_time;
+	}
+	else if (AmWalWriterProcess())
+	{
+		XLogCtl->stats.walwriter_writes += writes;
+		XLogCtl->stats.walwriter_write_blocks += write_blocks;
+		XLogCtl->stats.walwriter_total_write_time += total_write_time;
+		XLogCtl->stats.walwriter_total_sync_time += total_sync_time;
+	}
+	else
+	{
+		XLogCtl->stats.backend_writes += writes;
+		XLogCtl->stats.backend_write_blocks += write_blocks;
+		XLogCtl->stats.backend_total_write_time += total_write_time;
+		XLogCtl->stats.backend_total_sync_time += total_sync_time;
+	}
+
 	/*
 	 * Update shared-memory status
 	 *
@@ -3046,6 +3169,9 @@ XLogBackgroundFlush(void)
 	{
 		XLogWrite(WriteRqst, flexible);
 	}
+
+	/* Collect all the wal write shared counters into local, and report it to stats collector */
+	memcpy(&LocalWalWritesStats.stats, &XLogCtl->stats, sizeof(PgStat_WalWritesCounts));
 	LWLockRelease(WALWriteLock);
 
 	END_CRIT_SECTION();
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index dc40cde..08139a1 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -881,6 +881,9 @@ CREATE VIEW pg_stat_bgwriter AS
         pg_stat_get_buf_alloc() AS buffers_alloc,
         pg_stat_get_bgwriter_stat_reset_time() AS stats_reset;
 
+CREATE VIEW pg_stat_walwrites AS
+   SELECT * FROM pg_stat_get_walwrites() AS A;
+
 CREATE VIEW pg_stat_progress_vacuum AS
 	SELECT
 		S.pid AS pid, S.datid AS datid, D.datname AS datname,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 776b1c0..85e40c8 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -135,7 +135,6 @@ int			Log_autovacuum_min_duration = -1;
 
 /* Flags to tell if we are in an autovacuum process */
 static bool am_autovacuum_launcher = false;
-static bool am_autovacuum_worker = false;
 
 /* Flags set by signal handlers */
 static volatile sig_atomic_t got_SIGHUP = false;
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index accf302..95effbd 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -142,6 +142,15 @@ char	   *pgstat_stat_tmpname = NULL;
  */
 PgStat_MsgBgWriter BgWriterStats;
 
+/*
+ * WalWrites Local statistics counters.
+ * The statistics data gets populated in XLogWrite function.
+ * Stored directly in a stats message structure so it can be sent
+ * to stats collector process without needing to copy things around.
+ * We assume this inits to zeroes.
+ */
+PgStat_MsgWalWrites LocalWalWritesStats;
+
 /* ----------
  * Local data
  * ----------
@@ -256,6 +265,7 @@ static int	localNumBackends = 0;
  */
 static PgStat_ArchiverStats archiverStats;
 static PgStat_GlobalStats globalStats;
+static PgStat_WalWritesStats walwritesStats;
 
 /*
  * List of OIDs of databases we need to write out.  If an entry is InvalidOid,
@@ -336,6 +346,7 @@ static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
 static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
 static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
+static void pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len);
 
 /* ------------------------------------------------------------
  * Public functions called from postmaster follow
@@ -1336,11 +1347,13 @@ pgstat_reset_shared_counters(const char *target)
 		msg.m_resettarget = RESET_ARCHIVER;
 	else if (strcmp(target, "bgwriter") == 0)
 		msg.m_resettarget = RESET_BGWRITER;
+	else if (strcmp(target, "walwrites") == 0)
+		msg.m_resettarget = RESET_WALWRITES;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("unrecognized reset target: \"%s\"", target),
-				 errhint("Target must be \"archiver\" or \"bgwriter\".")));
+				 errhint("Target must be \"archiver\" or \"bgwriter\" or \"walwrites\".")));
 
 	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER);
 	pgstat_send(&msg, sizeof(msg));
@@ -2580,6 +2593,21 @@ pgstat_fetch_global(void)
 	return &globalStats;
 }
 
+/*
+ * ---------
+ * pgstat_fetch_stat_walwrites() -
+ *
+ *	Support function for the SQL-callable pgstat* functions. Returns
+ *	a pointer to the walwrites statistics struct.
+ * ---------
+ */
+PgStat_WalWritesStats *
+pgstat_fetch_stat_walwrites(void)
+{
+	backend_read_statsfile();
+
+	return &walwritesStats;
+}
 
 /* ------------------------------------------------------------
  * Functions for management of the shared-memory PgBackendStatus array
@@ -4176,6 +4204,39 @@ pgstat_send_bgwriter(void)
 	MemSet(&BgWriterStats, 0, sizeof(BgWriterStats));
 }
 
+/* ----------
+ * pgstat_send_walwrites() -
+ *
+ *		Send wal writes statistics to the collector
+ * ----------
+ */
+
+void
+pgstat_send_walwrites(void)
+{
+	/* We assume this initializes to zeroes */
+	static const PgStat_MsgWalWrites all_zeroes;
+
+	/*
+	 * This function can be called even if nothing at all has happened. In
+	 * this case, avoid sending a completely empty message to the stats
+	 * collector.
+	 */
+	if (memcmp(&LocalWalWritesStats, &all_zeroes, sizeof(PgStat_MsgWalWrites)) == 0)
+		return;
+
+	/*
+	 * Prepare and send the message
+	 */
+	pgstat_setheader(&LocalWalWritesStats.m_hdr, PGSTAT_MTYPE_WALWRITES);
+	pgstat_send(&LocalWalWritesStats, sizeof(LocalWalWritesStats));
+
+	/*
+	 * Clear out the statistics buffer, so it can be re-used.
+	 */
+	MemSet(&LocalWalWritesStats, 0, sizeof(LocalWalWritesStats));
+}
+
 
 /* ----------
  * PgstatCollectorMain() -
@@ -4392,6 +4453,10 @@ PgstatCollectorMain(int argc, char *argv[])
 					pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len);
 					break;
 
+				case PGSTAT_MTYPE_WALWRITES:
+					pgstat_recv_walwrites((PgStat_MsgWalWrites *) & msg, len);
+					break;
+
 				default:
 					break;
 			}
@@ -4658,6 +4723,12 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 	(void) rc;					/* we'll check for error with ferror */
 
 	/*
+	 * Write wal writes stats struct
+	 */
+	rc = fwrite(&walwritesStats, sizeof(walwritesStats), 1, fpout);
+	(void) rc;					/* we'll check for error with ferror */
+
+	/*
 	 * Walk through the database table.
 	 */
 	hash_seq_init(&hstat, pgStatDBHash);
@@ -4914,6 +4985,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	memset(&globalStats, 0, sizeof(globalStats));
 	memset(&archiverStats, 0, sizeof(archiverStats));
+	memset(&walwritesStats, 0, sizeof(walwritesStats));
 
 	/*
 	 * Set the current timestamp (will be kept only in case we can't load an
@@ -4921,6 +4993,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	globalStats.stat_reset_timestamp = GetCurrentTimestamp();
 	archiverStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
+	walwritesStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
 
 	/*
 	 * Try to open the stats file. If it doesn't exist, the backends simply
@@ -4985,6 +5058,16 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	}
 
 	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&walwritesStats, 1, sizeof(walwritesStats), fpin) != sizeof(walwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		goto done;
+	}
+
+	/*
 	 * We found an existing collector stats file. Read it and put all the
 	 * hashtable entries into place.
 	 */
@@ -5282,6 +5365,7 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 	PgStat_StatDBEntry dbentry;
 	PgStat_GlobalStats myGlobalStats;
 	PgStat_ArchiverStats myArchiverStats;
+	PgStat_WalWritesStats myWalwritesStats;
 	FILE	   *fpin;
 	int32		format_id;
 	const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
@@ -5336,6 +5420,18 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 		return false;
 	}
 
+	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&myWalwritesStats, 1, sizeof(myWalwritesStats),
+			  fpin) != sizeof(myWalwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		FreeFile(fpin);
+		return false;
+	}
+
 	/* By default, we're going to return the timestamp of the global file. */
 	*ts = myGlobalStats.stats_timestamp;
 
@@ -5898,6 +5994,12 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
 		memset(&archiverStats, 0, sizeof(archiverStats));
 		archiverStats.stat_reset_timestamp = GetCurrentTimestamp();
 	}
+	else if (msg->m_resettarget == RESET_WALWRITES)
+	{
+		/* Reset the wal writes statistics of the cluster. */
+		memset(&walwritesStats, 0, sizeof(walwritesStats));
+		walwritesStats.stat_reset_timestamp = GetCurrentTimestamp();
+	}
 
 	/*
 	 * Presumably the sender of this message validated the target, don't
@@ -6078,6 +6180,32 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
 }
 
 /* ----------
+ * pgstat_recv_walwrites() -
+ *
+ *	Process a WALWRITES message.
+ * ----------
+ */
+static void
+pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len)
+{
+	walwritesStats.stats.writes += msg->stats.writes;
+	walwritesStats.stats.walwriter_writes += msg->stats.walwriter_writes;
+	walwritesStats.stats.backend_writes += msg->stats.backend_writes;
+	walwritesStats.stats.dirty_writes += msg->stats.dirty_writes;
+	walwritesStats.stats.walwriter_dirty_writes += msg->stats.walwriter_dirty_writes;
+	walwritesStats.stats.backend_dirty_writes += msg->stats.backend_dirty_writes;
+	walwritesStats.stats.write_blocks += msg->stats.write_blocks;
+	walwritesStats.stats.walwriter_write_blocks += msg->stats.walwriter_write_blocks;
+	walwritesStats.stats.backend_write_blocks += msg->stats.backend_write_blocks;
+	walwritesStats.stats.total_write_time += msg->stats.total_write_time;
+	walwritesStats.stats.walwriter_total_write_time += msg->stats.walwriter_total_write_time;
+	walwritesStats.stats.backend_total_write_time += msg->stats.backend_total_write_time;
+	walwritesStats.stats.total_sync_time += msg->stats.total_sync_time;
+	walwritesStats.stats.walwriter_total_sync_time += msg->stats.walwriter_total_sync_time;
+	walwritesStats.stats.backend_total_sync_time += msg->stats.backend_total_sync_time;
+}
+
+/* ----------
  * pgstat_recv_recoveryconflict() -
  *
  *	Process a RECOVERYCONFLICT message.
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 7b89e02..4f4dd84 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -280,6 +280,8 @@ WalWriterMain(void)
 		else if (left_till_hibernate > 0)
 			left_till_hibernate--;
 
+		pgstat_send_walwrites();
+
 		/*
 		 * Sleep until we are signaled or WalWriterDelay has elapsed.  If we
 		 * haven't done anything useful for quite some time, lengthen the
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 20ce48b..f71e10d 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1861,3 +1861,47 @@ pg_stat_get_archiver(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(HeapTupleGetDatum(
 									  heap_form_tuple(tupdesc, values, nulls)));
 }
+
+#define NUM_PG_STAT_WALWRITE_ATTS 16
+
+Datum
+pg_stat_get_walwrites(PG_FUNCTION_ARGS)
+{
+	TupleDesc	tupdesc;
+	Datum		values[NUM_PG_STAT_WALWRITE_ATTS];
+	bool		nulls[NUM_PG_STAT_WALWRITE_ATTS];
+	PgStat_WalWritesStats *walwrite_stats;
+
+	/* Initialize values and NULL flags arrays */
+	MemSet(values, 0, sizeof(values));
+	MemSet(nulls, 0, sizeof(nulls));
+
+	/* 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");
+
+	/* Get statistics about the archiver process */
+	walwrite_stats = pgstat_fetch_stat_walwrites();
+
+	/* Fill values and NULLs */
+	values[0] = Int64GetDatum(walwrite_stats->stats.writes);
+	values[1] = Int64GetDatum(walwrite_stats->stats.walwriter_writes);
+	values[2] = Int64GetDatum(walwrite_stats->stats.backend_writes);
+	values[3] = Int64GetDatum(walwrite_stats->stats.dirty_writes);
+	values[4] = Int64GetDatum(walwrite_stats->stats.walwriter_dirty_writes);
+	values[5] = Int64GetDatum(walwrite_stats->stats.backend_dirty_writes);
+	values[6] = Int64GetDatum(walwrite_stats->stats.write_blocks);
+	values[7] = Int64GetDatum(walwrite_stats->stats.walwriter_write_blocks);
+	values[8] = Int64GetDatum(walwrite_stats->stats.backend_write_blocks);
+	values[9] = Float8GetDatum(walwrite_stats->stats.total_write_time);
+	values[10] = Float8GetDatum(walwrite_stats->stats.walwriter_total_write_time);
+	values[11] = Float8GetDatum(walwrite_stats->stats.backend_total_write_time);
+	values[12] = Float8GetDatum(walwrite_stats->stats.total_sync_time);
+	values[13] = Float8GetDatum(walwrite_stats->stats.walwriter_total_sync_time);
+	values[14] = Float8GetDatum(walwrite_stats->stats.backend_total_sync_time);
+	values[15] = TimestampTzGetDatum(walwrite_stats->stat_reset_timestamp);
+
+	/* Returns the record as Datum */
+	PG_RETURN_DATUM(HeapTupleGetDatum(
+								   heap_form_tuple(tupdesc, values, nulls)));
+}
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 7c09498..3cb2b54 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -101,6 +101,7 @@ bool		IsPostmasterEnvironment = false;
 bool		IsUnderPostmaster = false;
 bool		IsBinaryUpgrade = false;
 bool		IsBackgroundWorker = false;
+bool            am_autovacuum_worker = false;
 
 bool		ExitOnAnyError = false;
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d820b56..e1a61bc 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2973,6 +2973,9 @@ DESCR("statistics: number of backend buffer writes that did their own fsync");
 DATA(insert OID = 2859 ( pg_stat_get_buf_alloc			PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_buf_alloc _null_ _null_ _null_ ));
 DESCR("statistics: number of buffer allocations");
 
+DATA(insert OID = 3998 (  pg_stat_get_walwrites		PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{20,20,20,20,20,20,20,20,20,701,701,701,701,701,701,1184}" "{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{writes,walwriter_writes,backend_writes,dirty_writes,walwriter_dirty_writes,backend_dirty_writes,write_blocks,walwriter_write_blocks,backend_write_blocks,write_time,walwriter_write_time,backend_write_time,sync_time,walwriter_sync_time,backend_sync_time,stats_reset}" _null_ _null_ pg_stat_get_walwrites _null_ _null_ _null_ ));
+DESCR("statistics: information about WAL writes activity");
+
 DATA(insert OID = 2978 (  pg_stat_get_function_calls		PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_calls _null_ _null_ _null_ ));
 DESCR("statistics: number of function calls");
 DATA(insert OID = 2979 (  pg_stat_get_function_total_time	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 701 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_total_time _null_ _null_ _null_ ));
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index dad98de..201c8e6 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -243,6 +243,8 @@ extern PGDLLIMPORT int work_mem;
 extern PGDLLIMPORT int maintenance_work_mem;
 extern PGDLLIMPORT int replacement_sort_tuples;
 
+extern bool	am_autovacuum_worker;
+
 extern int	VacuumCostPageHit;
 extern int	VacuumCostPageMiss;
 extern int	VacuumCostPageDirty;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 57ac5d4..b4d1fca 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -64,7 +64,8 @@ typedef enum StatMsgType
 	PGSTAT_MTYPE_FUNCPURGE,
 	PGSTAT_MTYPE_RECOVERYCONFLICT,
 	PGSTAT_MTYPE_TEMPFILE,
-	PGSTAT_MTYPE_DEADLOCK
+	PGSTAT_MTYPE_DEADLOCK,
+	PGSTAT_MTYPE_WALWRITES
 } StatMsgType;
 
 /* ----------
@@ -119,7 +120,8 @@ typedef struct PgStat_TableCounts
 typedef enum PgStat_Shared_Reset_Target
 {
 	RESET_ARCHIVER,
-	RESET_BGWRITER
+	RESET_BGWRITER,
+	RESET_WALWRITES
 } PgStat_Shared_Reset_Target;
 
 /* Possible object types for resetting single counters */
@@ -422,6 +424,50 @@ typedef struct PgStat_MsgBgWriter
 	PgStat_Counter m_checkpoint_sync_time;
 } PgStat_MsgBgWriter;
 
+/*
+ * Walwrites statistics counters
+ */
+typedef struct PgStat_WalWritesCounts
+{
+	PgStat_Counter writes;		/* No of writes by background
+								 * processes/workers */
+	PgStat_Counter walwriter_writes;		/* No of writes by walwriter */
+	PgStat_Counter backend_writes;		/* No of writes by backend */
+	PgStat_Counter dirty_writes;/* No of dirty writes by background
+								 * processes/workers when WAL buffers full */
+	PgStat_Counter walwriter_dirty_writes;/* No of dirty writes by walwriter
+	 	 	 	 	 	 	 	 	 	   * when WAL buffers full */
+	PgStat_Counter backend_dirty_writes;		/* No of dirty writes by
+												 * backend when WAL buffers
+												 * full */
+	PgStat_Counter write_blocks;/* Total no of pages written by background
+								 * processes/workers */
+	PgStat_Counter walwriter_write_blocks;/* Total no of pages written by walwriter */
+	PgStat_Counter backend_write_blocks;	/* Total no of pages written by backend */
+	PgStat_Counter total_write_time;	/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter walwriter_total_write_time;	/* Total write time in milliseconds by
+										 	 	 * walwriter */
+	PgStat_Counter backend_total_write_time;	/* Total write time in
+												 * milliseconds by backend */
+	PgStat_Counter total_sync_time;		/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter walwriter_total_sync_time;		/* Total write time in milliseconds by
+										 	 	 	 * walwriter */
+	PgStat_Counter backend_total_sync_time;		/* Total write time in
+												 * milliseconds by backend */
+} PgStat_WalWritesCounts;
+
+/* ----------
+ * PgStat_MsgWalWrites			Sent by the walwriter after collecting all shared stats
+ * ----------
+ */
+typedef struct PgStat_MsgWalWrites
+{
+	PgStat_MsgHdr m_hdr;
+	PgStat_WalWritesCounts stats;
+} PgStat_MsgWalWrites;
+
 /* ----------
  * PgStat_MsgRecoveryConflict	Sent by the backend upon recovery conflict
  * ----------
@@ -555,6 +601,7 @@ typedef union PgStat_Msg
 	PgStat_MsgFuncpurge msg_funcpurge;
 	PgStat_MsgRecoveryConflict msg_recoveryconflict;
 	PgStat_MsgDeadlock msg_deadlock;
+	PgStat_MsgWalWrites msg_walwrites;
 } PgStat_Msg;
 
 
@@ -694,6 +741,14 @@ typedef struct PgStat_GlobalStats
 	TimestampTz stat_reset_timestamp;
 } PgStat_GlobalStats;
 
+/*
+ * Walwrites statistics kept in the stats collector
+ */
+typedef struct PgStat_WalWritesStats
+{
+	PgStat_WalWritesCounts stats;
+	TimestampTz stat_reset_timestamp;	/* Last time when the stats reset */
+} PgStat_WalWritesStats;
 
 /* ----------
  * Backend types
@@ -1120,6 +1175,11 @@ extern char *pgstat_stat_filename;
 extern PgStat_MsgBgWriter BgWriterStats;
 
 /*
+ * Wal writes statistics updated in XLogWrite function
+ */
+extern PgStat_MsgWalWrites LocalWalWritesStats;
+
+/*
  * Updated by pgstat_count_buffer_*_time macros
  */
 extern PgStat_Counter pgStatBlockReadTime;
@@ -1313,6 +1373,7 @@ extern void pgstat_twophase_postabort(TransactionId xid, uint16 info,
 
 extern void pgstat_send_archiver(const char *xlog, bool failed);
 extern void pgstat_send_bgwriter(void);
+extern void pgstat_send_walwrites(void);
 
 /* ----------
  * Support functions for the SQL-callable functions to
@@ -1327,5 +1388,7 @@ extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
 extern int	pgstat_fetch_stat_numbackends(void);
 extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
 extern PgStat_GlobalStats *pgstat_fetch_global(void);
+extern PgStat_WalWritesStats *pgstat_fetch_stat_walwrites(void);
+
 
 #endif							/* PGSTAT_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f1c1b44..6a833de 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1973,6 +1973,23 @@ pg_stat_wal_receiver| SELECT s.pid,
     s.conninfo
    FROM pg_stat_get_wal_receiver() s(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, conninfo)
   WHERE (s.pid IS NOT NULL);
+pg_stat_walwrites| SELECT a.writes,
+    a.walwriter_writes,
+    a.backend_writes,
+    a.dirty_writes,
+    a.walwriter_dirty_writes,
+    a.backend_dirty_writes,
+    a.write_blocks,
+    a.walwriter_write_blocks,
+    a.backend_write_blocks,
+    a.write_time,
+    a.walwriter_write_time,
+    a.backend_write_time,
+    a.sync_time,
+    a.walwriter_sync_time,
+    a.backend_sync_time,
+    a.stats_reset
+   FROM pg_stat_get_walwrites() a(writes, walwriter_writes, backend_writes, dirty_writes, walwriter_dirty_writes, backend_dirty_writes, write_blocks, walwriter_write_blocks, backend_write_blocks, write_time, walwriter_write_time, backend_write_time, sync_time, walwriter_sync_time, backend_sync_time, stats_reset);
 pg_stat_xact_all_tables| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 568b783..8448cac 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -67,6 +67,13 @@ select count(*) >= 0 as ok from pg_prepared_xacts;
  t
 (1 row)
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+ ok 
+----
+ t
+(1 row)
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 28e412b..3769880 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -32,6 +32,9 @@ select count(*) = 0 as ok from pg_prepared_statements;
 -- See also prepared_xacts.sql
 select count(*) >= 0 as ok from pg_prepared_xacts;
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
-- 
2.7.4.windows.1

#22Kuntal Ghosh
kuntalghosh.2007@gmail.com
In reply to: Haribabu Kommi (#21)
Re: pg_stat_wal_write statistics view

On Tue, Sep 12, 2017 at 9:06 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

On Tue, Sep 12, 2017 at 2:04 AM, Kuntal Ghosh <kuntalghosh.2007@gmail.com>
wrote:

On Wed, Sep 6, 2017 at 9:16 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

Attached the latest patch and performance report.

While looking into the patch, I realized that a normal backend has to
check almost 10 if conditions at worst case inside XLogWrite(6 in
am_background_process method, 1 for write, 1 for blocks, 2 for
fsyncs), just to decide whether it is a normal backend or not. The
count is more for walwriter process. Although I've not done any
performance tests, IMHO, it might add further overhead on the
XLogWrite Lock.

I was thinking whether it is possible to collect all the stats in
XLogWrite() irrespective of the type of backend and update the shared
counters at once at the end of the function. Thoughts?

Thanks for the review.
Yes, I agree with you that the stats update can be done at the end
of the function to avoid some overhead. Updated patch is attached.

Thanks for the patch.
+ * Check whether the current process is a normal backend or not.
+ * This function checks for the background processes that does
+ * some WAL write activity only and other background processes
+ * are not considered. It considers all the background workers
+ * as WAL write activity workers.
+ *
+ * Returns false - when the current process is a normal backend
+ *        true - when the current process a background process/worker
+ */
+static bool
+am_background_process()
+{
+   /* check whether current process is a background process/worker? */
+   if (!AmBackgroundWriterProcess() ||
+       !AmCheckpointerProcess() ||
+       !AmStartupProcess() ||
+       !IsBackgroundWorker ||
+       !am_walsender ||
+       !am_autovacuum_worker)
+   {
+       return false;
+   }
+
+   return true;
+}
I think you've to do AND operation here instead of OR. Isn't it?
Another point is that, the function description could start with
'Check whether the current process is a background process/worker.'

There is an overhead with IO time calculation. Is the view is good
enough without IO columns?

I'm not sure how IO columns are useful for tuning the WAL write/fsync
performance from an user's perspective. But, it's definitely useful
for developing/improving stuffs in XLogWrite.

And also during my tests, I didn't observe any other background
processes performing the xlogwrite operation, the values are always
zero. Is it fine to merge them with backend columns?

Apart from wal writer process, I don't see any reason why you should
track other background processes separately from normal backends.
However, I may be missing some important point.

--
Thanks & Regards,
Kuntal Ghosh
EnterpriseDB: http://www.enterprisedb.com

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

#23Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Kuntal Ghosh (#22)
3 attachment(s)
Re: pg_stat_wal_write statistics view

On Tue, Sep 12, 2017 at 3:14 PM, Kuntal Ghosh <kuntalghosh.2007@gmail.com>
wrote:

On Tue, Sep 12, 2017 at 9:06 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

On Tue, Sep 12, 2017 at 2:04 AM, Kuntal Ghosh <

kuntalghosh.2007@gmail.com>

wrote:

Thanks for the patch.
+ * Check whether the current process is a normal backend or not.
+ * This function checks for the background processes that does
+ * some WAL write activity only and other background processes
+ * are not considered. It considers all the background workers
+ * as WAL write activity workers.
+ *
+ * Returns false - when the current process is a normal backend
+ *        true - when the current process a background process/worker
+ */
+static bool
+am_background_process()
+{
+   /* check whether current process is a background process/worker? */
+   if (!AmBackgroundWriterProcess() ||
+       !AmCheckpointerProcess() ||
+       !AmStartupProcess() ||
+       !IsBackgroundWorker ||
+       !am_walsender ||
+       !am_autovacuum_worker)
+   {
+       return false;
+   }
+
+   return true;
+}
I think you've to do AND operation here instead of OR. Isn't it?
Another point is that, the function description could start with
'Check whether the current process is a background process/worker.'

Yes, it should be AND, while correcting a review comment, I messed
up that function.

There is an overhead with IO time calculation. Is the view is good

enough without IO columns?

I'm not sure how IO columns are useful for tuning the WAL write/fsync
performance from an user's perspective. But, it's definitely useful
for developing/improving stuffs in XLogWrite.

I ran the latest performance tests with and without IO times, there is an
overhead involved with IO time calculation and didn't observe any
performance
overhead with normal stats. May be we can enable the IO stats only in the
development environment to find out the IO stats?

And also during my tests, I didn't observe any other background
processes performing the xlogwrite operation, the values are always
zero. Is it fine to merge them with backend columns?

Apart from wal writer process, I don't see any reason why you should
track other background processes separately from normal backends.
However, I may be missing some important point.

I added the other background stats to find out how much WAL write is
carried out by the other background processes. Now I am able to collect
the stats for the other background processes also after the pgbench test.
So I feel now the separate background stats may be useful.

Attached latest patch, performance test results and stats details with
separate background stats and also combine them with backend including
the IO stats also.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_stat_walwrites_stats.txttext/plain; charset=US-ASCII; name=pg_stat_walwrites_stats.txtDownload
0001-pg_stat_walwrites-statistics-view_v8.patchapplication/octet-stream; name=0001-pg_stat_walwrites-statistics-view_v8.patchDownload
From 69b6b325dd07b2a0c1b9a2653ccbdc851e26e2f1 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Tue, 5 Sep 2017 11:17:47 +1000
Subject: [PATCH] pg_stat_walwrites statistics view

This view will provide the details of how many blocks,
writes and the time taken to write the WAL data to disk
by the backend, walwriter and other background process.
Based on this data, it is configure walwriter to reduce
the load on the backend processes, thus it should improve
the performance.

The time fields data gets populated only when the
track_io_timing GUC is enabled. This is to avoid adding
some overhead in finding out the time calculation that
is required to report in the view.
---
 doc/src/sgml/config.sgml               |   3 +-
 doc/src/sgml/monitoring.sgml           | 112 ++++++++++++++++++++++++++++
 src/backend/access/transam/xlog.c      | 126 ++++++++++++++++++++++++++++++++
 src/backend/catalog/system_views.sql   |   3 +
 src/backend/postmaster/autovacuum.c    |   1 -
 src/backend/postmaster/pgstat.c        | 130 ++++++++++++++++++++++++++++++++-
 src/backend/postmaster/walwriter.c     |   2 +
 src/backend/utils/adt/pgstatfuncs.c    |  44 +++++++++++
 src/backend/utils/init/globals.c       |   1 +
 src/include/catalog/pg_proc.h          |   3 +
 src/include/miscadmin.h                |   2 +
 src/include/pgstat.h                   |  67 ++++++++++++++++-
 src/test/regress/expected/rules.out    |  17 +++++
 src/test/regress/expected/sysviews.out |   7 ++
 src/test/regress/sql/sysviews.sql      |   3 +
 15 files changed, 516 insertions(+), 5 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 5f59a38..85e545d 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5761,7 +5761,8 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
         platforms.  You can use the <xref linkend="pgtesttiming"> tool to
         measure the overhead of timing on your system.
         I/O timing information is
-        displayed in <xref linkend="pg-stat-database-view">, in the output of
+        displayed in <xref linkend="pg-stat-database-view">, 
+        <xref linkend="pg-stat-walwrites-view">, in the output of
         <xref linkend="sql-explain"> when the <literal>BUFFERS</> option is
         used, and by <xref linkend="pgstatstatements">.  Only superusers can
         change this setting.
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 38bf636..8d79a05 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -365,6 +365,14 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
      </row>
 
      <row>
+      <entry><structname>pg_stat_walwrites</><indexterm><primary>pg_stat_walwrites</primary></indexterm></entry>
+      <entry>One row only, showing statistics about the
+       WAL writing activity. See
+       <xref linkend="pg-stat-walwrites-view"> for details.
+     </entry>
+     </row>
+
+     <row>
       <entry><structname>pg_stat_database</><indexterm><primary>pg_stat_database</primary></indexterm></entry>
       <entry>One row per database, showing database-wide statistics. See
        <xref linkend="pg-stat-database-view"> for details.
@@ -2254,6 +2262,108 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
    single row, containing global data for the cluster.
   </para>
 
+  <table id="pg-stat-walwrites-view" xreflabel="pg_stat_walwrites">
+   <title><structname>pg_stat_walwrites</structname> View</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of WAL writes that are carried out by background processes and workers. 
+      </entry>
+     </row>
+    <row>
+      <entry><structfield>backend_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL writes that are carried out by the backend process</entry>
+     </row>
+     <row>
+     <entry><structfield>dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by background processes and workers
+      when the <xref linkend="guc-wal-buffers"> are full.
+      </entry>
+     </row>
+    <row>
+      <entry><structfield>backend_dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by the backend processes when 
+      the <xref linkend="guc-wal-buffers"> are full. 
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the background processes/workers</entry>
+     </row>
+     <row>
+      <entry><structfield>backend_write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the backend processes</entry>
+     </row>
+     <row>
+      <entry><structfield>write_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are written to disk by the background processes/workers, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>backend_write_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are written to disk by the backend processes, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>sync_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are synchronized to disk by the background processes/workers, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>backend_sync_time</></entry>
+      <entry><type>double precision</type></entry>
+      <entry>
+      Total amount of time that has been spent in the portion of WAL write processing where files 
+      are synchronized to disk by the backend processes, in milliseconds.
+      This field data will be populated only when the <xref linkend="guc-track-io-timing"> is enabled
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>stats_reset</></entry>
+      <entry><type>timestamp with time zone</type></entry>
+      <entry>Time at which these statistics were last reset</entry>
+     </row>
+     </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_walwrites</structname> view will always have a
+   single row, containing data about the WAL writing activity of the cluster.
+  </para>
+
+
   <table id="pg-stat-database-view" xreflabel="pg_stat_database">
    <title><structname>pg_stat_database</structname> View</title>
    <tgroup cols="3">
@@ -3033,6 +3143,8 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        counters shown in the <structname>pg_stat_bgwriter</> view.
        Calling <literal>pg_stat_reset_shared('archiver')</> will zero all the
        counters shown in the <structname>pg_stat_archiver</> view.
+       Calling <literal>pg_stat_reset_shared('walwrites')</> will zero all the
+       counters shown in the <structname>pg_stat_walwrites</> view.
       </entry>
      </row>
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index a3e8ce0..3846ea5 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -598,6 +598,9 @@ typedef struct XLogCtlData
 	 */
 	XLogwrtResult LogwrtResult;
 
+	/* Protected by WALWriteLock */
+	PgStat_WalWritesCounts stats;
+
 	/*
 	 * Latest initialized page in the cache (last byte position + 1).
 	 *
@@ -862,6 +865,7 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void);
 
 static void AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic);
 static bool XLogCheckpointNeeded(XLogSegNo new_segno);
+static bool am_background_process(void);
 static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible);
 static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath,
 					   bool find_free, XLogSegNo max_segno,
@@ -2112,6 +2116,12 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic)
 					WriteRqst.Write = OldPageRqstPtr;
 					WriteRqst.Flush = 0;
 					XLogWrite(WriteRqst, false);
+					if (am_background_process())
+						XLogCtl->stats.dirty_writes++;
+					else if (AmWalWriterProcess())
+						XLogCtl->stats.walwriter_dirty_writes++;
+					else
+						XLogCtl->stats.backend_dirty_writes++;
 					LWLockRelease(WALWriteLock);
 					TRACE_POSTGRESQL_WAL_BUFFER_WRITE_DIRTY_DONE();
 				}
@@ -2322,6 +2332,33 @@ XLogCheckpointNeeded(XLogSegNo new_segno)
 }
 
 /*
+ * Check whether the current process is a background process/worker
+ * or not. This function checks for the background processes that
+ * does some WAL write activity only and other background processes
+ * are not considered. It considers all the background workers
+ * as WAL write activity workers.
+ *
+ * Returns false - when the current process is a normal backend
+ *		   true - when the current process a background process/worker
+ */
+static bool
+am_background_process()
+{
+	/* check whether current process is a background process/worker? */
+	if (!AmBackgroundWriterProcess() &&
+		!AmCheckpointerProcess() &&
+		!AmStartupProcess() &&
+		!IsBackgroundWorker &&
+		!am_walsender &&
+		!am_autovacuum_worker)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+/*
  * Write and/or fsync the log at least as far as WriteRqst indicates.
  *
  * If flexible == TRUE, we don't have to write as far as WriteRqst, but
@@ -2344,6 +2381,13 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 	int			npages;
 	int			startidx;
 	uint32		startoffset;
+	instr_time	io_start,
+				io_time;
+	PgStat_Counter writes = 0;
+	PgStat_Counter write_blocks = 0;
+	PgStat_Counter total_write_time = 0;
+	PgStat_Counter total_sync_time = 0;
+	bool		is_background_process = am_background_process();
 
 	/* We should always be inside a critical section here */
 	Assert(CritSectionCount > 0);
@@ -2461,6 +2505,11 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			/* OK to write the page(s) */
 			from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ;
 			nbytes = npages * (Size) XLOG_BLCKSZ;
+
+			/* Start timer to acquire start time of the wal write */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			nleft = nbytes;
 			do
 			{
@@ -2483,6 +2532,24 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				from += written;
 			} while (nleft > 0);
 
+			/* calculate the total time spent for wal writing */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				total_write_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				total_write_time = 0;
+			}
+
+			/* check whether writer is a normal backend or not? */
+			writes++;
+			write_blocks += npages;
+
+
 			/* Update state for write */
 			openLogOff += nbytes;
 			npages = 0;
@@ -2502,8 +2569,25 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			 */
 			if (finishing_seg)
 			{
+				/* Start timer to acquire start time of the wal sync */
+				if (track_io_timing)
+					INSTR_TIME_SET_CURRENT(io_start);
+
 				issue_xlog_fsync(openLogFile, openLogSegNo);
 
+				/* calculate the total time spent for wal sync */
+				if (track_io_timing)
+				{
+					INSTR_TIME_SET_CURRENT(io_time);
+					INSTR_TIME_SUBTRACT(io_time, io_start);
+
+					total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+				}
+				else
+				{
+					total_sync_time = 0;
+				}
+
 				/* signal that we need to wakeup walsenders later */
 				WalSndWakeupRequest();
 
@@ -2571,7 +2655,24 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				openLogOff = 0;
 			}
 
+			/* Start timer to acquire start time of the wal sync */
+			if (track_io_timing)
+				INSTR_TIME_SET_CURRENT(io_start);
+
 			issue_xlog_fsync(openLogFile, openLogSegNo);
+
+			/* calculate the total time spent for wal sync */
+			if (track_io_timing)
+			{
+				INSTR_TIME_SET_CURRENT(io_time);
+				INSTR_TIME_SUBTRACT(io_time, io_start);
+
+				total_sync_time += INSTR_TIME_GET_MILLISEC(io_time);
+			}
+			else
+			{
+				total_sync_time = 0;
+			}
 		}
 
 		/* signal that we need to wakeup walsenders later */
@@ -2580,6 +2681,28 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 		LogwrtResult.Flush = LogwrtResult.Write;
 	}
 
+	if (is_background_process)
+	{
+		XLogCtl->stats.writes += writes;
+		XLogCtl->stats.write_blocks += write_blocks;
+		XLogCtl->stats.total_write_time += total_write_time;
+		XLogCtl->stats.total_sync_time += total_sync_time;
+	}
+	else if (AmWalWriterProcess())
+	{
+		XLogCtl->stats.walwriter_writes += writes;
+		XLogCtl->stats.walwriter_write_blocks += write_blocks;
+		XLogCtl->stats.walwriter_total_write_time += total_write_time;
+		XLogCtl->stats.walwriter_total_sync_time += total_sync_time;
+	}
+	else
+	{
+		XLogCtl->stats.backend_writes += writes;
+		XLogCtl->stats.backend_write_blocks += write_blocks;
+		XLogCtl->stats.backend_total_write_time += total_write_time;
+		XLogCtl->stats.backend_total_sync_time += total_sync_time;
+	}
+
 	/*
 	 * Update shared-memory status
 	 *
@@ -3046,6 +3169,9 @@ XLogBackgroundFlush(void)
 	{
 		XLogWrite(WriteRqst, flexible);
 	}
+
+	/* Collect all the wal write shared counters into local, and report it to stats collector */
+	memcpy(&LocalWalWritesStats.stats, &XLogCtl->stats, sizeof(PgStat_WalWritesCounts));
 	LWLockRelease(WALWriteLock);
 
 	END_CRIT_SECTION();
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index dc40cde..08139a1 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -881,6 +881,9 @@ CREATE VIEW pg_stat_bgwriter AS
         pg_stat_get_buf_alloc() AS buffers_alloc,
         pg_stat_get_bgwriter_stat_reset_time() AS stats_reset;
 
+CREATE VIEW pg_stat_walwrites AS
+   SELECT * FROM pg_stat_get_walwrites() AS A;
+
 CREATE VIEW pg_stat_progress_vacuum AS
 	SELECT
 		S.pid AS pid, S.datid AS datid, D.datname AS datname,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 776b1c0..85e40c8 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -135,7 +135,6 @@ int			Log_autovacuum_min_duration = -1;
 
 /* Flags to tell if we are in an autovacuum process */
 static bool am_autovacuum_launcher = false;
-static bool am_autovacuum_worker = false;
 
 /* Flags set by signal handlers */
 static volatile sig_atomic_t got_SIGHUP = false;
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index accf302..95effbd 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -142,6 +142,15 @@ char	   *pgstat_stat_tmpname = NULL;
  */
 PgStat_MsgBgWriter BgWriterStats;
 
+/*
+ * WalWrites Local statistics counters.
+ * The statistics data gets populated in XLogWrite function.
+ * Stored directly in a stats message structure so it can be sent
+ * to stats collector process without needing to copy things around.
+ * We assume this inits to zeroes.
+ */
+PgStat_MsgWalWrites LocalWalWritesStats;
+
 /* ----------
  * Local data
  * ----------
@@ -256,6 +265,7 @@ static int	localNumBackends = 0;
  */
 static PgStat_ArchiverStats archiverStats;
 static PgStat_GlobalStats globalStats;
+static PgStat_WalWritesStats walwritesStats;
 
 /*
  * List of OIDs of databases we need to write out.  If an entry is InvalidOid,
@@ -336,6 +346,7 @@ static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
 static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
 static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
+static void pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len);
 
 /* ------------------------------------------------------------
  * Public functions called from postmaster follow
@@ -1336,11 +1347,13 @@ pgstat_reset_shared_counters(const char *target)
 		msg.m_resettarget = RESET_ARCHIVER;
 	else if (strcmp(target, "bgwriter") == 0)
 		msg.m_resettarget = RESET_BGWRITER;
+	else if (strcmp(target, "walwrites") == 0)
+		msg.m_resettarget = RESET_WALWRITES;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("unrecognized reset target: \"%s\"", target),
-				 errhint("Target must be \"archiver\" or \"bgwriter\".")));
+				 errhint("Target must be \"archiver\" or \"bgwriter\" or \"walwrites\".")));
 
 	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER);
 	pgstat_send(&msg, sizeof(msg));
@@ -2580,6 +2593,21 @@ pgstat_fetch_global(void)
 	return &globalStats;
 }
 
+/*
+ * ---------
+ * pgstat_fetch_stat_walwrites() -
+ *
+ *	Support function for the SQL-callable pgstat* functions. Returns
+ *	a pointer to the walwrites statistics struct.
+ * ---------
+ */
+PgStat_WalWritesStats *
+pgstat_fetch_stat_walwrites(void)
+{
+	backend_read_statsfile();
+
+	return &walwritesStats;
+}
 
 /* ------------------------------------------------------------
  * Functions for management of the shared-memory PgBackendStatus array
@@ -4176,6 +4204,39 @@ pgstat_send_bgwriter(void)
 	MemSet(&BgWriterStats, 0, sizeof(BgWriterStats));
 }
 
+/* ----------
+ * pgstat_send_walwrites() -
+ *
+ *		Send wal writes statistics to the collector
+ * ----------
+ */
+
+void
+pgstat_send_walwrites(void)
+{
+	/* We assume this initializes to zeroes */
+	static const PgStat_MsgWalWrites all_zeroes;
+
+	/*
+	 * This function can be called even if nothing at all has happened. In
+	 * this case, avoid sending a completely empty message to the stats
+	 * collector.
+	 */
+	if (memcmp(&LocalWalWritesStats, &all_zeroes, sizeof(PgStat_MsgWalWrites)) == 0)
+		return;
+
+	/*
+	 * Prepare and send the message
+	 */
+	pgstat_setheader(&LocalWalWritesStats.m_hdr, PGSTAT_MTYPE_WALWRITES);
+	pgstat_send(&LocalWalWritesStats, sizeof(LocalWalWritesStats));
+
+	/*
+	 * Clear out the statistics buffer, so it can be re-used.
+	 */
+	MemSet(&LocalWalWritesStats, 0, sizeof(LocalWalWritesStats));
+}
+
 
 /* ----------
  * PgstatCollectorMain() -
@@ -4392,6 +4453,10 @@ PgstatCollectorMain(int argc, char *argv[])
 					pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len);
 					break;
 
+				case PGSTAT_MTYPE_WALWRITES:
+					pgstat_recv_walwrites((PgStat_MsgWalWrites *) & msg, len);
+					break;
+
 				default:
 					break;
 			}
@@ -4658,6 +4723,12 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 	(void) rc;					/* we'll check for error with ferror */
 
 	/*
+	 * Write wal writes stats struct
+	 */
+	rc = fwrite(&walwritesStats, sizeof(walwritesStats), 1, fpout);
+	(void) rc;					/* we'll check for error with ferror */
+
+	/*
 	 * Walk through the database table.
 	 */
 	hash_seq_init(&hstat, pgStatDBHash);
@@ -4914,6 +4985,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	memset(&globalStats, 0, sizeof(globalStats));
 	memset(&archiverStats, 0, sizeof(archiverStats));
+	memset(&walwritesStats, 0, sizeof(walwritesStats));
 
 	/*
 	 * Set the current timestamp (will be kept only in case we can't load an
@@ -4921,6 +4993,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	globalStats.stat_reset_timestamp = GetCurrentTimestamp();
 	archiverStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
+	walwritesStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
 
 	/*
 	 * Try to open the stats file. If it doesn't exist, the backends simply
@@ -4985,6 +5058,16 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	}
 
 	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&walwritesStats, 1, sizeof(walwritesStats), fpin) != sizeof(walwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		goto done;
+	}
+
+	/*
 	 * We found an existing collector stats file. Read it and put all the
 	 * hashtable entries into place.
 	 */
@@ -5282,6 +5365,7 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 	PgStat_StatDBEntry dbentry;
 	PgStat_GlobalStats myGlobalStats;
 	PgStat_ArchiverStats myArchiverStats;
+	PgStat_WalWritesStats myWalwritesStats;
 	FILE	   *fpin;
 	int32		format_id;
 	const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
@@ -5336,6 +5420,18 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 		return false;
 	}
 
+	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&myWalwritesStats, 1, sizeof(myWalwritesStats),
+			  fpin) != sizeof(myWalwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		FreeFile(fpin);
+		return false;
+	}
+
 	/* By default, we're going to return the timestamp of the global file. */
 	*ts = myGlobalStats.stats_timestamp;
 
@@ -5898,6 +5994,12 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
 		memset(&archiverStats, 0, sizeof(archiverStats));
 		archiverStats.stat_reset_timestamp = GetCurrentTimestamp();
 	}
+	else if (msg->m_resettarget == RESET_WALWRITES)
+	{
+		/* Reset the wal writes statistics of the cluster. */
+		memset(&walwritesStats, 0, sizeof(walwritesStats));
+		walwritesStats.stat_reset_timestamp = GetCurrentTimestamp();
+	}
 
 	/*
 	 * Presumably the sender of this message validated the target, don't
@@ -6078,6 +6180,32 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
 }
 
 /* ----------
+ * pgstat_recv_walwrites() -
+ *
+ *	Process a WALWRITES message.
+ * ----------
+ */
+static void
+pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len)
+{
+	walwritesStats.stats.writes += msg->stats.writes;
+	walwritesStats.stats.walwriter_writes += msg->stats.walwriter_writes;
+	walwritesStats.stats.backend_writes += msg->stats.backend_writes;
+	walwritesStats.stats.dirty_writes += msg->stats.dirty_writes;
+	walwritesStats.stats.walwriter_dirty_writes += msg->stats.walwriter_dirty_writes;
+	walwritesStats.stats.backend_dirty_writes += msg->stats.backend_dirty_writes;
+	walwritesStats.stats.write_blocks += msg->stats.write_blocks;
+	walwritesStats.stats.walwriter_write_blocks += msg->stats.walwriter_write_blocks;
+	walwritesStats.stats.backend_write_blocks += msg->stats.backend_write_blocks;
+	walwritesStats.stats.total_write_time += msg->stats.total_write_time;
+	walwritesStats.stats.walwriter_total_write_time += msg->stats.walwriter_total_write_time;
+	walwritesStats.stats.backend_total_write_time += msg->stats.backend_total_write_time;
+	walwritesStats.stats.total_sync_time += msg->stats.total_sync_time;
+	walwritesStats.stats.walwriter_total_sync_time += msg->stats.walwriter_total_sync_time;
+	walwritesStats.stats.backend_total_sync_time += msg->stats.backend_total_sync_time;
+}
+
+/* ----------
  * pgstat_recv_recoveryconflict() -
  *
  *	Process a RECOVERYCONFLICT message.
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 7b89e02..4f4dd84 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -280,6 +280,8 @@ WalWriterMain(void)
 		else if (left_till_hibernate > 0)
 			left_till_hibernate--;
 
+		pgstat_send_walwrites();
+
 		/*
 		 * Sleep until we are signaled or WalWriterDelay has elapsed.  If we
 		 * haven't done anything useful for quite some time, lengthen the
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 20ce48b..f71e10d 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1861,3 +1861,47 @@ pg_stat_get_archiver(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(HeapTupleGetDatum(
 									  heap_form_tuple(tupdesc, values, nulls)));
 }
+
+#define NUM_PG_STAT_WALWRITE_ATTS 16
+
+Datum
+pg_stat_get_walwrites(PG_FUNCTION_ARGS)
+{
+	TupleDesc	tupdesc;
+	Datum		values[NUM_PG_STAT_WALWRITE_ATTS];
+	bool		nulls[NUM_PG_STAT_WALWRITE_ATTS];
+	PgStat_WalWritesStats *walwrite_stats;
+
+	/* Initialize values and NULL flags arrays */
+	MemSet(values, 0, sizeof(values));
+	MemSet(nulls, 0, sizeof(nulls));
+
+	/* 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");
+
+	/* Get statistics about the archiver process */
+	walwrite_stats = pgstat_fetch_stat_walwrites();
+
+	/* Fill values and NULLs */
+	values[0] = Int64GetDatum(walwrite_stats->stats.writes);
+	values[1] = Int64GetDatum(walwrite_stats->stats.walwriter_writes);
+	values[2] = Int64GetDatum(walwrite_stats->stats.backend_writes);
+	values[3] = Int64GetDatum(walwrite_stats->stats.dirty_writes);
+	values[4] = Int64GetDatum(walwrite_stats->stats.walwriter_dirty_writes);
+	values[5] = Int64GetDatum(walwrite_stats->stats.backend_dirty_writes);
+	values[6] = Int64GetDatum(walwrite_stats->stats.write_blocks);
+	values[7] = Int64GetDatum(walwrite_stats->stats.walwriter_write_blocks);
+	values[8] = Int64GetDatum(walwrite_stats->stats.backend_write_blocks);
+	values[9] = Float8GetDatum(walwrite_stats->stats.total_write_time);
+	values[10] = Float8GetDatum(walwrite_stats->stats.walwriter_total_write_time);
+	values[11] = Float8GetDatum(walwrite_stats->stats.backend_total_write_time);
+	values[12] = Float8GetDatum(walwrite_stats->stats.total_sync_time);
+	values[13] = Float8GetDatum(walwrite_stats->stats.walwriter_total_sync_time);
+	values[14] = Float8GetDatum(walwrite_stats->stats.backend_total_sync_time);
+	values[15] = TimestampTzGetDatum(walwrite_stats->stat_reset_timestamp);
+
+	/* Returns the record as Datum */
+	PG_RETURN_DATUM(HeapTupleGetDatum(
+								   heap_form_tuple(tupdesc, values, nulls)));
+}
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 7c09498..3cb2b54 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -101,6 +101,7 @@ bool		IsPostmasterEnvironment = false;
 bool		IsUnderPostmaster = false;
 bool		IsBinaryUpgrade = false;
 bool		IsBackgroundWorker = false;
+bool            am_autovacuum_worker = false;
 
 bool		ExitOnAnyError = false;
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d820b56..e1a61bc 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2973,6 +2973,9 @@ DESCR("statistics: number of backend buffer writes that did their own fsync");
 DATA(insert OID = 2859 ( pg_stat_get_buf_alloc			PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_buf_alloc _null_ _null_ _null_ ));
 DESCR("statistics: number of buffer allocations");
 
+DATA(insert OID = 3998 (  pg_stat_get_walwrites		PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{20,20,20,20,20,20,20,20,20,701,701,701,701,701,701,1184}" "{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{writes,walwriter_writes,backend_writes,dirty_writes,walwriter_dirty_writes,backend_dirty_writes,write_blocks,walwriter_write_blocks,backend_write_blocks,write_time,walwriter_write_time,backend_write_time,sync_time,walwriter_sync_time,backend_sync_time,stats_reset}" _null_ _null_ pg_stat_get_walwrites _null_ _null_ _null_ ));
+DESCR("statistics: information about WAL writes activity");
+
 DATA(insert OID = 2978 (  pg_stat_get_function_calls		PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_calls _null_ _null_ _null_ ));
 DESCR("statistics: number of function calls");
 DATA(insert OID = 2979 (  pg_stat_get_function_total_time	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 701 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_total_time _null_ _null_ _null_ ));
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index dad98de..201c8e6 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -243,6 +243,8 @@ extern PGDLLIMPORT int work_mem;
 extern PGDLLIMPORT int maintenance_work_mem;
 extern PGDLLIMPORT int replacement_sort_tuples;
 
+extern bool	am_autovacuum_worker;
+
 extern int	VacuumCostPageHit;
 extern int	VacuumCostPageMiss;
 extern int	VacuumCostPageDirty;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 57ac5d4..b4d1fca 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -64,7 +64,8 @@ typedef enum StatMsgType
 	PGSTAT_MTYPE_FUNCPURGE,
 	PGSTAT_MTYPE_RECOVERYCONFLICT,
 	PGSTAT_MTYPE_TEMPFILE,
-	PGSTAT_MTYPE_DEADLOCK
+	PGSTAT_MTYPE_DEADLOCK,
+	PGSTAT_MTYPE_WALWRITES
 } StatMsgType;
 
 /* ----------
@@ -119,7 +120,8 @@ typedef struct PgStat_TableCounts
 typedef enum PgStat_Shared_Reset_Target
 {
 	RESET_ARCHIVER,
-	RESET_BGWRITER
+	RESET_BGWRITER,
+	RESET_WALWRITES
 } PgStat_Shared_Reset_Target;
 
 /* Possible object types for resetting single counters */
@@ -422,6 +424,50 @@ typedef struct PgStat_MsgBgWriter
 	PgStat_Counter m_checkpoint_sync_time;
 } PgStat_MsgBgWriter;
 
+/*
+ * Walwrites statistics counters
+ */
+typedef struct PgStat_WalWritesCounts
+{
+	PgStat_Counter writes;		/* No of writes by background
+								 * processes/workers */
+	PgStat_Counter walwriter_writes;		/* No of writes by walwriter */
+	PgStat_Counter backend_writes;		/* No of writes by backend */
+	PgStat_Counter dirty_writes;/* No of dirty writes by background
+								 * processes/workers when WAL buffers full */
+	PgStat_Counter walwriter_dirty_writes;/* No of dirty writes by walwriter
+	 	 	 	 	 	 	 	 	 	   * when WAL buffers full */
+	PgStat_Counter backend_dirty_writes;		/* No of dirty writes by
+												 * backend when WAL buffers
+												 * full */
+	PgStat_Counter write_blocks;/* Total no of pages written by background
+								 * processes/workers */
+	PgStat_Counter walwriter_write_blocks;/* Total no of pages written by walwriter */
+	PgStat_Counter backend_write_blocks;	/* Total no of pages written by backend */
+	PgStat_Counter total_write_time;	/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter walwriter_total_write_time;	/* Total write time in milliseconds by
+										 	 	 * walwriter */
+	PgStat_Counter backend_total_write_time;	/* Total write time in
+												 * milliseconds by backend */
+	PgStat_Counter total_sync_time;		/* Total write time in milliseconds by
+										 * background processes/workers */
+	PgStat_Counter walwriter_total_sync_time;		/* Total write time in milliseconds by
+										 	 	 	 * walwriter */
+	PgStat_Counter backend_total_sync_time;		/* Total write time in
+												 * milliseconds by backend */
+} PgStat_WalWritesCounts;
+
+/* ----------
+ * PgStat_MsgWalWrites			Sent by the walwriter after collecting all shared stats
+ * ----------
+ */
+typedef struct PgStat_MsgWalWrites
+{
+	PgStat_MsgHdr m_hdr;
+	PgStat_WalWritesCounts stats;
+} PgStat_MsgWalWrites;
+
 /* ----------
  * PgStat_MsgRecoveryConflict	Sent by the backend upon recovery conflict
  * ----------
@@ -555,6 +601,7 @@ typedef union PgStat_Msg
 	PgStat_MsgFuncpurge msg_funcpurge;
 	PgStat_MsgRecoveryConflict msg_recoveryconflict;
 	PgStat_MsgDeadlock msg_deadlock;
+	PgStat_MsgWalWrites msg_walwrites;
 } PgStat_Msg;
 
 
@@ -694,6 +741,14 @@ typedef struct PgStat_GlobalStats
 	TimestampTz stat_reset_timestamp;
 } PgStat_GlobalStats;
 
+/*
+ * Walwrites statistics kept in the stats collector
+ */
+typedef struct PgStat_WalWritesStats
+{
+	PgStat_WalWritesCounts stats;
+	TimestampTz stat_reset_timestamp;	/* Last time when the stats reset */
+} PgStat_WalWritesStats;
 
 /* ----------
  * Backend types
@@ -1120,6 +1175,11 @@ extern char *pgstat_stat_filename;
 extern PgStat_MsgBgWriter BgWriterStats;
 
 /*
+ * Wal writes statistics updated in XLogWrite function
+ */
+extern PgStat_MsgWalWrites LocalWalWritesStats;
+
+/*
  * Updated by pgstat_count_buffer_*_time macros
  */
 extern PgStat_Counter pgStatBlockReadTime;
@@ -1313,6 +1373,7 @@ extern void pgstat_twophase_postabort(TransactionId xid, uint16 info,
 
 extern void pgstat_send_archiver(const char *xlog, bool failed);
 extern void pgstat_send_bgwriter(void);
+extern void pgstat_send_walwrites(void);
 
 /* ----------
  * Support functions for the SQL-callable functions to
@@ -1327,5 +1388,7 @@ extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
 extern int	pgstat_fetch_stat_numbackends(void);
 extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
 extern PgStat_GlobalStats *pgstat_fetch_global(void);
+extern PgStat_WalWritesStats *pgstat_fetch_stat_walwrites(void);
+
 
 #endif							/* PGSTAT_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f1c1b44..6a833de 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1973,6 +1973,23 @@ pg_stat_wal_receiver| SELECT s.pid,
     s.conninfo
    FROM pg_stat_get_wal_receiver() s(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, conninfo)
   WHERE (s.pid IS NOT NULL);
+pg_stat_walwrites| SELECT a.writes,
+    a.walwriter_writes,
+    a.backend_writes,
+    a.dirty_writes,
+    a.walwriter_dirty_writes,
+    a.backend_dirty_writes,
+    a.write_blocks,
+    a.walwriter_write_blocks,
+    a.backend_write_blocks,
+    a.write_time,
+    a.walwriter_write_time,
+    a.backend_write_time,
+    a.sync_time,
+    a.walwriter_sync_time,
+    a.backend_sync_time,
+    a.stats_reset
+   FROM pg_stat_get_walwrites() a(writes, walwriter_writes, backend_writes, dirty_writes, walwriter_dirty_writes, backend_dirty_writes, write_blocks, walwriter_write_blocks, backend_write_blocks, write_time, walwriter_write_time, backend_write_time, sync_time, walwriter_sync_time, backend_sync_time, stats_reset);
 pg_stat_xact_all_tables| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 568b783..8448cac 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -67,6 +67,13 @@ select count(*) >= 0 as ok from pg_prepared_xacts;
  t
 (1 row)
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+ ok 
+----
+ t
+(1 row)
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 28e412b..3769880 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -32,6 +32,9 @@ select count(*) = 0 as ok from pg_prepared_statements;
 -- See also prepared_xacts.sql
 select count(*) >= 0 as ok from pg_prepared_xacts;
 
+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
-- 
2.7.4.windows.1

pg_stat_walwrites_perf.xlsxapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet; name=pg_stat_walwrites_perf.xlsxDownload
#24Julien Rouhaud
rjuju123@gmail.com
In reply to: Haribabu Kommi (#23)
Re: pg_stat_wal_write statistics view

Hello,

On Wed, Sep 13, 2017 at 3:01 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

I ran the latest performance tests with and without IO times, there is an
overhead involved with IO time calculation and didn't observe any
performance
overhead with normal stats. May be we can enable the IO stats only in the
development environment to find out the IO stats?

-1 for me to have these columns depending on a GUC *and* wether it's a
debug or assert build (unless this behaviour already exists for other
functions, but AFAIK that's not the case).

I added the other background stats to find out how much WAL write is
carried out by the other background processes. Now I am able to collect
the stats for the other background processes also after the pgbench test.
So I feel now the separate background stats may be useful.

Attached latest patch, performance test results and stats details with
separate background stats and also combine them with backend including
the IO stats also.

The results seem to vary too much to have a strong opinion, but it
looks like the timing instrumentation can be too expensive, so I'd be
-1 on adding this overhead to track_io_timing.

I have some minor comments on the last patch:

+    <row>
+      <entry><structfield>backend_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL writes that are carried out by the backend
process</entry>

it should be backend processes

+#define NUM_PG_STAT_WALWRITE_ATTS 16
+
+Datum
+pg_stat_get_walwrites(PG_FUNCTION_ARGS)
+{
+   TupleDesc   tupdesc;
+   Datum       values[NUM_PG_STAT_WALWRITE_ATTS];
+   bool        nulls[NUM_PG_STAT_WALWRITE_ATTS];

For consistency, the #define should be just before the tupdesc
declaration, and be named PG_STAT_GET_WALWRITE_COLS

+ PgStat_Counter backend_writes; /* No of writes by backend */

+   PgStat_Counter backend_dirty_writes;        /* No of dirty writes by
+                                                * backend when WAL buffers
+                                                * full */

+ PgStat_Counter backend_write_blocks; /* Total no of pages
written by backend */

+   PgStat_Counter backend_total_write_time;    /* Total write time in
+                                                * milliseconds by backend */
+   PgStat_Counter backend_total_sync_time;     /* Total write time in
+                                                * milliseconds by backend */

these comments should all be say "backends" for consistency

+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;

The test should be count(*) = 1

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

#25Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Julien Rouhaud (#24)
1 attachment(s)
Re: pg_stat_wal_write statistics view

On Fri, Sep 22, 2017 at 5:46 PM, Julien Rouhaud <rjuju123@gmail.com> wrote:

Hello,

On Wed, Sep 13, 2017 at 3:01 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

I ran the latest performance tests with and without IO times, there is an
overhead involved with IO time calculation and didn't observe any
performance
overhead with normal stats. May be we can enable the IO stats only in the
development environment to find out the IO stats?

-1 for me to have these columns depending on a GUC *and* wether it's a
debug or assert build (unless this behaviour already exists for other
functions, but AFAIK that's not the case).

I added the other background stats to find out how much WAL write is
carried out by the other background processes. Now I am able to collect
the stats for the other background processes also after the pgbench test.
So I feel now the separate background stats may be useful.

Attached latest patch, performance test results and stats details with
separate background stats and also combine them with backend including
the IO stats also.

The results seem to vary too much to have a strong opinion, but it
looks like the timing instrumentation can be too expensive, so I'd be
-1 on adding this overhead to track_io_timing.

Thanks for the review.
I removed the time related columns from the view. As these columns are
adding
an overhead and GUC controlled behavior is different to the other views.

Apart from above time columns, I removed walwriter_dirty_writes, as the
walwriter writers cannot be treated as dirty writes.

I have some minor comments on the last patch:

+    <row>
+      <entry><structfield>backend_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL writes that are carried out by the backend
process</entry>

it should be backend processes

Changed.

+#define NUM_PG_STAT_WALWRITE_ATTS 16
+
+Datum
+pg_stat_get_walwrites(PG_FUNCTION_ARGS)
+{
+   TupleDesc   tupdesc;
+   Datum       values[NUM_PG_STAT_WALWRITE_ATTS];
+   bool        nulls[NUM_PG_STAT_WALWRITE_ATTS];

For consistency, the #define should be just before the tupdesc
declaration, and be named PG_STAT_GET_WALWRITE_COLS

Changed.

+ PgStat_Counter backend_writes; /* No of writes by backend */

+   PgStat_Counter backend_dirty_writes;        /* No of dirty writes by
+                                                * backend when WAL buffers
+                                                * full */

+ PgStat_Counter backend_write_blocks; /* Total no of pages
written by backend */

these comments should all be say "backends" for consistency

Changed.

+-- There will surely and maximum one record
+select count(*) > 0 as ok from pg_stat_walwrites;

The test should be count(*) = 1

Changed.

Updated patch attached.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_stat_walwrites-statistics-view_v9.patchapplication/octet-stream; name=pg_stat_walwrites-statistics-view_v9.patchDownload
From 10c65fe527734e3c427ef7dd206e5c5173d21cd7 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Tue, 5 Sep 2017 11:17:47 +1000
Subject: [PATCH] pg_stat_walwrites statistics view

This view will provide the details of how many blocks,
writes and the time taken to write the WAL data to disk
by the backend, walwriter and other background process.
Based on this data, it is configure walwriter to reduce
the load on the backend processes, thus it should improve
the performance.
---
 doc/src/sgml/monitoring.sgml           |  86 +++++++++++++++++++++++
 src/backend/access/transam/xlog.c      |  69 ++++++++++++++++++
 src/backend/catalog/system_views.sql   |   3 +
 src/backend/postmaster/autovacuum.c    |   1 -
 src/backend/postmaster/pgstat.c        | 123 ++++++++++++++++++++++++++++++++-
 src/backend/postmaster/walwriter.c     |   2 +
 src/backend/utils/adt/pgstatfuncs.c    |  36 ++++++++++
 src/backend/utils/init/globals.c       |   1 +
 src/include/catalog/pg_proc.h          |   3 +
 src/include/miscadmin.h                |   2 +
 src/include/pgstat.h                   |  53 +++++++++++++-
 src/test/regress/expected/rules.out    |  10 +++
 src/test/regress/expected/sysviews.out |   7 ++
 src/test/regress/sql/sysviews.sql      |   3 +
 14 files changed, 395 insertions(+), 4 deletions(-)

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 18fb9c2aa6..7d0b098a36 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -364,6 +364,14 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
      </entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_walwrites</><indexterm><primary>pg_stat_walwrites</primary></indexterm></entry>
+      <entry>One row only, showing statistics about the
+       WAL writing activity. See
+       <xref linkend="pg-stat-walwrites-view"> for details.
+     </entry>
+     </row>
+
      <row>
       <entry><structname>pg_stat_database</><indexterm><primary>pg_stat_database</primary></indexterm></entry>
       <entry>One row per database, showing database-wide statistics. See
@@ -2262,6 +2270,82 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
    single row, containing global data for the cluster.
   </para>
 
+  <table id="pg-stat-walwrites-view" xreflabel="pg_stat_walwrites">
+   <title><structname>pg_stat_walwrites</structname> View</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of WAL writes that are carried out by background processes and workers. 
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>walwriter_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL writes that are carried out by the wal writer process</entry>
+     </row>
+     <row>
+      <entry><structfield>backend_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL writes that are carried out by the backend processes</entry>
+     </row>
+     <row>
+      <entry><structfield>dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by background processes and workers
+      when the <xref linkend="guc-wal-buffers"> are full.
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>backend_dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by the backend processes when 
+      the <xref linkend="guc-wal-buffers"> are full. 
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the background processes/workers</entry>
+     </row>
+     <row>
+      <entry><structfield>walwriter_write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the wal writer process</entry>
+     </row>
+     <row>
+      <entry><structfield>backend_write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the backend processes</entry>
+     </row>
+     <row>
+      <entry><structfield>stats_reset</></entry>
+      <entry><type>timestamp with time zone</type></entry>
+      <entry>Time at which these statistics were last reset</entry>
+     </row>
+     </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_walwrites</structname> view will always have a
+   single row, containing data about the WAL writing activity of the cluster.
+  </para>
+
+
   <table id="pg-stat-database-view" xreflabel="pg_stat_database">
    <title><structname>pg_stat_database</structname> View</title>
    <tgroup cols="3">
@@ -3041,6 +3125,8 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        counters shown in the <structname>pg_stat_bgwriter</> view.
        Calling <literal>pg_stat_reset_shared('archiver')</> will zero all the
        counters shown in the <structname>pg_stat_archiver</> view.
+       Calling <literal>pg_stat_reset_shared('walwrites')</> will zero all the
+       counters shown in the <structname>pg_stat_walwrites</> view.
       </entry>
      </row>
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index dd028a12a4..c3e6f9eb54 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -600,6 +600,9 @@ typedef struct XLogCtlData
 	 */
 	XLogwrtResult LogwrtResult;
 
+	/* Protected by WALWriteLock */
+	PgStat_WalWritesCounts stats;
+
 	/*
 	 * Latest initialized page in the cache (last byte position + 1).
 	 *
@@ -866,6 +869,7 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void);
 
 static void AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic);
 static bool XLogCheckpointNeeded(XLogSegNo new_segno);
+static bool am_background_process(void);
 static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible);
 static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath,
 					   bool find_free, XLogSegNo max_segno,
@@ -2118,6 +2122,17 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic)
 					WriteRqst.Write = OldPageRqstPtr;
 					WriteRqst.Flush = 0;
 					XLogWrite(WriteRqst, false);
+					if (AmWalWriterProcess())
+					{
+						/*
+						 * Don't consider the writes of wal writer process as dirty writes,
+						 * so skipping.
+						 */
+					}
+					else if (am_background_process())
+						XLogCtl->stats.dirty_writes++;
+					else
+						XLogCtl->stats.backend_dirty_writes++;
 					LWLockRelease(WALWriteLock);
 					TRACE_POSTGRESQL_WAL_BUFFER_WRITE_DIRTY_DONE();
 				}
@@ -2331,6 +2346,33 @@ XLogCheckpointNeeded(XLogSegNo new_segno)
 	return false;
 }
 
+/*
+ * Check whether the current process is a background process/worker
+ * or not. This function checks for the background processes that
+ * does some WAL write activity only and other background processes
+ * are not considered. It considers all the background workers
+ * as WAL write activity workers.
+ *
+ * Returns false - when the current process is a normal backend
+ *		   true - when the current process a background process/worker
+ */
+static bool
+am_background_process()
+{
+	/* check whether current process is a background process/worker? */
+	if (!AmBackgroundWriterProcess() &&
+		!AmCheckpointerProcess() &&
+		!AmStartupProcess() &&
+		!IsBackgroundWorker &&
+		!am_walsender &&
+		!am_autovacuum_worker)
+	{
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * Write and/or fsync the log at least as far as WriteRqst indicates.
  *
@@ -2354,6 +2396,9 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 	int			npages;
 	int			startidx;
 	uint32		startoffset;
+	PgStat_Counter writes = 0;
+	PgStat_Counter write_blocks = 0;
+	bool		is_background_process = am_background_process();
 
 	/* We should always be inside a critical section here */
 	Assert(CritSectionCount > 0);
@@ -2475,6 +2520,7 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			/* OK to write the page(s) */
 			from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ;
 			nbytes = npages * (Size) XLOG_BLCKSZ;
+
 			nleft = nbytes;
 			do
 			{
@@ -2497,6 +2543,10 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				from += written;
 			} while (nleft > 0);
 
+			/* check whether writer is a normal backend or not? */
+			writes++;
+			write_blocks += npages;
+
 			/* Update state for write */
 			openLogOff += nbytes;
 			npages = 0;
@@ -2596,6 +2646,22 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 		LogwrtResult.Flush = LogwrtResult.Write;
 	}
 
+	if (is_background_process)
+	{
+		XLogCtl->stats.writes += writes;
+		XLogCtl->stats.write_blocks += write_blocks;
+	}
+	else if (AmWalWriterProcess())
+	{
+		XLogCtl->stats.walwriter_writes += writes;
+		XLogCtl->stats.walwriter_write_blocks += write_blocks;
+	}
+	else
+	{
+		XLogCtl->stats.backend_writes += writes;
+		XLogCtl->stats.backend_write_blocks += write_blocks;
+	}
+
 	/*
 	 * Update shared-memory status
 	 *
@@ -3063,6 +3129,9 @@ XLogBackgroundFlush(void)
 	{
 		XLogWrite(WriteRqst, flexible);
 	}
+
+	/* Collect all the wal write shared counters into local, and report it to stats collector */
+	memcpy(&LocalWalWritesStats.stats, &XLogCtl->stats, sizeof(PgStat_WalWritesCounts));
 	LWLockRelease(WALWriteLock);
 
 	END_CRIT_SECTION();
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index dc40cde424..08139a14c5 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -881,6 +881,9 @@ CREATE VIEW pg_stat_bgwriter AS
         pg_stat_get_buf_alloc() AS buffers_alloc,
         pg_stat_get_bgwriter_stat_reset_time() AS stats_reset;
 
+CREATE VIEW pg_stat_walwrites AS
+   SELECT * FROM pg_stat_get_walwrites() AS A;
+
 CREATE VIEW pg_stat_progress_vacuum AS
 	SELECT
 		S.pid AS pid, S.datid AS datid, D.datname AS datname,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index db6d91ffdf..18db3aef13 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -135,7 +135,6 @@ int			Log_autovacuum_min_duration = -1;
 
 /* Flags to tell if we are in an autovacuum process */
 static bool am_autovacuum_launcher = false;
-static bool am_autovacuum_worker = false;
 
 /* Flags set by signal handlers */
 static volatile sig_atomic_t got_SIGHUP = false;
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 3a0b49c7c4..0fc34ae946 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -142,6 +142,15 @@ char	   *pgstat_stat_tmpname = NULL;
  */
 PgStat_MsgBgWriter BgWriterStats;
 
+/*
+ * WalWrites Local statistics counters.
+ * The statistics data gets populated in XLogWrite function.
+ * Stored directly in a stats message structure so it can be sent
+ * to stats collector process without needing to copy things around.
+ * We assume this inits to zeroes.
+ */
+PgStat_MsgWalWrites LocalWalWritesStats;
+
 /* ----------
  * Local data
  * ----------
@@ -256,6 +265,7 @@ static int	localNumBackends = 0;
  */
 static PgStat_ArchiverStats archiverStats;
 static PgStat_GlobalStats globalStats;
+static PgStat_WalWritesStats walwritesStats;
 
 /*
  * List of OIDs of databases we need to write out.  If an entry is InvalidOid,
@@ -336,6 +346,7 @@ static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
 static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
 static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
+static void pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len);
 
 /* ------------------------------------------------------------
  * Public functions called from postmaster follow
@@ -1336,11 +1347,13 @@ pgstat_reset_shared_counters(const char *target)
 		msg.m_resettarget = RESET_ARCHIVER;
 	else if (strcmp(target, "bgwriter") == 0)
 		msg.m_resettarget = RESET_BGWRITER;
+	else if (strcmp(target, "walwrites") == 0)
+		msg.m_resettarget = RESET_WALWRITES;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("unrecognized reset target: \"%s\"", target),
-				 errhint("Target must be \"archiver\" or \"bgwriter\".")));
+				 errhint("Target must be \"archiver\" or \"bgwriter\" or \"walwrites\".")));
 
 	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER);
 	pgstat_send(&msg, sizeof(msg));
@@ -2580,6 +2593,21 @@ pgstat_fetch_global(void)
 	return &globalStats;
 }
 
+/*
+ * ---------
+ * pgstat_fetch_stat_walwrites() -
+ *
+ *	Support function for the SQL-callable pgstat* functions. Returns
+ *	a pointer to the walwrites statistics struct.
+ * ---------
+ */
+PgStat_WalWritesStats *
+pgstat_fetch_stat_walwrites(void)
+{
+	backend_read_statsfile();
+
+	return &walwritesStats;
+}
 
 /* ------------------------------------------------------------
  * Functions for management of the shared-memory PgBackendStatus array
@@ -4184,6 +4212,39 @@ pgstat_send_bgwriter(void)
 	MemSet(&BgWriterStats, 0, sizeof(BgWriterStats));
 }
 
+/* ----------
+ * pgstat_send_walwrites() -
+ *
+ *		Send wal writes statistics to the collector
+ * ----------
+ */
+
+void
+pgstat_send_walwrites(void)
+{
+	/* We assume this initializes to zeroes */
+	static const PgStat_MsgWalWrites all_zeroes;
+
+	/*
+	 * This function can be called even if nothing at all has happened. In
+	 * this case, avoid sending a completely empty message to the stats
+	 * collector.
+	 */
+	if (memcmp(&LocalWalWritesStats, &all_zeroes, sizeof(PgStat_MsgWalWrites)) == 0)
+		return;
+
+	/*
+	 * Prepare and send the message
+	 */
+	pgstat_setheader(&LocalWalWritesStats.m_hdr, PGSTAT_MTYPE_WALWRITES);
+	pgstat_send(&LocalWalWritesStats, sizeof(LocalWalWritesStats));
+
+	/*
+	 * Clear out the statistics buffer, so it can be re-used.
+	 */
+	MemSet(&LocalWalWritesStats, 0, sizeof(LocalWalWritesStats));
+}
+
 
 /* ----------
  * PgstatCollectorMain() -
@@ -4400,6 +4461,10 @@ PgstatCollectorMain(int argc, char *argv[])
 					pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len);
 					break;
 
+				case PGSTAT_MTYPE_WALWRITES:
+					pgstat_recv_walwrites((PgStat_MsgWalWrites *) & msg, len);
+					break;
+
 				default:
 					break;
 			}
@@ -4665,6 +4730,12 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 	rc = fwrite(&archiverStats, sizeof(archiverStats), 1, fpout);
 	(void) rc;					/* we'll check for error with ferror */
 
+	/*
+	 * Write wal writes stats struct
+	 */
+	rc = fwrite(&walwritesStats, sizeof(walwritesStats), 1, fpout);
+	(void) rc;					/* we'll check for error with ferror */
+
 	/*
 	 * Walk through the database table.
 	 */
@@ -4922,6 +4993,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	memset(&globalStats, 0, sizeof(globalStats));
 	memset(&archiverStats, 0, sizeof(archiverStats));
+	memset(&walwritesStats, 0, sizeof(walwritesStats));
 
 	/*
 	 * Set the current timestamp (will be kept only in case we can't load an
@@ -4929,6 +5001,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	globalStats.stat_reset_timestamp = GetCurrentTimestamp();
 	archiverStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
+	walwritesStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
 
 	/*
 	 * Try to open the stats file. If it doesn't exist, the backends simply
@@ -4992,6 +5065,16 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 		goto done;
 	}
 
+	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&walwritesStats, 1, sizeof(walwritesStats), fpin) != sizeof(walwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		goto done;
+	}
+
 	/*
 	 * We found an existing collector stats file. Read it and put all the
 	 * hashtable entries into place.
@@ -5290,6 +5373,7 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 	PgStat_StatDBEntry dbentry;
 	PgStat_GlobalStats myGlobalStats;
 	PgStat_ArchiverStats myArchiverStats;
+	PgStat_WalWritesStats myWalwritesStats;
 	FILE	   *fpin;
 	int32		format_id;
 	const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
@@ -5344,6 +5428,18 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 		return false;
 	}
 
+	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&myWalwritesStats, 1, sizeof(myWalwritesStats),
+			  fpin) != sizeof(myWalwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		FreeFile(fpin);
+		return false;
+	}
+
 	/* By default, we're going to return the timestamp of the global file. */
 	*ts = myGlobalStats.stats_timestamp;
 
@@ -5906,6 +6002,12 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
 		memset(&archiverStats, 0, sizeof(archiverStats));
 		archiverStats.stat_reset_timestamp = GetCurrentTimestamp();
 	}
+	else if (msg->m_resettarget == RESET_WALWRITES)
+	{
+		/* Reset the wal writes statistics of the cluster. */
+		memset(&walwritesStats, 0, sizeof(walwritesStats));
+		walwritesStats.stat_reset_timestamp = GetCurrentTimestamp();
+	}
 
 	/*
 	 * Presumably the sender of this message validated the target, don't
@@ -6085,6 +6187,25 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
 	globalStats.buf_alloc += msg->m_buf_alloc;
 }
 
+/* ----------
+ * pgstat_recv_walwrites() -
+ *
+ *	Process a WALWRITES message.
+ * ----------
+ */
+static void
+pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len)
+{
+	walwritesStats.stats.writes += msg->stats.writes;
+	walwritesStats.stats.walwriter_writes += msg->stats.walwriter_writes;
+	walwritesStats.stats.backend_writes += msg->stats.backend_writes;
+	walwritesStats.stats.dirty_writes += msg->stats.dirty_writes;
+	walwritesStats.stats.backend_dirty_writes += msg->stats.backend_dirty_writes;
+	walwritesStats.stats.write_blocks += msg->stats.write_blocks;
+	walwritesStats.stats.walwriter_write_blocks += msg->stats.walwriter_write_blocks;
+	walwritesStats.stats.backend_write_blocks += msg->stats.backend_write_blocks;
+}
+
 /* ----------
  * pgstat_recv_recoveryconflict() -
  *
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 7b89e02428..4f4dd849f8 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -280,6 +280,8 @@ WalWriterMain(void)
 		else if (left_till_hibernate > 0)
 			left_till_hibernate--;
 
+		pgstat_send_walwrites();
+
 		/*
 		 * Sleep until we are signaled or WalWriterDelay has elapsed.  If we
 		 * haven't done anything useful for quite some time, lengthen the
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 5a968e3758..ad6c886656 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1870,3 +1870,39 @@ pg_stat_get_archiver(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(HeapTupleGetDatum(
 									  heap_form_tuple(tupdesc, values, nulls)));
 }
+
+Datum
+pg_stat_get_walwrites(PG_FUNCTION_ARGS)
+{
+	TupleDesc	tupdesc;
+#define NUM_PG_STAT_WALWRITE_COLS 9
+	Datum		values[NUM_PG_STAT_WALWRITE_COLS];
+	bool		nulls[NUM_PG_STAT_WALWRITE_COLS];
+	PgStat_WalWritesStats *walwrite_stats;
+
+	/* Initialize values and NULL flags arrays */
+	MemSet(values, 0, sizeof(values));
+	MemSet(nulls, 0, sizeof(nulls));
+
+	/* 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");
+
+	/* Get statistics about the archiver process */
+	walwrite_stats = pgstat_fetch_stat_walwrites();
+
+	/* Fill values and NULLs */
+	values[0] = Int64GetDatum(walwrite_stats->stats.writes);
+	values[1] = Int64GetDatum(walwrite_stats->stats.walwriter_writes);
+	values[2] = Int64GetDatum(walwrite_stats->stats.backend_writes);
+	values[3] = Int64GetDatum(walwrite_stats->stats.dirty_writes);
+	values[4] = Int64GetDatum(walwrite_stats->stats.backend_dirty_writes);
+	values[5] = Int64GetDatum(walwrite_stats->stats.write_blocks);
+	values[6] = Int64GetDatum(walwrite_stats->stats.walwriter_write_blocks);
+	values[7] = Int64GetDatum(walwrite_stats->stats.backend_write_blocks);
+	values[8] = TimestampTzGetDatum(walwrite_stats->stat_reset_timestamp);
+
+	/* Returns the record as Datum */
+	PG_RETURN_DATUM(HeapTupleGetDatum(
+								   heap_form_tuple(tupdesc, values, nulls)));
+}
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 7c09498dc0..3cb2b5456f 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -101,6 +101,7 @@ bool		IsPostmasterEnvironment = false;
 bool		IsUnderPostmaster = false;
 bool		IsBinaryUpgrade = false;
 bool		IsBackgroundWorker = false;
+bool            am_autovacuum_worker = false;
 
 bool		ExitOnAnyError = false;
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 93c031aad7..27205f3c9d 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2980,6 +2980,9 @@ DESCR("statistics: number of backend buffer writes that did their own fsync");
 DATA(insert OID = 2859 ( pg_stat_get_buf_alloc			PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_buf_alloc _null_ _null_ _null_ ));
 DESCR("statistics: number of buffer allocations");
 
+DATA(insert OID = 3998 (  pg_stat_get_walwrites		PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{20,20,20,20,20,20,20,20,1184}" "{o,o,o,o,o,o,o,o,o}" "{writes,walwriter_writes,backend_writes,dirty_writes,backend_dirty_writes,write_blocks,walwriter_write_blocks,backend_write_blocks,stats_reset}" _null_ _null_ pg_stat_get_walwrites _null_ _null_ _null_ ));
+DESCR("statistics: information about WAL writes activity");
+
 DATA(insert OID = 2978 (  pg_stat_get_function_calls		PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_calls _null_ _null_ _null_ ));
 DESCR("statistics: number of function calls");
 DATA(insert OID = 2979 (  pg_stat_get_function_total_time	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 701 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_total_time _null_ _null_ _null_ ));
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index dad98de98d..201c8e6217 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -243,6 +243,8 @@ extern PGDLLIMPORT int work_mem;
 extern PGDLLIMPORT int maintenance_work_mem;
 extern PGDLLIMPORT int replacement_sort_tuples;
 
+extern bool	am_autovacuum_worker;
+
 extern int	VacuumCostPageHit;
 extern int	VacuumCostPageMiss;
 extern int	VacuumCostPageDirty;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 089b7c3a10..5be0b062ca 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -64,7 +64,8 @@ typedef enum StatMsgType
 	PGSTAT_MTYPE_FUNCPURGE,
 	PGSTAT_MTYPE_RECOVERYCONFLICT,
 	PGSTAT_MTYPE_TEMPFILE,
-	PGSTAT_MTYPE_DEADLOCK
+	PGSTAT_MTYPE_DEADLOCK,
+	PGSTAT_MTYPE_WALWRITES
 } StatMsgType;
 
 /* ----------
@@ -119,7 +120,8 @@ typedef struct PgStat_TableCounts
 typedef enum PgStat_Shared_Reset_Target
 {
 	RESET_ARCHIVER,
-	RESET_BGWRITER
+	RESET_BGWRITER,
+	RESET_WALWRITES
 } PgStat_Shared_Reset_Target;
 
 /* Possible object types for resetting single counters */
@@ -422,6 +424,36 @@ typedef struct PgStat_MsgBgWriter
 	PgStat_Counter m_checkpoint_sync_time;
 } PgStat_MsgBgWriter;
 
+/*
+ * Walwrites statistics counters
+ */
+typedef struct PgStat_WalWritesCounts
+{
+	PgStat_Counter writes;		/* No of writes by background
+								 * processes/workers */
+	PgStat_Counter walwriter_writes;		/* No of writes by walwriter */
+	PgStat_Counter backend_writes;		/* No of writes by backends */
+	PgStat_Counter dirty_writes;/* No of dirty writes by background
+								 * processes/workers when WAL buffers full */
+	PgStat_Counter backend_dirty_writes;		/* No of dirty writes by
+												 * backends when WAL buffers
+												 * full */
+	PgStat_Counter write_blocks;/* Total no of pages written by background
+								 * processes/workers */
+	PgStat_Counter walwriter_write_blocks;/* Total no of pages written by walwriter */
+	PgStat_Counter backend_write_blocks;	/* Total no of pages written by backends */
+} PgStat_WalWritesCounts;
+
+/* ----------
+ * PgStat_MsgWalWrites			Sent by the walwriter after collecting all shared stats
+ * ----------
+ */
+typedef struct PgStat_MsgWalWrites
+{
+	PgStat_MsgHdr m_hdr;
+	PgStat_WalWritesCounts stats;
+} PgStat_MsgWalWrites;
+
 /* ----------
  * PgStat_MsgRecoveryConflict	Sent by the backend upon recovery conflict
  * ----------
@@ -555,6 +587,7 @@ typedef union PgStat_Msg
 	PgStat_MsgFuncpurge msg_funcpurge;
 	PgStat_MsgRecoveryConflict msg_recoveryconflict;
 	PgStat_MsgDeadlock msg_deadlock;
+	PgStat_MsgWalWrites msg_walwrites;
 } PgStat_Msg;
 
 
@@ -694,6 +727,14 @@ typedef struct PgStat_GlobalStats
 	TimestampTz stat_reset_timestamp;
 } PgStat_GlobalStats;
 
+/*
+ * Walwrites statistics kept in the stats collector
+ */
+typedef struct PgStat_WalWritesStats
+{
+	PgStat_WalWritesCounts stats;
+	TimestampTz stat_reset_timestamp;	/* Last time when the stats reset */
+} PgStat_WalWritesStats;
 
 /* ----------
  * Backend types
@@ -1125,6 +1166,11 @@ extern char *pgstat_stat_filename;
  */
 extern PgStat_MsgBgWriter BgWriterStats;
 
+/*
+ * Wal writes statistics updated in XLogWrite function
+ */
+extern PgStat_MsgWalWrites LocalWalWritesStats;
+
 /*
  * Updated by pgstat_count_buffer_*_time macros
  */
@@ -1321,6 +1367,7 @@ extern void pgstat_twophase_postabort(TransactionId xid, uint16 info,
 
 extern void pgstat_send_archiver(const char *xlog, bool failed);
 extern void pgstat_send_bgwriter(void);
+extern void pgstat_send_walwrites(void);
 
 /* ----------
  * Support functions for the SQL-callable functions to
@@ -1335,5 +1382,7 @@ extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
 extern int	pgstat_fetch_stat_numbackends(void);
 extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
 extern PgStat_GlobalStats *pgstat_fetch_global(void);
+extern PgStat_WalWritesStats *pgstat_fetch_stat_walwrites(void);
+
 
 #endif							/* PGSTAT_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f1c1b44d6f..6a84e3f980 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1973,6 +1973,16 @@ pg_stat_wal_receiver| SELECT s.pid,
     s.conninfo
    FROM pg_stat_get_wal_receiver() s(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, conninfo)
   WHERE (s.pid IS NOT NULL);
+pg_stat_walwrites| SELECT a.writes,
+    a.walwriter_writes,
+    a.backend_writes,
+    a.dirty_writes,
+    a.backend_dirty_writes,
+    a.write_blocks,
+    a.walwriter_write_blocks,
+    a.backend_write_blocks,
+    a.stats_reset
+   FROM pg_stat_get_walwrites() a(writes, walwriter_writes, backend_writes, dirty_writes, backend_dirty_writes, write_blocks, walwriter_write_blocks, backend_write_blocks, stats_reset);
 pg_stat_xact_all_tables| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 568b783f5e..aa91df0a6c 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -67,6 +67,13 @@ select count(*) >= 0 as ok from pg_prepared_xacts;
  t
 (1 row)
 
+-- There will surely and maximum one record
+select count(*) = 1 as ok from pg_stat_walwrites;
+ ok 
+----
+ t
+(1 row)
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 28e412b735..21f49c9a3b 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -32,6 +32,9 @@ select count(*) = 0 as ok from pg_prepared_statements;
 -- See also prepared_xacts.sql
 select count(*) >= 0 as ok from pg_prepared_xacts;
 
+-- There will surely and maximum one record
+select count(*) = 1 as ok from pg_stat_walwrites;
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
-- 
2.14.1.windows.1

#26Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Haribabu Kommi (#25)
1 attachment(s)
Re: pg_stat_wal_write statistics view

On Wed, Sep 27, 2017 at 6:58 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

Updated patch attached.

Patch rebased.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_stat_walwrites-statistics-view_v10.patchapplication/octet-stream; name=pg_stat_walwrites-statistics-view_v10.patchDownload
From 43ba7bf73e60f92022e5c32ed546567be6d3f9c8 Mon Sep 17 00:00:00 2001
From: Hari Babu <haribabuk@fast.au.fujitsu.com>
Date: Tue, 5 Sep 2017 11:17:47 +1000
Subject: [PATCH] pg_stat_walwrites statistics view

This view will provide the details of how many blocks,
writes and the time taken to write the WAL data to disk
by the backend, walwriter and other background process.
Based on this data, it is configure walwriter to reduce
the load on the backend processes, thus it should improve
the performance.
---
 doc/src/sgml/monitoring.sgml           |  86 +++++++++++++++++++++++
 src/backend/access/transam/xlog.c      |  69 ++++++++++++++++++
 src/backend/catalog/system_views.sql   |   3 +
 src/backend/postmaster/autovacuum.c    |   1 -
 src/backend/postmaster/pgstat.c        | 123 ++++++++++++++++++++++++++++++++-
 src/backend/postmaster/walwriter.c     |   2 +
 src/backend/utils/adt/pgstatfuncs.c    |  36 ++++++++++
 src/backend/utils/init/globals.c       |   1 +
 src/include/catalog/pg_proc.h          |   3 +
 src/include/miscadmin.h                |   2 +
 src/include/pgstat.h                   |  53 +++++++++++++-
 src/test/regress/expected/rules.out    |  10 +++
 src/test/regress/expected/sysviews.out |   7 ++
 src/test/regress/sql/sysviews.sql      |   3 +
 14 files changed, 395 insertions(+), 4 deletions(-)

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 6f8203355e..f91de9c5da 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -364,6 +364,14 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
      </entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_walwrites</><indexterm><primary>pg_stat_walwrites</primary></indexterm></entry>
+      <entry>One row only, showing statistics about the
+       WAL writing activity. See
+       <xref linkend="pg-stat-walwrites-view"> for details.
+     </entry>
+     </row>
+
      <row>
       <entry><structname>pg_stat_database</structname><indexterm><primary>pg_stat_database</primary></indexterm></entry>
       <entry>One row per database, showing database-wide statistics. See
@@ -2262,6 +2270,82 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
    single row, containing global data for the cluster.
   </para>
 
+  <table id="pg-stat-walwrites-view" xreflabel="pg_stat_walwrites">
+   <title><structname>pg_stat_walwrites</structname> View</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of WAL writes that are carried out by background processes and workers. 
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>walwriter_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL writes that are carried out by the wal writer process</entry>
+     </row>
+     <row>
+      <entry><structfield>backend_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL writes that are carried out by the backend processes</entry>
+     </row>
+     <row>
+      <entry><structfield>dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by background processes and workers
+      when the <xref linkend="guc-wal-buffers"> are full.
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>backend_dirty_writes</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by the backend processes when 
+      the <xref linkend="guc-wal-buffers"> are full. 
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the background processes/workers</entry>
+     </row>
+     <row>
+      <entry><structfield>walwriter_write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the wal writer process</entry>
+     </row>
+     <row>
+      <entry><structfield>backend_write_blocks</></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the backend processes</entry>
+     </row>
+     <row>
+      <entry><structfield>stats_reset</></entry>
+      <entry><type>timestamp with time zone</type></entry>
+      <entry>Time at which these statistics were last reset</entry>
+     </row>
+     </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_walwrites</structname> view will always have a
+   single row, containing data about the WAL writing activity of the cluster.
+  </para>
+
+
   <table id="pg-stat-database-view" xreflabel="pg_stat_database">
    <title><structname>pg_stat_database</structname> View</title>
    <tgroup cols="3">
@@ -3041,6 +3125,8 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        counters shown in the <structname>pg_stat_bgwriter</structname> view.
        Calling <literal>pg_stat_reset_shared('archiver')</literal> will zero all the
        counters shown in the <structname>pg_stat_archiver</structname> view.
+       Calling <literal>pg_stat_reset_shared('walwrites')</literal> will zero all the
+       counters shown in the <structname>pg_stat_walwrites</structname> view.
       </entry>
      </row>
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index dd028a12a4..c3e6f9eb54 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -600,6 +600,9 @@ typedef struct XLogCtlData
 	 */
 	XLogwrtResult LogwrtResult;
 
+	/* Protected by WALWriteLock */
+	PgStat_WalWritesCounts stats;
+
 	/*
 	 * Latest initialized page in the cache (last byte position + 1).
 	 *
@@ -866,6 +869,7 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void);
 
 static void AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic);
 static bool XLogCheckpointNeeded(XLogSegNo new_segno);
+static bool am_background_process(void);
 static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible);
 static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath,
 					   bool find_free, XLogSegNo max_segno,
@@ -2118,6 +2122,17 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic)
 					WriteRqst.Write = OldPageRqstPtr;
 					WriteRqst.Flush = 0;
 					XLogWrite(WriteRqst, false);
+					if (AmWalWriterProcess())
+					{
+						/*
+						 * Don't consider the writes of wal writer process as dirty writes,
+						 * so skipping.
+						 */
+					}
+					else if (am_background_process())
+						XLogCtl->stats.dirty_writes++;
+					else
+						XLogCtl->stats.backend_dirty_writes++;
 					LWLockRelease(WALWriteLock);
 					TRACE_POSTGRESQL_WAL_BUFFER_WRITE_DIRTY_DONE();
 				}
@@ -2331,6 +2346,33 @@ XLogCheckpointNeeded(XLogSegNo new_segno)
 	return false;
 }
 
+/*
+ * Check whether the current process is a background process/worker
+ * or not. This function checks for the background processes that
+ * does some WAL write activity only and other background processes
+ * are not considered. It considers all the background workers
+ * as WAL write activity workers.
+ *
+ * Returns false - when the current process is a normal backend
+ *		   true - when the current process a background process/worker
+ */
+static bool
+am_background_process()
+{
+	/* check whether current process is a background process/worker? */
+	if (!AmBackgroundWriterProcess() &&
+		!AmCheckpointerProcess() &&
+		!AmStartupProcess() &&
+		!IsBackgroundWorker &&
+		!am_walsender &&
+		!am_autovacuum_worker)
+	{
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * Write and/or fsync the log at least as far as WriteRqst indicates.
  *
@@ -2354,6 +2396,9 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 	int			npages;
 	int			startidx;
 	uint32		startoffset;
+	PgStat_Counter writes = 0;
+	PgStat_Counter write_blocks = 0;
+	bool		is_background_process = am_background_process();
 
 	/* We should always be inside a critical section here */
 	Assert(CritSectionCount > 0);
@@ -2475,6 +2520,7 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 			/* OK to write the page(s) */
 			from = XLogCtl->pages + startidx * (Size) XLOG_BLCKSZ;
 			nbytes = npages * (Size) XLOG_BLCKSZ;
+
 			nleft = nbytes;
 			do
 			{
@@ -2497,6 +2543,10 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				from += written;
 			} while (nleft > 0);
 
+			/* check whether writer is a normal backend or not? */
+			writes++;
+			write_blocks += npages;
+
 			/* Update state for write */
 			openLogOff += nbytes;
 			npages = 0;
@@ -2596,6 +2646,22 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 		LogwrtResult.Flush = LogwrtResult.Write;
 	}
 
+	if (is_background_process)
+	{
+		XLogCtl->stats.writes += writes;
+		XLogCtl->stats.write_blocks += write_blocks;
+	}
+	else if (AmWalWriterProcess())
+	{
+		XLogCtl->stats.walwriter_writes += writes;
+		XLogCtl->stats.walwriter_write_blocks += write_blocks;
+	}
+	else
+	{
+		XLogCtl->stats.backend_writes += writes;
+		XLogCtl->stats.backend_write_blocks += write_blocks;
+	}
+
 	/*
 	 * Update shared-memory status
 	 *
@@ -3063,6 +3129,9 @@ XLogBackgroundFlush(void)
 	{
 		XLogWrite(WriteRqst, flexible);
 	}
+
+	/* Collect all the wal write shared counters into local, and report it to stats collector */
+	memcpy(&LocalWalWritesStats.stats, &XLogCtl->stats, sizeof(PgStat_WalWritesCounts));
 	LWLockRelease(WALWriteLock);
 
 	END_CRIT_SECTION();
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index dc40cde424..08139a14c5 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -881,6 +881,9 @@ CREATE VIEW pg_stat_bgwriter AS
         pg_stat_get_buf_alloc() AS buffers_alloc,
         pg_stat_get_bgwriter_stat_reset_time() AS stats_reset;
 
+CREATE VIEW pg_stat_walwrites AS
+   SELECT * FROM pg_stat_get_walwrites() AS A;
+
 CREATE VIEW pg_stat_progress_vacuum AS
 	SELECT
 		S.pid AS pid, S.datid AS datid, D.datname AS datname,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 48765bb01b..5fbcbcc0a9 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -136,7 +136,6 @@ int			Log_autovacuum_min_duration = -1;
 
 /* Flags to tell if we are in an autovacuum process */
 static bool am_autovacuum_launcher = false;
-static bool am_autovacuum_worker = false;
 
 /* Flags set by signal handlers */
 static volatile sig_atomic_t got_SIGHUP = false;
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 3a0b49c7c4..0fc34ae946 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -142,6 +142,15 @@ char	   *pgstat_stat_tmpname = NULL;
  */
 PgStat_MsgBgWriter BgWriterStats;
 
+/*
+ * WalWrites Local statistics counters.
+ * The statistics data gets populated in XLogWrite function.
+ * Stored directly in a stats message structure so it can be sent
+ * to stats collector process without needing to copy things around.
+ * We assume this inits to zeroes.
+ */
+PgStat_MsgWalWrites LocalWalWritesStats;
+
 /* ----------
  * Local data
  * ----------
@@ -256,6 +265,7 @@ static int	localNumBackends = 0;
  */
 static PgStat_ArchiverStats archiverStats;
 static PgStat_GlobalStats globalStats;
+static PgStat_WalWritesStats walwritesStats;
 
 /*
  * List of OIDs of databases we need to write out.  If an entry is InvalidOid,
@@ -336,6 +346,7 @@ static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
 static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
 static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
+static void pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len);
 
 /* ------------------------------------------------------------
  * Public functions called from postmaster follow
@@ -1336,11 +1347,13 @@ pgstat_reset_shared_counters(const char *target)
 		msg.m_resettarget = RESET_ARCHIVER;
 	else if (strcmp(target, "bgwriter") == 0)
 		msg.m_resettarget = RESET_BGWRITER;
+	else if (strcmp(target, "walwrites") == 0)
+		msg.m_resettarget = RESET_WALWRITES;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("unrecognized reset target: \"%s\"", target),
-				 errhint("Target must be \"archiver\" or \"bgwriter\".")));
+				 errhint("Target must be \"archiver\" or \"bgwriter\" or \"walwrites\".")));
 
 	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER);
 	pgstat_send(&msg, sizeof(msg));
@@ -2580,6 +2593,21 @@ pgstat_fetch_global(void)
 	return &globalStats;
 }
 
+/*
+ * ---------
+ * pgstat_fetch_stat_walwrites() -
+ *
+ *	Support function for the SQL-callable pgstat* functions. Returns
+ *	a pointer to the walwrites statistics struct.
+ * ---------
+ */
+PgStat_WalWritesStats *
+pgstat_fetch_stat_walwrites(void)
+{
+	backend_read_statsfile();
+
+	return &walwritesStats;
+}
 
 /* ------------------------------------------------------------
  * Functions for management of the shared-memory PgBackendStatus array
@@ -4184,6 +4212,39 @@ pgstat_send_bgwriter(void)
 	MemSet(&BgWriterStats, 0, sizeof(BgWriterStats));
 }
 
+/* ----------
+ * pgstat_send_walwrites() -
+ *
+ *		Send wal writes statistics to the collector
+ * ----------
+ */
+
+void
+pgstat_send_walwrites(void)
+{
+	/* We assume this initializes to zeroes */
+	static const PgStat_MsgWalWrites all_zeroes;
+
+	/*
+	 * This function can be called even if nothing at all has happened. In
+	 * this case, avoid sending a completely empty message to the stats
+	 * collector.
+	 */
+	if (memcmp(&LocalWalWritesStats, &all_zeroes, sizeof(PgStat_MsgWalWrites)) == 0)
+		return;
+
+	/*
+	 * Prepare and send the message
+	 */
+	pgstat_setheader(&LocalWalWritesStats.m_hdr, PGSTAT_MTYPE_WALWRITES);
+	pgstat_send(&LocalWalWritesStats, sizeof(LocalWalWritesStats));
+
+	/*
+	 * Clear out the statistics buffer, so it can be re-used.
+	 */
+	MemSet(&LocalWalWritesStats, 0, sizeof(LocalWalWritesStats));
+}
+
 
 /* ----------
  * PgstatCollectorMain() -
@@ -4400,6 +4461,10 @@ PgstatCollectorMain(int argc, char *argv[])
 					pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len);
 					break;
 
+				case PGSTAT_MTYPE_WALWRITES:
+					pgstat_recv_walwrites((PgStat_MsgWalWrites *) & msg, len);
+					break;
+
 				default:
 					break;
 			}
@@ -4665,6 +4730,12 @@ pgstat_write_statsfiles(bool permanent, bool allDbs)
 	rc = fwrite(&archiverStats, sizeof(archiverStats), 1, fpout);
 	(void) rc;					/* we'll check for error with ferror */
 
+	/*
+	 * Write wal writes stats struct
+	 */
+	rc = fwrite(&walwritesStats, sizeof(walwritesStats), 1, fpout);
+	(void) rc;					/* we'll check for error with ferror */
+
 	/*
 	 * Walk through the database table.
 	 */
@@ -4922,6 +4993,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	memset(&globalStats, 0, sizeof(globalStats));
 	memset(&archiverStats, 0, sizeof(archiverStats));
+	memset(&walwritesStats, 0, sizeof(walwritesStats));
 
 	/*
 	 * Set the current timestamp (will be kept only in case we can't load an
@@ -4929,6 +5001,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	 */
 	globalStats.stat_reset_timestamp = GetCurrentTimestamp();
 	archiverStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
+	walwritesStats.stat_reset_timestamp = globalStats.stat_reset_timestamp;
 
 	/*
 	 * Try to open the stats file. If it doesn't exist, the backends simply
@@ -4992,6 +5065,16 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 		goto done;
 	}
 
+	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&walwritesStats, 1, sizeof(walwritesStats), fpin) != sizeof(walwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		goto done;
+	}
+
 	/*
 	 * We found an existing collector stats file. Read it and put all the
 	 * hashtable entries into place.
@@ -5290,6 +5373,7 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 	PgStat_StatDBEntry dbentry;
 	PgStat_GlobalStats myGlobalStats;
 	PgStat_ArchiverStats myArchiverStats;
+	PgStat_WalWritesStats myWalwritesStats;
 	FILE	   *fpin;
 	int32		format_id;
 	const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
@@ -5344,6 +5428,18 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
 		return false;
 	}
 
+	/*
+	 * Read wal writes stats struct
+	 */
+	if (fread(&myWalwritesStats, 1, sizeof(myWalwritesStats),
+			  fpin) != sizeof(myWalwritesStats))
+	{
+		ereport(pgStatRunningInCollector ? LOG : WARNING,
+				(errmsg("corrupted statistics file \"%s\"", statfile)));
+		FreeFile(fpin);
+		return false;
+	}
+
 	/* By default, we're going to return the timestamp of the global file. */
 	*ts = myGlobalStats.stats_timestamp;
 
@@ -5906,6 +6002,12 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
 		memset(&archiverStats, 0, sizeof(archiverStats));
 		archiverStats.stat_reset_timestamp = GetCurrentTimestamp();
 	}
+	else if (msg->m_resettarget == RESET_WALWRITES)
+	{
+		/* Reset the wal writes statistics of the cluster. */
+		memset(&walwritesStats, 0, sizeof(walwritesStats));
+		walwritesStats.stat_reset_timestamp = GetCurrentTimestamp();
+	}
 
 	/*
 	 * Presumably the sender of this message validated the target, don't
@@ -6085,6 +6187,25 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
 	globalStats.buf_alloc += msg->m_buf_alloc;
 }
 
+/* ----------
+ * pgstat_recv_walwrites() -
+ *
+ *	Process a WALWRITES message.
+ * ----------
+ */
+static void
+pgstat_recv_walwrites(PgStat_MsgWalWrites * msg, int len)
+{
+	walwritesStats.stats.writes += msg->stats.writes;
+	walwritesStats.stats.walwriter_writes += msg->stats.walwriter_writes;
+	walwritesStats.stats.backend_writes += msg->stats.backend_writes;
+	walwritesStats.stats.dirty_writes += msg->stats.dirty_writes;
+	walwritesStats.stats.backend_dirty_writes += msg->stats.backend_dirty_writes;
+	walwritesStats.stats.write_blocks += msg->stats.write_blocks;
+	walwritesStats.stats.walwriter_write_blocks += msg->stats.walwriter_write_blocks;
+	walwritesStats.stats.backend_write_blocks += msg->stats.backend_write_blocks;
+}
+
 /* ----------
  * pgstat_recv_recoveryconflict() -
  *
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 7b89e02428..4f4dd849f8 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -280,6 +280,8 @@ WalWriterMain(void)
 		else if (left_till_hibernate > 0)
 			left_till_hibernate--;
 
+		pgstat_send_walwrites();
+
 		/*
 		 * Sleep until we are signaled or WalWriterDelay has elapsed.  If we
 		 * haven't done anything useful for quite some time, lengthen the
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 8d9e7c10ae..1adca065c7 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1882,3 +1882,39 @@ pg_stat_get_archiver(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(HeapTupleGetDatum(
 									  heap_form_tuple(tupdesc, values, nulls)));
 }
+
+Datum
+pg_stat_get_walwrites(PG_FUNCTION_ARGS)
+{
+	TupleDesc	tupdesc;
+#define NUM_PG_STAT_WALWRITE_COLS 9
+	Datum		values[NUM_PG_STAT_WALWRITE_COLS];
+	bool		nulls[NUM_PG_STAT_WALWRITE_COLS];
+	PgStat_WalWritesStats *walwrite_stats;
+
+	/* Initialize values and NULL flags arrays */
+	MemSet(values, 0, sizeof(values));
+	MemSet(nulls, 0, sizeof(nulls));
+
+	/* 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");
+
+	/* Get statistics about the archiver process */
+	walwrite_stats = pgstat_fetch_stat_walwrites();
+
+	/* Fill values and NULLs */
+	values[0] = Int64GetDatum(walwrite_stats->stats.writes);
+	values[1] = Int64GetDatum(walwrite_stats->stats.walwriter_writes);
+	values[2] = Int64GetDatum(walwrite_stats->stats.backend_writes);
+	values[3] = Int64GetDatum(walwrite_stats->stats.dirty_writes);
+	values[4] = Int64GetDatum(walwrite_stats->stats.backend_dirty_writes);
+	values[5] = Int64GetDatum(walwrite_stats->stats.write_blocks);
+	values[6] = Int64GetDatum(walwrite_stats->stats.walwriter_write_blocks);
+	values[7] = Int64GetDatum(walwrite_stats->stats.backend_write_blocks);
+	values[8] = TimestampTzGetDatum(walwrite_stats->stat_reset_timestamp);
+
+	/* Returns the record as Datum */
+	PG_RETURN_DATUM(HeapTupleGetDatum(
+								   heap_form_tuple(tupdesc, values, nulls)));
+}
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 9680a4b0f7..cd09d0da61 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -101,6 +101,7 @@ bool		IsPostmasterEnvironment = false;
 bool		IsUnderPostmaster = false;
 bool		IsBinaryUpgrade = false;
 bool		IsBackgroundWorker = false;
+bool            am_autovacuum_worker = false;
 
 bool		ExitOnAnyError = false;
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 93c031aad7..27205f3c9d 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2980,6 +2980,9 @@ DESCR("statistics: number of backend buffer writes that did their own fsync");
 DATA(insert OID = 2859 ( pg_stat_get_buf_alloc			PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_buf_alloc _null_ _null_ _null_ ));
 DESCR("statistics: number of buffer allocations");
 
+DATA(insert OID = 3998 (  pg_stat_get_walwrites		PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{20,20,20,20,20,20,20,20,1184}" "{o,o,o,o,o,o,o,o,o}" "{writes,walwriter_writes,backend_writes,dirty_writes,backend_dirty_writes,write_blocks,walwriter_write_blocks,backend_write_blocks,stats_reset}" _null_ _null_ pg_stat_get_walwrites _null_ _null_ _null_ ));
+DESCR("statistics: information about WAL writes activity");
+
 DATA(insert OID = 2978 (  pg_stat_get_function_calls		PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_calls _null_ _null_ _null_ ));
 DESCR("statistics: number of function calls");
 DATA(insert OID = 2979 (  pg_stat_get_function_total_time	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 701 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_total_time _null_ _null_ _null_ ));
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 3950054368..d0a2d6a3d0 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -242,6 +242,8 @@ extern bool allowSystemTableMods;
 extern PGDLLIMPORT int work_mem;
 extern PGDLLIMPORT int maintenance_work_mem;
 
+extern bool	am_autovacuum_worker;
+
 extern int	VacuumCostPageHit;
 extern int	VacuumCostPageMiss;
 extern int	VacuumCostPageDirty;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 089b7c3a10..5be0b062ca 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -64,7 +64,8 @@ typedef enum StatMsgType
 	PGSTAT_MTYPE_FUNCPURGE,
 	PGSTAT_MTYPE_RECOVERYCONFLICT,
 	PGSTAT_MTYPE_TEMPFILE,
-	PGSTAT_MTYPE_DEADLOCK
+	PGSTAT_MTYPE_DEADLOCK,
+	PGSTAT_MTYPE_WALWRITES
 } StatMsgType;
 
 /* ----------
@@ -119,7 +120,8 @@ typedef struct PgStat_TableCounts
 typedef enum PgStat_Shared_Reset_Target
 {
 	RESET_ARCHIVER,
-	RESET_BGWRITER
+	RESET_BGWRITER,
+	RESET_WALWRITES
 } PgStat_Shared_Reset_Target;
 
 /* Possible object types for resetting single counters */
@@ -422,6 +424,36 @@ typedef struct PgStat_MsgBgWriter
 	PgStat_Counter m_checkpoint_sync_time;
 } PgStat_MsgBgWriter;
 
+/*
+ * Walwrites statistics counters
+ */
+typedef struct PgStat_WalWritesCounts
+{
+	PgStat_Counter writes;		/* No of writes by background
+								 * processes/workers */
+	PgStat_Counter walwriter_writes;		/* No of writes by walwriter */
+	PgStat_Counter backend_writes;		/* No of writes by backends */
+	PgStat_Counter dirty_writes;/* No of dirty writes by background
+								 * processes/workers when WAL buffers full */
+	PgStat_Counter backend_dirty_writes;		/* No of dirty writes by
+												 * backends when WAL buffers
+												 * full */
+	PgStat_Counter write_blocks;/* Total no of pages written by background
+								 * processes/workers */
+	PgStat_Counter walwriter_write_blocks;/* Total no of pages written by walwriter */
+	PgStat_Counter backend_write_blocks;	/* Total no of pages written by backends */
+} PgStat_WalWritesCounts;
+
+/* ----------
+ * PgStat_MsgWalWrites			Sent by the walwriter after collecting all shared stats
+ * ----------
+ */
+typedef struct PgStat_MsgWalWrites
+{
+	PgStat_MsgHdr m_hdr;
+	PgStat_WalWritesCounts stats;
+} PgStat_MsgWalWrites;
+
 /* ----------
  * PgStat_MsgRecoveryConflict	Sent by the backend upon recovery conflict
  * ----------
@@ -555,6 +587,7 @@ typedef union PgStat_Msg
 	PgStat_MsgFuncpurge msg_funcpurge;
 	PgStat_MsgRecoveryConflict msg_recoveryconflict;
 	PgStat_MsgDeadlock msg_deadlock;
+	PgStat_MsgWalWrites msg_walwrites;
 } PgStat_Msg;
 
 
@@ -694,6 +727,14 @@ typedef struct PgStat_GlobalStats
 	TimestampTz stat_reset_timestamp;
 } PgStat_GlobalStats;
 
+/*
+ * Walwrites statistics kept in the stats collector
+ */
+typedef struct PgStat_WalWritesStats
+{
+	PgStat_WalWritesCounts stats;
+	TimestampTz stat_reset_timestamp;	/* Last time when the stats reset */
+} PgStat_WalWritesStats;
 
 /* ----------
  * Backend types
@@ -1125,6 +1166,11 @@ extern char *pgstat_stat_filename;
  */
 extern PgStat_MsgBgWriter BgWriterStats;
 
+/*
+ * Wal writes statistics updated in XLogWrite function
+ */
+extern PgStat_MsgWalWrites LocalWalWritesStats;
+
 /*
  * Updated by pgstat_count_buffer_*_time macros
  */
@@ -1321,6 +1367,7 @@ extern void pgstat_twophase_postabort(TransactionId xid, uint16 info,
 
 extern void pgstat_send_archiver(const char *xlog, bool failed);
 extern void pgstat_send_bgwriter(void);
+extern void pgstat_send_walwrites(void);
 
 /* ----------
  * Support functions for the SQL-callable functions to
@@ -1335,5 +1382,7 @@ extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
 extern int	pgstat_fetch_stat_numbackends(void);
 extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
 extern PgStat_GlobalStats *pgstat_fetch_global(void);
+extern PgStat_WalWritesStats *pgstat_fetch_stat_walwrites(void);
+
 
 #endif							/* PGSTAT_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f1c1b44d6f..6a84e3f980 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1973,6 +1973,16 @@ pg_stat_wal_receiver| SELECT s.pid,
     s.conninfo
    FROM pg_stat_get_wal_receiver() s(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, conninfo)
   WHERE (s.pid IS NOT NULL);
+pg_stat_walwrites| SELECT a.writes,
+    a.walwriter_writes,
+    a.backend_writes,
+    a.dirty_writes,
+    a.backend_dirty_writes,
+    a.write_blocks,
+    a.walwriter_write_blocks,
+    a.backend_write_blocks,
+    a.stats_reset
+   FROM pg_stat_get_walwrites() a(writes, walwriter_writes, backend_writes, dirty_writes, backend_dirty_writes, write_blocks, walwriter_write_blocks, backend_write_blocks, stats_reset);
 pg_stat_xact_all_tables| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index cd1f7f301d..40c19838c5 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -67,6 +67,13 @@ select count(*) >= 0 as ok from pg_prepared_xacts;
  t
 (1 row)
 
+-- There will surely and maximum one record
+select count(*) = 1 as ok from pg_stat_walwrites;
+ ok 
+----
+ t
+(1 row)
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 28e412b735..21f49c9a3b 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -32,6 +32,9 @@ select count(*) = 0 as ok from pg_prepared_statements;
 -- See also prepared_xacts.sql
 select count(*) >= 0 as ok from pg_prepared_xacts;
 
+-- There will surely and maximum one record
+select count(*) = 1 as ok from pg_stat_walwrites;
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
-- 
2.14.2.windows.1

#27Robert Haas
robertmhaas@gmail.com
In reply to: Haribabu Kommi (#26)
Re: pg_stat_wal_write statistics view

On Tue, Nov 7, 2017 at 4:31 AM, Haribabu Kommi <kommi.haribabu@gmail.com> wrote:

Updated patch attached.

Patch rebased.

I think the earlier concerns about the performance impact of this are
probably very valid concerns, and I don't see how the new version of
the patch gets us much closer to solving them.

I am also not sure I understand how the backend_write_blocks column is
intended to work. The only call to pgstat_send_walwrites() is in
WalWriterMain, so where do the other backends report anything?

Also, if there's only ever one global set of counters (as opposed to
one per table, say) then why use the stats collector machinery for
this at all, vs. having a structure in shared memory that can be
updated directly? It seems like adding a lot of overhead for no
functional benefit.

--
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

#28Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Robert Haas (#27)
Re: [HACKERS] pg_stat_wal_write statistics view

On Wed, Nov 8, 2017 at 8:46 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Nov 7, 2017 at 4:31 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

Updated patch attached.

Patch rebased.

I think the earlier concerns about the performance impact of this are
probably very valid concerns, and I don't see how the new version of
the patch gets us much closer to solving them.

I will check the performance with the changes of removing the stats
collector
usage and provide the details.

I am also not sure I understand how the backend_write_blocks column is
intended to work. The only call to pgstat_send_walwrites() is in
WalWriterMain, so where do the other backends report anything?

With the current patch, All the backends update the stats in shared memory
structure and only WAL writer process gathers the stats and share with the
stats collector.

Also, if there's only ever one global set of counters (as opposed to
one per table, say) then why use the stats collector machinery for
this at all, vs. having a structure in shared memory that can be
updated directly? It seems like adding a lot of overhead for no
functional benefit.

Yes, I agree that using stats collector for these stats is an overhead.
I change the patch to use just the shared memory structure and
gather the performance results and post it to the next commitfest.

Currently I marked the patch as "returned with feedback" in the
ongoing commitfest.

Regards,
Hari Babu
Fujitsu Australia

#29Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Haribabu Kommi (#28)
1 attachment(s)
Re: [HACKERS] pg_stat_wal_write statistics view

On Mon, Nov 27, 2017 at 2:12 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Wed, Nov 8, 2017 at 8:46 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Nov 7, 2017 at 4:31 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

Updated patch attached.

Patch rebased.

I think the earlier concerns about the performance impact of this are
probably very valid concerns, and I don't see how the new version of
the patch gets us much closer to solving them.

I will check the performance with the changes of removing the stats
collector
usage and provide the details.

I ran the pgbench performance after removing stats collector usage and
moving
the stats into shared memory. I find it that this stats addition doesn't
have any
overhead while collecting the stats. I ran these tests on a small machine,
these may
need to be retested on an high end machine to confirm that there is no
impact.

Threads Clients HEAD PATCH Diff
1 1 88.72981533 88.527789 -0.23%
2 2 88.17999 88.823842 0.73%
4 4 169.430813 167.897937 -0.90%
8 8 311.790313 315.6659117 1.24%
16 16 558.6014777 562.5883583 0.71%
32 32 874.0996587 899.855634 2.95%

Attached is the updated patch accordingly to use the shared memory stats.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_stat_walwrites-statistics-view_v11.patchapplication/octet-stream; name=pg_stat_walwrites-statistics-view_v11.patchDownload
From be6bcf386e92a8b92da3da2c4c59792e9d0e01a4 Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Tue, 12 Dec 2017 20:14:16 +1100
Subject: [PATCH] pg_stat_walwrites statistics view

This view will provide the details of how many blocks,
writes and the time taken to write the WAL data to disk
by the backend, walwriter and other background process.
Based on this data, it is configure walwriter to reduce
the load on the backend processes, thus it should improve
the performance.

All the stats are collected and stored in a shared memory
strucuture protected by the WALWriteLock
---
 doc/src/sgml/monitoring.sgml           | 86 ++++++++++++++++++++++++++++++++++
 src/backend/access/transam/xlog.c      | 62 ++++++++++++++++++++++++
 src/backend/catalog/system_views.sql   |  3 ++
 src/backend/postmaster/autovacuum.c    |  1 -
 src/backend/postmaster/pgstat.c        | 48 ++++++++++++++++++-
 src/backend/storage/ipc/ipci.c         |  2 +
 src/backend/utils/adt/pgstatfuncs.c    | 36 ++++++++++++++
 src/backend/utils/init/globals.c       |  1 +
 src/include/catalog/pg_proc.h          |  3 ++
 src/include/miscadmin.h                |  2 +
 src/include/pgstat.h                   | 47 +++++++++++++++++++
 src/test/regress/expected/rules.out    | 10 ++++
 src/test/regress/expected/sysviews.out |  7 +++
 src/test/regress/sql/sysviews.sql      |  3 ++
 14 files changed, 309 insertions(+), 2 deletions(-)

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index b6f80d9708..dd2c2cec7e 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -364,6 +364,14 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
      </entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_walwrites</structname><indexterm><primary>pg_stat_walwrites</primary></indexterm></entry>
+      <entry>One row only, showing statistics about the
+       WAL writing activity. See
+       <xref linkend="pg-stat-walwrites-view"/> for details.
+      </entry>
+     </row>
+
      <row>
       <entry><structname>pg_stat_database</structname><indexterm><primary>pg_stat_database</primary></indexterm></entry>
       <entry>One row per database, showing database-wide statistics. See
@@ -2267,6 +2275,82 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
    single row, containing global data for the cluster.
   </para>
 
+  <table id="pg-stat-walwrites-view" xreflabel="pg_stat_walwrites">
+   <title><structname>pg_stat_walwrites</structname> View</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>writes</structfield></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of WAL writes that are carried out by background processes and workers. 
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>walwriter_writes</structfield></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL writes that are carried out by the wal writer process</entry>
+     </row>
+     <row>
+      <entry><structfield>backend_writes</structfield></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL writes that are carried out by the backend processes</entry>
+     </row>
+     <row>
+      <entry><structfield>dirty_writes</structfield></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by background processes and workers
+      when the <xref linkend="guc-wal-buffers"/> are full.
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>backend_dirty_writes</structfield></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+      Number of dirty WAL writes that are carried out by the backend processes when 
+      the <xref linkend="guc-wal-buffers"/> are full. 
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>write_blocks</structfield></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the background processes/workers</entry>
+     </row>
+     <row>
+      <entry><structfield>walwriter_write_blocks</structfield></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the wal writer process</entry>
+     </row>
+     <row>
+      <entry><structfield>backend_write_blocks</structfield></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Number of WAL pages written to the disk by the backend processes</entry>
+     </row>
+     <row>
+      <entry><structfield>stats_reset</structfield></entry>
+      <entry><type>timestamp with time zone</type></entry>
+      <entry>Time at which these statistics were last reset</entry>
+     </row>
+     </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_walwrites</structname> view will always have a
+   single row, containing data about the WAL writing activity of the cluster.
+  </para>
+
+
   <table id="pg-stat-database-view" xreflabel="pg_stat_database">
    <title><structname>pg_stat_database</structname> View</title>
    <tgroup cols="3">
@@ -3046,6 +3130,8 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
        counters shown in the <structname>pg_stat_bgwriter</structname> view.
        Calling <literal>pg_stat_reset_shared('archiver')</literal> will zero all the
        counters shown in the <structname>pg_stat_archiver</structname> view.
+       Calling <literal>pg_stat_reset_shared('walwrites')</literal> will zero all the
+       counters shown in the <structname>pg_stat_walwrites</structname> view.
       </entry>
      </row>
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 0791404263..54f2b1448f 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -866,6 +866,7 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void);
 
 static void AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic);
 static bool XLogCheckpointNeeded(XLogSegNo new_segno);
+static bool am_background_process(void);
 static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible);
 static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath,
 					   bool find_free, XLogSegNo max_segno,
@@ -2118,6 +2119,17 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic)
 					WriteRqst.Write = OldPageRqstPtr;
 					WriteRqst.Flush = 0;
 					XLogWrite(WriteRqst, false);
+					if (AmWalWriterProcess())
+					{
+						/*
+						 * Don't consider the writes of wal writer process as
+						 * dirty writes, so skipping.
+						 */
+					}
+					else if (am_background_process())
+						WALWriteStats->stats.dirty_writes++;
+					else
+						WALWriteStats->stats.backend_dirty_writes++;
 					LWLockRelease(WALWriteLock);
 					TRACE_POSTGRESQL_WAL_BUFFER_WRITE_DIRTY_DONE();
 				}
@@ -2321,6 +2333,33 @@ XLogCheckpointNeeded(XLogSegNo new_segno)
 	return false;
 }
 
+/*
+ * Check whether the current process is a background process/worker
+ * or not. This function checks for the background processes that
+ * does some WAL write activity only and other background processes
+ * are not considered. It considers all the background workers
+ * as WAL write activity workers.
+ *
+ * Returns false - when the current process is a normal backend
+ *		   true - when the current process a background process/worker
+ */
+static bool
+am_background_process()
+{
+	/* check whether current process is a background process/worker? */
+	if (!AmBackgroundWriterProcess() &&
+		!AmCheckpointerProcess() &&
+		!AmStartupProcess() &&
+		!IsBackgroundWorker &&
+		!am_walsender &&
+		!am_autovacuum_worker)
+	{
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * Write and/or fsync the log at least as far as WriteRqst indicates.
  *
@@ -2344,6 +2383,9 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 	int			npages;
 	int			startidx;
 	uint32		startoffset;
+	PgStat_Counter writes = 0;
+	PgStat_Counter write_blocks = 0;
+	bool		is_background_process = am_background_process();
 
 	/* We should always be inside a critical section here */
 	Assert(CritSectionCount > 0);
@@ -2487,6 +2529,10 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 				from += written;
 			} while (nleft > 0);
 
+			/* check whether writer is a normal backend or not? */
+			writes++;
+			write_blocks += npages;
+
 			/* Update state for write */
 			openLogOff += nbytes;
 			npages = 0;
@@ -2586,6 +2632,22 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible)
 		LogwrtResult.Flush = LogwrtResult.Write;
 	}
 
+	if (is_background_process)
+	{
+		WALWriteStats->stats.writes += writes;
+		WALWriteStats->stats.write_blocks += write_blocks;
+	}
+	else if (AmWalWriterProcess())
+	{
+		WALWriteStats->stats.walwriter_writes += writes;
+		WALWriteStats->stats.walwriter_write_blocks += write_blocks;
+	}
+	else
+	{
+		WALWriteStats->stats.backend_writes += writes;
+		WALWriteStats->stats.backend_write_blocks += write_blocks;
+	}
+
 	/*
 	 * Update shared-memory status
 	 *
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 394aea8e0f..9843db1c8b 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -881,6 +881,9 @@ CREATE VIEW pg_stat_bgwriter AS
         pg_stat_get_buf_alloc() AS buffers_alloc,
         pg_stat_get_bgwriter_stat_reset_time() AS stats_reset;
 
+CREATE VIEW pg_stat_walwrites AS
+   SELECT * FROM pg_stat_get_walwrites() AS A;
+
 CREATE VIEW pg_stat_progress_vacuum AS
 	SELECT
 		S.pid AS pid, S.datid AS datid, D.datname AS datname,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 48765bb01b..5fbcbcc0a9 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -136,7 +136,6 @@ int			Log_autovacuum_min_duration = -1;
 
 /* Flags to tell if we are in an autovacuum process */
 static bool am_autovacuum_launcher = false;
-static bool am_autovacuum_worker = false;
 
 /* Flags set by signal handlers */
 static volatile sig_atomic_t got_SIGHUP = false;
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 5c256ff8ab..202ea9c355 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -142,6 +142,15 @@ char	   *pgstat_stat_tmpname = NULL;
  */
 PgStat_MsgBgWriter BgWriterStats;
 
+/*
+ * WalWrites Local statistics counters.
+ * The statistics data gets populated in XLogWrite function.
+ * Stored directly in a stats message structure so it can be sent
+ * to stats collector process without needing to copy things around.
+ * We assume this inits to zeroes.
+ */
+PgStat_WalWritesStats *WALWriteStats;
+
 /* ----------
  * Local data
  * ----------
@@ -630,6 +639,29 @@ startup_failed:
 	SetConfigOption("track_counts", "off", PGC_INTERNAL, PGC_S_OVERRIDE);
 }
 
+/*
+ * Initialization of shared memory for WALWritesStats
+ */
+Size
+WALWritesShmemSize(void)
+{
+	return sizeof(PgStat_WalWritesStats);
+}
+
+void
+WALWritesShmemInit(void)
+{
+	bool		foundWALWrites;
+
+	WALWriteStats = (PgStat_WalWritesStats *)
+		ShmemInitStruct("WAL WriteStats", WALWritesShmemSize(), &foundWALWrites);
+
+	if (!foundWALWrites)
+	{
+		MemSet(WALWriteStats, 0, sizeof(PgStat_WalWritesStats));
+	}
+}
+
 /*
  * subroutine for pgstat_reset_all
  */
@@ -1336,11 +1368,25 @@ pgstat_reset_shared_counters(const char *target)
 		msg.m_resettarget = RESET_ARCHIVER;
 	else if (strcmp(target, "bgwriter") == 0)
 		msg.m_resettarget = RESET_BGWRITER;
+	else if (strcmp(target, "walwrites") == 0)
+	{
+		/*
+		 * Reset the wal writes statistics of the cluster. These statistics
+		 * are not reset by the stats collector because these are resides in a
+		 * shared memory, so it is not possible for the stats collector to
+		 * reset them. FIXME: This may need a sepearate function entirely to
+		 * reset the stats.
+		 */
+		LWLockAcquire(WALWriteLock, LW_EXCLUSIVE);
+		memset(WALWriteStats, 0, sizeof(PgStat_WalWritesStats));
+		WALWriteStats->stat_reset_timestamp = GetCurrentTimestamp();
+		LWLockRelease(WALWriteLock);
+	}
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("unrecognized reset target: \"%s\"", target),
-				 errhint("Target must be \"archiver\" or \"bgwriter\".")));
+				 errhint("Target must be \"archiver\" or \"bgwriter\" or \"walwrites\".")));
 
 	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER);
 	pgstat_send(&msg, sizeof(msg));
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2d1ed143e0..d9e244b5f4 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -150,6 +150,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
 		size = add_size(size, SyncScanShmemSize());
 		size = add_size(size, AsyncShmemSize());
 		size = add_size(size, BackendRandomShmemSize());
+		size = add_size(size, WALWritesShmemSize());
 #ifdef EXEC_BACKEND
 		size = add_size(size, ShmemBackendArraySize());
 #endif
@@ -218,6 +219,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
 	 * Set up xlog, clog, and buffers
 	 */
 	XLOGShmemInit();
+	WALWritesShmemInit();
 	CLOGShmemInit();
 	CommitTsShmemInit();
 	SUBTRANSShmemInit();
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 04cf209b5b..08dd1533d4 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1882,3 +1882,39 @@ pg_stat_get_archiver(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(HeapTupleGetDatum(
 									  heap_form_tuple(tupdesc, values, nulls)));
 }
+
+Datum
+pg_stat_get_walwrites(PG_FUNCTION_ARGS)
+{
+	TupleDesc	tupdesc;
+#define NUM_PG_STAT_WALWRITE_COLS 9
+	Datum		values[NUM_PG_STAT_WALWRITE_COLS];
+	bool		nulls[NUM_PG_STAT_WALWRITE_COLS];
+
+	/* Initialize values and NULL flags arrays */
+	MemSet(values, 0, sizeof(values));
+	MemSet(nulls, 0, sizeof(nulls));
+
+	/* 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");
+
+	LWLockAcquire(WALWriteLock, LW_EXCLUSIVE);
+
+	/* Get statistics about the archiver process */
+	/* Fill values and NULLs */
+	values[0] = Int64GetDatum(WALWriteStats->stats.writes);
+	values[1] = Int64GetDatum(WALWriteStats->stats.walwriter_writes);
+	values[2] = Int64GetDatum(WALWriteStats->stats.backend_writes);
+	values[3] = Int64GetDatum(WALWriteStats->stats.dirty_writes);
+	values[4] = Int64GetDatum(WALWriteStats->stats.backend_dirty_writes);
+	values[5] = Int64GetDatum(WALWriteStats->stats.write_blocks);
+	values[6] = Int64GetDatum(WALWriteStats->stats.walwriter_write_blocks);
+	values[7] = Int64GetDatum(WALWriteStats->stats.backend_write_blocks);
+	values[8] = TimestampTzGetDatum(WALWriteStats->stat_reset_timestamp);
+
+	LWLockRelease(WALWriteLock);
+	/* Returns the record as Datum */
+	PG_RETURN_DATUM(HeapTupleGetDatum(
+									  heap_form_tuple(tupdesc, values, nulls)));
+}
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 9680a4b0f7..317be66ab3 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -101,6 +101,7 @@ bool		IsPostmasterEnvironment = false;
 bool		IsUnderPostmaster = false;
 bool		IsBinaryUpgrade = false;
 bool		IsBackgroundWorker = false;
+bool		am_autovacuum_worker = false;
 
 bool		ExitOnAnyError = false;
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c969375981..a0bb323e1d 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2980,6 +2980,9 @@ DESCR("statistics: number of backend buffer writes that did their own fsync");
 DATA(insert OID = 2859 ( pg_stat_get_buf_alloc			PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_buf_alloc _null_ _null_ _null_ ));
 DESCR("statistics: number of buffer allocations");
 
+DATA(insert OID = 3998 (  pg_stat_get_walwrites		PGNSP PGUID 12 1 0 0 0 f f f f f f s r 0 0 2249 "" "{20,20,20,20,20,20,20,20,1184}" "{o,o,o,o,o,o,o,o,o}" "{writes,walwriter_writes,backend_writes,dirty_writes,backend_dirty_writes,write_blocks,walwriter_write_blocks,backend_write_blocks,stats_reset}" _null_ _null_ pg_stat_get_walwrites _null_ _null_ _null_ ));
+DESCR("statistics: information about WAL writes activity");
+
 DATA(insert OID = 2978 (  pg_stat_get_function_calls		PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_calls _null_ _null_ _null_ ));
 DESCR("statistics: number of function calls");
 DATA(insert OID = 2979 (  pg_stat_get_function_total_time	PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 701 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_function_total_time _null_ _null_ _null_ ));
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 59da7a6091..220a23e8e2 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -242,6 +242,8 @@ extern PGDLLIMPORT bool allowSystemTableMods;
 extern PGDLLIMPORT int work_mem;
 extern PGDLLIMPORT int maintenance_work_mem;
 
+extern bool am_autovacuum_worker;
+
 extern int	VacuumCostPageHit;
 extern int	VacuumCostPageMiss;
 extern int	VacuumCostPageDirty;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 089b7c3a10..39f04dbde3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -422,6 +422,37 @@ typedef struct PgStat_MsgBgWriter
 	PgStat_Counter m_checkpoint_sync_time;
 } PgStat_MsgBgWriter;
 
+/*
+ * Walwrites statistics counters
+ */
+typedef struct PgStat_WalWritesCounts
+{
+	PgStat_Counter writes;		/* No of writes by background
+								 * processes/workers */
+	PgStat_Counter walwriter_writes;	/* No of writes by walwriter */
+	PgStat_Counter backend_writes;	/* No of writes by backends */
+	PgStat_Counter dirty_writes;	/* No of dirty writes by background
+									 * processes/workers when WAL buffers full */
+	PgStat_Counter backend_dirty_writes;	/* No of dirty writes by backends
+											 * when WAL buffers full */
+	PgStat_Counter write_blocks;	/* Total no of pages written by background
+									 * processes/workers */
+	PgStat_Counter walwriter_write_blocks;	/* Total no of pages written by
+											 * walwriter */
+	PgStat_Counter backend_write_blocks;	/* Total no of pages written by
+											 * backends */
+} PgStat_WalWritesCounts;
+
+/* ----------
+ * PgStat_MsgWalWrites			Sent by the walwriter after collecting all shared stats
+ * ----------
+ */
+typedef struct PgStat_MsgWalWrites
+{
+	PgStat_MsgHdr m_hdr;
+	PgStat_WalWritesCounts stats;
+} PgStat_MsgWalWrites;
+
 /* ----------
  * PgStat_MsgRecoveryConflict	Sent by the backend upon recovery conflict
  * ----------
@@ -694,6 +725,14 @@ typedef struct PgStat_GlobalStats
 	TimestampTz stat_reset_timestamp;
 } PgStat_GlobalStats;
 
+/*
+ * Walwrites statistics kept in the stats collector
+ */
+typedef struct PgStat_WalWritesStats
+{
+	PgStat_WalWritesCounts stats;
+	TimestampTz stat_reset_timestamp;	/* Last time when the stats reset */
+}			PgStat_WalWritesStats;
 
 /* ----------
  * Backend types
@@ -1125,6 +1164,11 @@ extern char *pgstat_stat_filename;
  */
 extern PgStat_MsgBgWriter BgWriterStats;
 
+/*
+ * Wal writes statistics updated in XLogWrite function
+ */
+extern PgStat_WalWritesStats * WALWriteStats;
+
 /*
  * Updated by pgstat_count_buffer_*_time macros
  */
@@ -1143,6 +1187,9 @@ extern int	pgstat_start(void);
 extern void pgstat_reset_all(void);
 extern void allow_immediate_pgstat_restart(void);
 
+extern Size WALWritesShmemSize(void);
+extern void WALWritesShmemInit(void);
+
 #ifdef EXEC_BACKEND
 extern void PgstatCollectorMain(int argc, char *argv[]) pg_attribute_noreturn();
 #endif
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f1c1b44d6f..6a84e3f980 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1973,6 +1973,16 @@ pg_stat_wal_receiver| SELECT s.pid,
     s.conninfo
    FROM pg_stat_get_wal_receiver() s(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, conninfo)
   WHERE (s.pid IS NOT NULL);
+pg_stat_walwrites| SELECT a.writes,
+    a.walwriter_writes,
+    a.backend_writes,
+    a.dirty_writes,
+    a.backend_dirty_writes,
+    a.write_blocks,
+    a.walwriter_write_blocks,
+    a.backend_write_blocks,
+    a.stats_reset
+   FROM pg_stat_get_walwrites() a(writes, walwriter_writes, backend_writes, dirty_writes, backend_dirty_writes, write_blocks, walwriter_write_blocks, backend_write_blocks, stats_reset);
 pg_stat_xact_all_tables| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 2b738aae7c..89619bebaa 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -67,6 +67,13 @@ select count(*) >= 0 as ok from pg_prepared_xacts;
  t
 (1 row)
 
+-- There will surely and maximum one record
+select count(*) = 1 as ok from pg_stat_walwrites;
+ ok 
+----
+ t
+(1 row)
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 28e412b735..21f49c9a3b 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -32,6 +32,9 @@ select count(*) = 0 as ok from pg_prepared_statements;
 -- See also prepared_xacts.sql
 select count(*) >= 0 as ok from pg_prepared_xacts;
 
+-- There will surely and maximum one record
+select count(*) = 1 as ok from pg_stat_walwrites;
+
 -- This is to record the prevailing planner enable_foo settings during
 -- a regression test run.
 select name, setting from pg_settings where name like 'enable%';
-- 
2.15.0.windows.1

#30Robert Haas
robertmhaas@gmail.com
In reply to: Haribabu Kommi (#29)
Re: [HACKERS] pg_stat_wal_write statistics view

On Tue, Dec 12, 2017 at 4:18 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

I ran the pgbench performance after removing stats collector usage and
moving
the stats into shared memory. I find it that this stats addition doesn't
have any
overhead while collecting the stats. I ran these tests on a small machine,
these may
need to be retested on an high end machine to confirm that there is no
impact.

Threads Clients HEAD PATCH Diff
1 1 88.72981533 88.527789 -0.23%
2 2 88.17999 88.823842 0.73%
4 4 169.430813 167.897937 -0.90%
8 8 311.790313 315.6659117 1.24%
16 16 558.6014777 562.5883583 0.71%
32 32 874.0996587 899.855634 2.95%

Attached is the updated patch accordingly to use the shared memory stats.

I think the concerns have more to do with systems that have a huge
number of tables.

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