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

