From f9c30e6e930eea2b48e6328bb8bdca919a905db6 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Tue, 13 Jan 2015 14:06:54 +0900
Subject: [PATCH] Make log_autovacuum_min_duration a relation option

This is useful to control autovacuum log spam on systems where monitoring
only a set of tables is important.
---
 doc/src/sgml/ref/create_table.sgml     | 17 ++++++++++++++---
 src/backend/access/common/reloptions.c | 10 ++++++++++
 src/backend/commands/analyze.c         | 12 ++++++------
 src/backend/commands/vacuum.c          |  2 +-
 src/backend/commands/vacuumlazy.c      |  8 ++++----
 src/backend/nodes/copyfuncs.c          |  1 +
 src/backend/nodes/equalfuncs.c         |  1 +
 src/backend/parser/gram.y              |  7 +++++++
 src/backend/postmaster/autovacuum.c    |  9 +++++++++
 src/bin/psql/tab-complete.c            |  2 ++
 src/include/nodes/parsenodes.h         |  2 ++
 src/include/utils/rel.h                |  1 +
 12 files changed, 58 insertions(+), 14 deletions(-)

diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 299cce8..15962aa 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -872,9 +872,11 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
     <literal>toast.</literal>, which can be used to control the behavior of the
     table's secondary <acronym>TOAST</> table, if any
     (see <xref linkend="storage-toast"> for more information about TOAST).
-    Note that the TOAST table inherits the
-    <literal>autovacuum_*</literal> values from its parent table, if there are
-    no <literal>toast.autovacuum_*</literal> settings set.
+    Note that the TOAST table inherits values of <literal>autovacuum_*</literal>
+    and <literal>log_autovacuum_min_duration</literal> from its parent table, if
+    there are no values set for respectively
+    <literal>toast.autovacuum_*</literal> and
+    <literal>toast.log_autovacuum_min_duration</literal>.
    </para>
 
    <variablelist>
@@ -1052,6 +1054,15 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
    </varlistentry>
 
    <varlistentry>
+    <term><literal>log_autovacuum_min_duration</literal>, <literal>toast.log_autovacuum_min_duration</literal> (<type>integer</type>)</term>
+    <listitem>
+     <para>
+      Custom <xref linkend="guc-log-autovacuum-min-duration"> parameter.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><literal>user_catalog_table</literal> (<type>boolean</type>)</term>
     <listitem>
      <para>
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index f008fab..8176b6a 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -211,6 +211,14 @@ static relopt_int intRelOpts[] =
 	},
 	{
 		{
+			"log_autovacuum_min_duration",
+			"Sets the minimum execution time above which autovacuum actions will be logged",
+			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
+		},
+		-1, -1, INT_MAX
+	},
+	{
+		{
 			"pages_per_range",
 			"Number of pages that each page range covers in a BRIN index",
 			RELOPT_KIND_BRIN
@@ -1210,6 +1218,8 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
 		offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, multixact_freeze_max_age)},
 		{"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
 		offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, multixact_freeze_table_age)},
+		{"log_autovacuum_min_duration", RELOPT_TYPE_INT,
+		offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, log_min_duration)},
 		{"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
 		offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_scale_factor)},
 		{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index d2856a3..e939d4e 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -151,7 +151,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
 	else
 	{
 		onerel = NULL;
-		if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+		if (IsAutoVacuumWorkerProcess() && vacstmt->log_min_duration >= 0)
 			ereport(LOG,
 					(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
 				  errmsg("skipping analyze of \"%s\" --- lock not available",
@@ -360,10 +360,10 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
 	save_nestlevel = NewGUCNestLevel();
 
 	/* measure elapsed time iff autovacuum logging requires it */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && vacstmt->log_min_duration >= 0)
 	{
 		pg_rusage_init(&ru0);
-		if (Log_autovacuum_min_duration > 0)
+		if (vacstmt->log_min_duration > 0)
 			starttime = GetCurrentTimestamp();
 	}
 
@@ -648,11 +648,11 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
 	vac_close_indexes(nindexes, Irel, NoLock);
 
 	/* Log the action if appropriate */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && vacstmt->log_min_duration >= 0)
 	{
-		if (Log_autovacuum_min_duration == 0 ||
+		if (vacstmt->log_min_duration == 0 ||
 			TimestampDifferenceExceeds(starttime, GetCurrentTimestamp(),
-									   Log_autovacuum_min_duration))
+									   vacstmt->log_min_duration))
 			ereport(LOG,
 					(errmsg("automatic analyze of table \"%s.%s.%s\" system usage: %s",
 							get_database_name(MyDatabaseId),
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 2f3f79d..1b91147 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -1190,7 +1190,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
 	else
 	{
 		onerel = NULL;
-		if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+		if (IsAutoVacuumWorkerProcess() && vacstmt->log_min_duration >= 0)
 			ereport(LOG,
 					(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
 				   errmsg("skipping vacuum of \"%s\" --- lock not available",
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 7d9e49e..afa7567 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -194,7 +194,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
 	MultiXactId new_min_multi;
 
 	/* measure elapsed time iff autovacuum logging requires it */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && vacstmt->log_min_duration >= 0)
 	{
 		pg_rusage_init(&ru0);
 		starttime = GetCurrentTimestamp();
@@ -325,13 +325,13 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
 						 vacrelstats->new_dead_tuples);
 
 	/* and log the action if appropriate */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && vacstmt->log_min_duration >= 0)
 	{
 		TimestampTz endtime = GetCurrentTimestamp();
 
-		if (Log_autovacuum_min_duration == 0 ||
+		if (vacstmt->log_min_duration == 0 ||
 			TimestampDifferenceExceeds(starttime, endtime,
-									   Log_autovacuum_min_duration))
+									   vacstmt->log_min_duration))
 		{
 			StringInfoData	buf;
 			TimestampDifference(starttime, endtime, &secs, &usecs);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 9fe8008..a0053ab 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3296,6 +3296,7 @@ _copyVacuumStmt(const VacuumStmt *from)
 	COPY_SCALAR_FIELD(freeze_table_age);
 	COPY_SCALAR_FIELD(multixact_freeze_min_age);
 	COPY_SCALAR_FIELD(multixact_freeze_table_age);
+	COPY_SCALAR_FIELD(log_min_duration);
 	COPY_NODE_FIELD(relation);
 	COPY_NODE_FIELD(va_cols);
 
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index fe509b0..c21b8e3 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1511,6 +1511,7 @@ _equalVacuumStmt(const VacuumStmt *a, const VacuumStmt *b)
 	COMPARE_SCALAR_FIELD(freeze_table_age);
 	COMPARE_SCALAR_FIELD(multixact_freeze_min_age);
 	COMPARE_SCALAR_FIELD(multixact_freeze_table_age);
+	COMPARE_SCALAR_FIELD(log_min_duration);
 	COMPARE_NODE_FIELD(relation);
 	COMPARE_NODE_FIELD(va_cols);
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 581f7a1..4e81cc6 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -9074,6 +9074,7 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 					n->freeze_table_age = $3 ? 0 : -1;
 					n->multixact_freeze_min_age = $3 ? 0 : -1;
 					n->multixact_freeze_table_age = $3 ? 0 : -1;
+					n->log_min_duration = -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9090,6 +9091,7 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 					n->freeze_table_age = $3 ? 0 : -1;
 					n->multixact_freeze_min_age = $3 ? 0 : -1;
 					n->multixact_freeze_table_age = $3 ? 0 : -1;
+					n->log_min_duration = -1;
 					n->relation = $5;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9106,6 +9108,7 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 					n->freeze_table_age = $3 ? 0 : -1;
 					n->multixact_freeze_min_age = $3 ? 0 : -1;
 					n->multixact_freeze_table_age = $3 ? 0 : -1;
+					n->log_min_duration = -1;
 					$$ = (Node *)n;
 				}
 			| VACUUM '(' vacuum_option_list ')'
@@ -9124,6 +9127,7 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->multixact_freeze_min_age = -1;
 						n->multixact_freeze_table_age = -1;
 					}
+					n->log_min_duration = -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *) n;
@@ -9144,6 +9148,7 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->multixact_freeze_min_age = -1;
 						n->multixact_freeze_table_age = -1;
 					}
+					n->log_min_duration = -1;
 					n->relation = $5;
 					n->va_cols = $6;
 					if (n->va_cols != NIL)	/* implies analyze */
@@ -9175,6 +9180,7 @@ AnalyzeStmt:
 					n->freeze_table_age = -1;
 					n->multixact_freeze_min_age = -1;
 					n->multixact_freeze_table_age = -1;
+					n->log_min_duration = -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9189,6 +9195,7 @@ AnalyzeStmt:
 					n->freeze_table_age = -1;
 					n->multixact_freeze_min_age = -1;
 					n->multixact_freeze_table_age = -1;
+					n->log_min_duration = -1;
 					n->relation = $3;
 					n->va_cols = $4;
 					$$ = (Node *)n;
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 77158c1..b02c2fc 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -192,6 +192,7 @@ typedef struct autovac_table
 	int			at_multixact_freeze_table_age;
 	int			at_vacuum_cost_delay;
 	int			at_vacuum_cost_limit;
+	int			at_log_min_duration;
 	bool		at_dobalance;
 	bool		at_wraparound;
 	char	   *at_relname;
@@ -2485,6 +2486,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 		int			multixact_freeze_table_age;
 		int			vac_cost_limit;
 		int			vac_cost_delay;
+		int			log_min_duration;
 
 		/*
 		 * Calculate the vacuum cost parameters and the freeze ages.  If there
@@ -2507,6 +2509,11 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 			? autovacuum_vac_cost_limit
 			: VacuumCostLimit;
 
+		/* -1 in autovac setting means using log_autovacuum_min_duration */
+		log_min_duration = (avopts && avopts->log_min_duration >= 0)
+			? avopts->log_min_duration
+			: Log_autovacuum_min_duration;
+
 		/* these do not have autovacuum-specific settings */
 		freeze_min_age = (avopts && avopts->freeze_min_age >= 0)
 			? avopts->freeze_min_age
@@ -2537,6 +2544,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 		tab->at_vacuum_cost_limit = vac_cost_limit;
 		tab->at_vacuum_cost_delay = vac_cost_delay;
 		tab->at_wraparound = wraparound;
+		tab->at_log_min_duration = log_min_duration;
 		tab->at_relname = NULL;
 		tab->at_nspname = NULL;
 		tab->at_datname = NULL;
@@ -2762,6 +2770,7 @@ autovacuum_do_vac_analyze(autovac_table *tab,
 	vacstmt.freeze_table_age = tab->at_freeze_table_age;
 	vacstmt.multixact_freeze_min_age = tab->at_multixact_freeze_min_age;
 	vacstmt.multixact_freeze_table_age = tab->at_multixact_freeze_table_age;
+	vacstmt.log_min_duration = tab->at_log_min_duration;
 	/* we pass the OID, but might need this anyway for an error message */
 	vacstmt.relation = &rangevar;
 	vacstmt.va_cols = NIL;
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index e39a07c..d43c114 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1788,6 +1788,7 @@ psql_completion(const char *text, int start, int end)
 			"autovacuum_vacuum_scale_factor",
 			"autovacuum_vacuum_threshold",
 			"fillfactor",
+			"log_autovacuum_min_duration",
 			"toast.autovacuum_enabled",
 			"toast.autovacuum_freeze_max_age",
 			"toast.autovacuum_freeze_min_age",
@@ -1799,6 +1800,7 @@ psql_completion(const char *text, int start, int end)
 			"toast.autovacuum_vacuum_cost_limit",
 			"toast.autovacuum_vacuum_scale_factor",
 			"toast.autovacuum_vacuum_threshold",
+			"toast.log_autovacuum_min_duration",
 			"user_catalog_table",
 			NULL
 		};
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ac13302..7e129ed 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2623,6 +2623,8 @@ typedef struct VacuumStmt
 												 * or -1 to use default */
 	int			multixact_freeze_table_age;		/* multixact age at which to
 												 * scan whole table */
+	int			log_min_duration;		/* minimum threshold at which vacuum
+										 * or analyze activity is logged */
 	RangeVar   *relation;		/* single table to process, or NULL */
 	List	   *va_cols;		/* list of column names, or NIL for all */
 } VacuumStmt;
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 6bd786d..9e17d87 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -209,6 +209,7 @@ typedef struct AutoVacOpts
 	int			multixact_freeze_min_age;
 	int			multixact_freeze_max_age;
 	int			multixact_freeze_table_age;
+	int			log_min_duration;
 	float8		vacuum_scale_factor;
 	float8		analyze_scale_factor;
 } AutoVacOpts;
-- 
2.3.1

