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

