From e1e1d6d38895151d287515dafb9d9ee8f8cc912e Mon Sep 17 00:00:00 2001
From: Julien Rouhaud <julien.rouhaud@free.fr>
Date: Wed, 4 Mar 2020 20:09:22 +0100
Subject: [PATCH v5 3/3] Keep track of (auto)vacuum WAL usage in
 pg_stat_database.

---
 src/backend/catalog/system_views.sql |  6 ++
 src/backend/commands/vacuum.c        | 11 ++++
 src/backend/postmaster/pgstat.c      | 56 ++++++++++++++++
 src/backend/utils/adt/pgstatfuncs.c  | 96 ++++++++++++++++++++++++++++
 src/include/catalog/pg_proc.dat      | 24 +++++++
 src/include/pgstat.h                 | 27 +++++++-
 src/test/regress/expected/rules.out  |  6 ++
 7 files changed, 225 insertions(+), 1 deletion(-)

diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index b8a3f46912..a3d1ac2523 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -892,6 +892,12 @@ CREATE VIEW pg_stat_database AS
             pg_stat_get_db_checksum_last_failure(D.oid) AS checksum_last_failure,
             pg_stat_get_db_blk_read_time(D.oid) AS blk_read_time,
             pg_stat_get_db_blk_write_time(D.oid) AS blk_write_time,
+            pg_stat_get_db_vac_wal_records(D.oid) AS vac_wal_records,
+            pg_stat_get_db_vac_wal_fp_records(D.oid) AS vac_wal_fp_records,
+            pg_stat_get_db_vac_wal_bytes(D.oid) AS vac_wal_bytes,
+            pg_stat_get_db_autovac_wal_records(D.oid) AS autovac_wal_records,
+            pg_stat_get_db_autovac_wal_fp_records(D.oid) AS autovac_wal_fp_records,
+            pg_stat_get_db_autovac_wal_bytes(D.oid) AS autovac_wal_bytes,
             pg_stat_get_db_stat_reset_time(D.oid) AS stats_reset
     FROM (
         SELECT 0 AS oid, NULL::name AS datname
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index d625d17bf4..14a235a2ed 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -275,6 +275,8 @@ vacuum(List *relations, VacuumParams *params,
 	   BufferAccessStrategy bstrategy, bool isTopLevel)
 {
 	static bool in_vacuum = false;
+	WalUsage walusage_start = pgWalUsage;
+	WalUsage walusage = {0, 0, 0};
 
 	const char *stmttype;
 	volatile bool in_outer_xact,
@@ -489,6 +491,15 @@ vacuum(List *relations, VacuumParams *params,
 	{
 		in_vacuum = false;
 		VacuumCostActive = false;
+		walusage.wal_records = pgWalUsage.wal_records -
+			walusage_start.wal_records;
+		walusage.wal_fp_records = pgWalUsage.wal_fp_records -
+			walusage_start.wal_fp_records;
+		walusage.wal_bytes = pgWalUsage.wal_bytes - walusage_start.wal_bytes;
+		pgstat_report_vac_wal_usage(walusage.wal_records,
+									walusage.wal_fp_records,
+									walusage.wal_bytes,
+									IsAutoVacuumWorkerProcess());
 	}
 	PG_END_TRY();
 
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index f9287b7942..c9d65669ce 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -330,6 +330,7 @@ static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int le
 static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
 static void pgstat_recv_checksum_failure(PgStat_MsgChecksumFailure *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
+static void pgstat_recv_vac_walusage(PgStat_MsgVacWalUsage *msg, int len);
 
 /* ------------------------------------------------------------
  * Public functions called from postmaster follow
@@ -1572,6 +1573,30 @@ pgstat_report_tempfile(size_t filesize)
 	pgstat_send(&msg, sizeof(msg));
 }
 
+/* --------
+ * pgstat_report_vac_wal_usage() -
+ *
+ *	Tell the collector about (auto)vacuum WAL usage.
+ * --------
+ */
+void
+pgstat_report_vac_wal_usage(long wal_records, long wal_fp_records,
+							long wal_bytes, bool autovacuum)
+{
+	PgStat_MsgVacWalUsage msg;
+
+	if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
+		return;
+
+	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACWALUSAGE);
+	msg.m_databaseid = MyDatabaseId;
+	msg.m_wal_records = wal_records;
+	msg.m_wal_fp_records = wal_fp_records;
+	msg.m_wal_bytes = wal_bytes;
+	msg.m_autovacuum = autovacuum;
+	pgstat_send(&msg, sizeof(msg));
+}
+
 
 /* ----------
  * pgstat_ping() -
@@ -4525,6 +4550,10 @@ PgstatCollectorMain(int argc, char *argv[])
 					pgstat_recv_tempfile(&msg.msg_tempfile, len);
 					break;
 
+				case PGSTAT_MTYPE_VACWALUSAGE:
+					pgstat_recv_vac_walusage(&msg.msg_vac_walusage, len);
+					break;
+
 				case PGSTAT_MTYPE_CHECKSUMFAILURE:
 					pgstat_recv_checksum_failure(&msg.msg_checksumfailure,
 												 len);
@@ -6282,6 +6311,33 @@ pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len)
 	dbentry->n_temp_files += 1;
 }
 
+/* ----------
+ * pgstat_recv_vac_walusage() -
+ *
+ *	Process a VACWALUSAGE message.
+ * ----------
+ */
+static void
+pgstat_recv_vac_walusage(PgStat_MsgVacWalUsage *msg, int len)
+{
+	PgStat_StatDBEntry *dbentry;
+
+	dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
+
+	if (msg->m_autovacuum)
+	{
+		dbentry->n_autovac_wal_records += msg->m_wal_records;
+		dbentry->n_autovac_wal_fp_records += msg->m_wal_fp_records;
+		dbentry->autovac_wal_bytes += msg->m_wal_bytes;
+	}
+	else
+	{
+		dbentry->n_vac_wal_records += msg->m_wal_records;
+		dbentry->n_vac_wal_fp_records += msg->m_wal_fp_records;
+		dbentry->vac_wal_bytes += msg->m_wal_bytes;
+	}
+}
+
 /* ----------
  * pgstat_recv_funcstat() -
  *
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index cea01534a5..1bc7291c2b 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1606,6 +1606,102 @@ pg_stat_get_db_blk_write_time(PG_FUNCTION_ARGS)
 	PG_RETURN_FLOAT8(result);
 }
 
+Datum
+pg_stat_get_db_vac_wal_records(PG_FUNCTION_ARGS)
+{
+	Oid			dbid = PG_GETARG_OID(0);
+	int64		result;
+	PgStat_StatDBEntry *dbentry;
+
+	/* convert counter from microsec to millisec for display */
+	if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL)
+		result = 0;
+	else
+		result = (int64) (dbentry->n_vac_wal_records);
+
+	PG_RETURN_INT64(result);
+}
+
+Datum
+pg_stat_get_db_vac_wal_fp_records(PG_FUNCTION_ARGS)
+{
+	Oid			dbid = PG_GETARG_OID(0);
+	int64		result;
+	PgStat_StatDBEntry *dbentry;
+
+	/* convert counter from microsec to millisec for display */
+	if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL)
+		result = 0;
+	else
+		result = (int64) (dbentry->n_vac_wal_fp_records);
+
+	PG_RETURN_INT64(result);
+}
+
+Datum
+pg_stat_get_db_vac_wal_bytes(PG_FUNCTION_ARGS)
+{
+	Oid			dbid = PG_GETARG_OID(0);
+	int64		result;
+	PgStat_StatDBEntry *dbentry;
+
+	/* convert counter from microsec to millisec for display */
+	if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL)
+		result = 0;
+	else
+		result = (int64) (dbentry->vac_wal_bytes);
+
+	PG_RETURN_INT64(result);
+}
+
+Datum
+pg_stat_get_db_autovac_wal_records(PG_FUNCTION_ARGS)
+{
+	Oid			dbid = PG_GETARG_OID(0);
+	int64		result;
+	PgStat_StatDBEntry *dbentry;
+
+	/* convert counter from microsec to millisec for display */
+	if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL)
+		result = 0;
+	else
+		result = (int64) (dbentry->n_autovac_wal_records);
+
+	PG_RETURN_INT64(result);
+}
+
+Datum
+pg_stat_get_db_autovac_wal_fp_records(PG_FUNCTION_ARGS)
+{
+	Oid			dbid = PG_GETARG_OID(0);
+	int64		result;
+	PgStat_StatDBEntry *dbentry;
+
+	/* convert counter from microsec to millisec for display */
+	if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL)
+		result = 0;
+	else
+		result = (int64) (dbentry->n_autovac_wal_fp_records);
+
+	PG_RETURN_INT64(result);
+}
+
+Datum
+pg_stat_get_db_autovac_wal_bytes(PG_FUNCTION_ARGS)
+{
+	Oid			dbid = PG_GETARG_OID(0);
+	int64		result;
+	PgStat_StatDBEntry *dbentry;
+
+	/* convert counter from microsec to millisec for display */
+	if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL)
+		result = 0;
+	else
+		result = (int64) (dbentry->autovac_wal_bytes);
+
+	PG_RETURN_INT64(result);
+}
+
 Datum
 pg_stat_get_bgwriter_timed_checkpoints(PG_FUNCTION_ARGS)
 {
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 7fb574f9dc..8b1c1487ca 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5372,6 +5372,30 @@
   proname => 'pg_stat_get_db_blk_write_time', provolatile => 's',
   proparallel => 'r', prorettype => 'float8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_db_blk_write_time' },
+{ oid => '8176', descr => 'statistics: number of vacuum wal records',
+  proname => 'pg_stat_get_db_vac_wal_records', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_db_vac_wal_records' },
+{ oid => '8177', descr => 'statistics: number of vacuum wal full page records',
+  proname => 'pg_stat_get_db_vac_wal_fp_records', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_db_vac_wal_fp_records' },
+{ oid => '8178', descr => 'statistics: number of vacuum wal bytes',
+  proname => 'pg_stat_get_db_vac_wal_bytes', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_db_vac_wal_bytes' },
+{ oid => '8179', descr => 'statistics: number of autovacuum wal records',
+  proname => 'pg_stat_get_db_autovac_wal_records', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_db_autovac_wal_records' },
+{ oid => '8180', descr => 'statistics: number of autovacuum wal full page records',
+  proname => 'pg_stat_get_db_autovac_wal_fp_records', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_db_autovac_wal_fp_records' },
+{ oid => '8181', descr => 'statistics: number of autovacuum wal bytes',
+  proname => 'pg_stat_get_db_autovac_wal_bytes', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_db_autovac_wal_bytes' },
 { oid => '3195', descr => 'statistics: information about WAL archiver',
   proname => 'pg_stat_get_archiver', proisstrict => 'f', provolatile => 's',
   proparallel => 'r', prorettype => 'record', proargtypes => '',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 1a19921f80..40fb97bdc9 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -65,7 +65,8 @@ typedef enum StatMsgType
 	PGSTAT_MTYPE_RECOVERYCONFLICT,
 	PGSTAT_MTYPE_TEMPFILE,
 	PGSTAT_MTYPE_DEADLOCK,
-	PGSTAT_MTYPE_CHECKSUMFAILURE
+	PGSTAT_MTYPE_CHECKSUMFAILURE,
+	PGSTAT_MTYPE_VACWALUSAGE
 } StatMsgType;
 
 /* ----------
@@ -544,6 +545,21 @@ typedef struct PgStat_MsgChecksumFailure
 	TimestampTz m_failure_time;
 } PgStat_MsgChecksumFailure;
 
+/* ----------
+ * PgStat_MsgVacWalUsage	Sent by the backend to tell the collector
+ *								about (auto)vacuum WAL usage.
+ * ----------
+ */
+typedef struct PgStat_MsgVacWalUsage
+{
+	PgStat_MsgHdr m_hdr;
+	Oid			m_databaseid;
+	long		m_wal_records;
+	long		m_wal_fp_records;
+	long		m_wal_bytes;
+	bool		m_autovacuum;
+} PgStat_MsgVacWalUsage;
+
 
 /* ----------
  * PgStat_Msg					Union over all possible messages.
@@ -571,6 +587,7 @@ typedef union PgStat_Msg
 	PgStat_MsgDeadlock msg_deadlock;
 	PgStat_MsgTempFile msg_tempfile;
 	PgStat_MsgChecksumFailure msg_checksumfailure;
+	PgStat_MsgVacWalUsage msg_vac_walusage;
 } PgStat_Msg;
 
 
@@ -613,6 +630,12 @@ typedef struct PgStat_StatDBEntry
 	TimestampTz last_checksum_failure;
 	PgStat_Counter n_block_read_time;	/* times in microseconds */
 	PgStat_Counter n_block_write_time;
+	PgStat_Counter n_vac_wal_records;
+	PgStat_Counter n_vac_wal_fp_records;
+	PgStat_Counter vac_wal_bytes;
+	PgStat_Counter n_autovac_wal_records;
+	PgStat_Counter n_autovac_wal_fp_records;
+	PgStat_Counter autovac_wal_bytes;
 
 	TimestampTz stat_reset_timestamp;
 	TimestampTz stats_timestamp;	/* time of db stats file update */
@@ -1261,6 +1284,8 @@ extern void pgstat_bestart(void);
 
 extern void pgstat_report_activity(BackendState state, const char *cmd_str);
 extern void pgstat_report_tempfile(size_t filesize);
+extern void pgstat_report_vac_wal_usage(long wal_records, long wal_fp_record,
+										long wal_bytes, bool autovacuum);
 extern void pgstat_report_appname(const char *appname);
 extern void pgstat_report_xact_timestamp(TimestampTz tstamp);
 extern const char *pgstat_get_wait_event(uint32 wait_event_info);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index c7304611c3..aa311cd594 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1833,6 +1833,12 @@ pg_stat_database| SELECT d.oid AS datid,
     pg_stat_get_db_checksum_last_failure(d.oid) AS checksum_last_failure,
     pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time,
     pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time,
+    pg_stat_get_db_vac_wal_records(d.oid) AS vac_wal_records,
+    pg_stat_get_db_vac_wal_fp_records(d.oid) AS vac_wal_fp_records,
+    pg_stat_get_db_vac_wal_bytes(d.oid) AS vac_wal_bytes,
+    pg_stat_get_db_autovac_wal_records(d.oid) AS autovac_wal_records,
+    pg_stat_get_db_autovac_wal_fp_records(d.oid) AS autovac_wal_fp_records,
+    pg_stat_get_db_autovac_wal_bytes(d.oid) AS autovac_wal_bytes,
     pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset
    FROM ( SELECT 0 AS oid,
             NULL::name AS datname
-- 
2.20.1

