Table-level log_autovacuum_min_duration

Started by Michael Paquierabout 11 years ago45 messages
#1Michael Paquier
michael.paquier@gmail.com

Hi all,

Today I spent a bit of time looking at the activity of autovacuum for
one table particularly bloated. log_autovacuum_min_duration was
enabled and set to a high value but even with that I had to deal with
some spam from the jobs of other tables. It would be cool to have the
possibility to do some filtering IMO. So, what about having a relopt
to control log_autovacuum_min_duration? RelationData->rd_options is
largely accessible for a given relation in the code paths of vacuum
and analyze.
Thoughts?
--
Michael

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

#2Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Michael Paquier (#1)
Re: Table-level log_autovacuum_min_duration

Michael Paquier wrote:

Hi all,

Today I spent a bit of time looking at the activity of autovacuum for
one table particularly bloated. log_autovacuum_min_duration was
enabled and set to a high value but even with that I had to deal with
some spam from the jobs of other tables. It would be cool to have the
possibility to do some filtering IMO. So, what about having a relopt
to control log_autovacuum_min_duration? RelationData->rd_options is
largely accessible for a given relation in the code paths of vacuum
and analyze.

+1

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

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

#3Michael Paquier
michael.paquier@gmail.com
In reply to: Alvaro Herrera (#2)
Re: Table-level log_autovacuum_min_duration

On Thu, Dec 18, 2014 at 11:15 PM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Michael Paquier wrote:

Hi all,

Today I spent a bit of time looking at the activity of autovacuum for
one table particularly bloated. log_autovacuum_min_duration was
enabled and set to a high value but even with that I had to deal with
some spam from the jobs of other tables. It would be cool to have the
possibility to do some filtering IMO. So, what about having a relopt
to control log_autovacuum_min_duration? RelationData->rd_options is
largely accessible for a given relation in the code paths of vacuum
and analyze.

+1

As long as I got this idea in mind, I looked at the code to see how
much it would be possible to tune log_autovacuum_min_duration in the
code paths of analyze and vacuum. First, I think that it would be good
to have parameters for both parent relations and their toast relation
similarly to the other parameters...

But now, here are some things to consider if we use directly the
reloptions available with RelationData:
- If the parameters toast.autovacuum_* are not set, toast relations
inherit values from their parent relation. Looking at autovacuum.c
which is the only place where autovacuum options are located, we keep
a hash table to save the mapping toast -> parent relation. Using that
it is easy to fetch for a given toast relation the relopts of its
parent. Note this is strictly located in the autovacuum code path, so
to let vacuum and analyze now the custom value of
log_autovacuum_min_duration, with the inheritance property kept, we
would need to pass some extra values from autovacuum to the calls of
vacuum().
- It would not be possible to log autovacuum and analyze being skipped
when lock is not available because in this case Relation cannot be
safely fetched, so there are no reltoptions directly available. This
is for example this kind of message:
skipping analyze of "foo" --- lock not available

Both those things could be solved by passing a value through
VacuumStmt, like what we do when passing a value for
multixact_freeze_min_age, or with an extra argument in vacuum() for
example. Now I am not sure if it is worth it, and we could live as
well with a parameter that do not support the inheritance parent
relation -> toast, so log value set for a toast relation and its
parent share no dependency. In short that's a trade between code
simplicity and usability.

Thoughts?
--
Michael

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

#4Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#3)
Re: Table-level log_autovacuum_min_duration

On Sat, Dec 20, 2014 at 9:17 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

But now, here are some things to consider if we use directly the
reloptions available with RelationData:
- If the parameters toast.autovacuum_* are not set, toast relations
inherit values from their parent relation. Looking at autovacuum.c
which is the only place where autovacuum options are located, we keep
a hash table to save the mapping toast -> parent relation. Using that
it is easy to fetch for a given toast relation the relopts of its
parent. Note this is strictly located in the autovacuum code path, so
to let vacuum and analyze now the custom value of
log_autovacuum_min_duration, with the inheritance property kept, we
would need to pass some extra values from autovacuum to the calls of
vacuum().
- It would not be possible to log autovacuum and analyze being skipped
when lock is not available because in this case Relation cannot be
safely fetched, so there are no reltoptions directly available. This
is for example this kind of message:
skipping analyze of "foo" --- lock not available

Both those things could be solved by passing a value through
VacuumStmt, like what we do when passing a value for
multixact_freeze_min_age, or with an extra argument in vacuum() for
example. Now I am not sure if it is worth it, and we could live as
well with a parameter that do not support the inheritance parent
relation -> toast, so log value set for a toast relation and its
parent share no dependency. In short that's a trade between code
simplicity and usability.

I'm not sure I follow all of the particulars of what you are asking
here, but in general I would say that you shouldn't hesitate to pass
more information down the call stack when that helps to obtain correct
behavior.

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

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

#5Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#3)
1 attachment(s)
Re: Table-level log_autovacuum_min_duration

On Sat, Dec 20, 2014 at 11:17 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Michael Paquier wrote:

Today I spent a bit of time looking at the activity of autovacuum for
one table particularly bloated. log_autovacuum_min_duration was
enabled and set to a high value but even with that I had to deal with
some spam from the jobs of other tables. It would be cool to have the
possibility to do some filtering IMO. So, what about having a relopt
to control log_autovacuum_min_duration? RelationData->rd_options is
largely accessible for a given relation in the code paths of vacuum
and analyze.

OK, instead of only words, attached is a patch adding relopts called
log_autovacuum_min_duration and toast.log_autovacuum_min_duration to
control the logging of autovacuum at relation-level. The default value
of those parameters is -1, meaning that in this case the global
log_autovacuum_min_duration is used to control the logs. The patch has
finished by being simpler than I though first by using VacuumStmt to
pass the relopts from autovacuum to the code paths of analyze and
vacuum. Note that this uses the same mechanisms as the other relopts,
meaning that the toast relations use the values of their parent tables
if it is defined.

I am adding it to the next CF.
--
Michael

Attachments:

20150113_autovacuum_log_relopts.patchtext/x-diff; charset=US-ASCII; name=20150113_autovacuum_log_relopts.patchDownload
From 4bdd3f0f9ef29334dff575d4fd2811d9d636adc8 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     |  9 +++++++++
 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/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 +
 10 files changed, 51 insertions(+), 11 deletions(-)

diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 299cce8..533a62f 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -1052,6 +1052,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..c16697a 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",
+			"Log autovacuum execution for given threshold",
+			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 5de2b39..7229b1b 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/parser/gram.y b/src/backend/parser/gram.y
index 36dac29..37d71c1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -9066,6 +9066,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 = $3 ? 0 : -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9082,6 +9083,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 = $3 ? 0 : -1;
 					n->relation = $5;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9098,6 +9100,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 = $3 ? 0 : -1;
 					$$ = (Node *)n;
 				}
 			| VACUUM '(' vacuum_option_list ')'
@@ -9109,12 +9112,14 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->freeze_min_age = n->freeze_table_age = 0;
 						n->multixact_freeze_min_age = 0;
 						n->multixact_freeze_table_age = 0;
+						n->log_min_duration = 0;
 					}
 					else
 					{
 						n->freeze_min_age = 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;
@@ -9129,12 +9134,14 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->freeze_min_age = n->freeze_table_age = 0;
 						n->multixact_freeze_min_age = 0;
 						n->multixact_freeze_table_age = 0;
+						n->log_min_duration = 0;
 					}
 					else
 					{
 						n->freeze_min_age = n->freeze_table_age = -1;
 						n->multixact_freeze_min_age = -1;
 						n->multixact_freeze_table_age = -1;
+						n->log_min_duration = -1;
 					}
 					n->relation = $5;
 					n->va_cols = $6;
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 062b120..960ad11 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -193,6 +193,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;
@@ -2531,6 +2532,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
@@ -2553,6 +2555,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
@@ -2583,6 +2590,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;
@@ -2808,6 +2816,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 b1dfa85..95b5e21 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2615,6 +2615,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 vaccum
+										 * 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.2.1

#6Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#5)
1 attachment(s)
Re: Table-level log_autovacuum_min_duration

I wrote:

I am adding it to the next CF.

Patch 2 attached. I forgot the update of copyfuncs.c and equalfuncs.c.
--
Michael

Attachments:

20150115_autovacuum_log_relopts_v2.patchtext/x-patch; charset=US-ASCII; name=20150115_autovacuum_log_relopts_v2.patchDownload
From 3fc9e6ba924cbf522e0c85ad3dff1e382cd59f15 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     |  9 +++++++++
 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, 53 insertions(+), 11 deletions(-)

diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 299cce8..533a62f 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -1052,6 +1052,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..c16697a 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",
+			"Log autovacuum execution for given threshold",
+			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 5de2b39..7229b1b 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 f1a24f5..f77fc76 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3291,6 +3291,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 6e8b308..f4b6bff 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1508,6 +1508,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 36dac29..37d71c1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -9066,6 +9066,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 = $3 ? 0 : -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9082,6 +9083,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 = $3 ? 0 : -1;
 					n->relation = $5;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9098,6 +9100,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 = $3 ? 0 : -1;
 					$$ = (Node *)n;
 				}
 			| VACUUM '(' vacuum_option_list ')'
@@ -9109,12 +9112,14 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->freeze_min_age = n->freeze_table_age = 0;
 						n->multixact_freeze_min_age = 0;
 						n->multixact_freeze_table_age = 0;
+						n->log_min_duration = 0;
 					}
 					else
 					{
 						n->freeze_min_age = 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;
@@ -9129,12 +9134,14 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->freeze_min_age = n->freeze_table_age = 0;
 						n->multixact_freeze_min_age = 0;
 						n->multixact_freeze_table_age = 0;
+						n->log_min_duration = 0;
 					}
 					else
 					{
 						n->freeze_min_age = n->freeze_table_age = -1;
 						n->multixact_freeze_min_age = -1;
 						n->multixact_freeze_table_age = -1;
+						n->log_min_duration = -1;
 					}
 					n->relation = $5;
 					n->va_cols = $6;
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 062b120..960ad11 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -193,6 +193,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;
@@ -2531,6 +2532,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
@@ -2553,6 +2555,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
@@ -2583,6 +2590,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;
@@ -2808,6 +2816,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 b1dfa85..95b5e21 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2615,6 +2615,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 vaccum
+										 * 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.2.2

#7Naoya Anzai
anzai-naoya@mxu.nes.nec.co.jp
In reply to: Michael Paquier (#6)
Re: Table-level log_autovacuum_min_duration

The following review has been posted through the commitfest application:
make installcheck-world: tested, failed
Implements feature: tested, failed
Spec compliant: tested, failed
Documentation: tested, failed

Hi,

I'm Naoya Anzai.
I've been working as a PostgreSQL Support Engineer for 6 years.
I am a newcomer of reviewing, and My programming skill is not so high.
But the following members also participate in this review. (We say
"Two heads are better than one." :))

Akira Kurosawa <kurosawa-akira@mxc.nes.nec.co.jp>
Taiki Kondo <kondo-taiki@mxt.nes.nec.co.jp>
Huong Dangminh <dangminh-huong@mxm.nes.nec.co.jp>

So I believe reviewing patches is not difficult for us.

This is a review of Table-level log_autovacuum_min_duration patch:
/messages/by-id/CAB7nPqTBQsbEgvb8cOH01K7ARGYs9KBuV8dr+aqGonFVb8koAQ@mail.gmail.com

Submission review
====================
The patch applies cleanly to HEAD, and it works fine on Fedora
release 20.
There is no regression test,but I think it is no problem
because other parameter also is not tested.

Usability review
====================
pg_dump/pg_restore support is OK.
I think this feature is a nice idea and I also want this feature.

Feature test
====================
I executed following commands after setting
"log_autovacuum_min_duration" to 1000 in the GUC. ("bar" table is
already created with no options.)

CREATE TABLE foo ( ... ) WITH ( log_autovacuum_min_duration = 0 );
ALTER TABLE bar SET (log_autovacuum_min_duration = 0 );

Then, only in "foo" and "bar" table, autovacuum log was printed out
even if elapsed time of autovacuum is lesser than 1000ms. This
behavior was expected and there was no crash or failed assertion.
So it looked good for me. But, I executed following command, in
"buzz" table, autovacuum log was printed out if elapsed time is
more than 1000ms.

CREATE TABLE buzz ( ... ) WITH ( log_autovacuum_min_duration = -1 );
^^

I expect autovacuum log is NOT printed out even if elapsed time is
more than 1000ms in this case. My thought is wrong, isn't it? In my
opinion, there is an use case that autovacuum log is always printed
out in all tables excepting specified tables. I think there is a
person who wants to use it like this case, but I (or he) can NOT use
in this situation.

How about your opinion?

Performance review
====================
Not reviewed from this point of view.

Coding review
====================
I think description of log_autovacuum_min_duration in reloptions.c
(line:215) should be like other parameters (match with guc.c). So
it should be "Sets the minimum execution time above which autovacuum
actions will be logged." but not "Log autovacuum execution for
given threshold".

There is no new function which is defined in this patch, and
modified contents are not related to OS-dependent contents, so I
think it will work fine on Windows/BSD etc.

Architecture review
====================
About the modification of gram.y.

I think it is not good that log_min_duration is initialized to
zeros in gram.y when "FREEZE" option is set. Because any VACUUM
queries never use this parameter. I think log_min_duration always
should be initialized to -1.

Regards,
Naoya Anzai (NEC Solution Innovators,Ltd.)

The new status of this patch is: Waiting on Author

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

#8Naoya Anzai
anzai-naoya@mxu.nes.nec.co.jp
In reply to: Naoya Anzai (#7)
Re: Table-level log_autovacuum_min_duration

The following review has been posted through the commitfest application:
make installcheck-world: tested, failed
Implements feature: tested, failed
Spec compliant: tested, failed
Documentation: tested, failed

I'm sorry, I just sent it by mistake.
All of them have passed.

---
Naoya Anzai

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

#9Michael Paquier
michael.paquier@gmail.com
In reply to: Naoya Anzai (#8)
Re: Table-level log_autovacuum_min_duration

On Fri, Feb 6, 2015 at 4:50 AM, Naoya Anzai
<anzai-naoya@mxu.nes.nec.co.jp> wrote:

The following review has been posted through the commitfest application:
make installcheck-world: tested, failed
Implements feature: tested, failed
Spec compliant: tested, failed
Documentation: tested, failed

I'm sorry, I just sent it by mistake.
All of them have passed.

That's fine. I think you used the UI on the commit fest app, and it is
not intuitive that you need to check those boxes at first sight when
using it for the first time.
--
Michael

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

#10Michael Paquier
michael.paquier@gmail.com
In reply to: Naoya Anzai (#7)
1 attachment(s)
Re: Table-level log_autovacuum_min_duration

On Fri, Feb 6, 2015 at 4:39 AM, Naoya Anzai wrote:

I've been working as a PostgreSQL Support Engineer for 6 years.

Thanks for your input Anzai-san.

I am a newcomer of reviewing, and My programming skill is not so high.
But the following members also participate in this review. (We say
"Two heads are better than one." :))

FWIW, any review for any kind of patches are always welcome, either
knowing or not the code you are looking at for a given patch. It is
even encouraged to look at new areas when possible.

Feature test
====================
[blah]
CREATE TABLE buzz ( ... ) WITH ( log_autovacuum_min_duration = -1 );
I expect autovacuum log is NOT printed out even if elapsed time is
more than 1000ms in this case. My thought is wrong, isn't it? In my
opinion, there is an use case that autovacuum log is always printed
out in all tables excepting specified tables. I think there is a
person who wants to use it like this case, but I (or he) can NOT use
in this situation.

How about your opinion?

The patch is working as expected. Similarly to the other parameters
like vacuum_cost_delay, a value of -1 is the default behavior, meaning
that the underlying GUC parameter is used. I thought about using a
special value like -2 to define the behavior you are mentioning here,
aka with "-2" disable custom value and GUC parameter but I don't think
it is worth adding as that's an ugly 3 line of code of this type:
if (log_min_duration == -2)
enforce_log_min = -1;
And you can actually get what you are looking for by setting
min_duration through ALTER TABLE to a very high value, like say 2e9 to
suppress the autovacuum output of a set of tables out of the rest.

Coding review
====================
I think description of log_autovacuum_min_duration in reloptions.c
(line:215) should be like other parameters (match with guc.c). So
it should be "Sets the minimum execution time above which autovacuum
actions will be logged." but not "Log autovacuum execution for
given threshold".

What about that then?
"Minimum execution time above which autovacuum actions will be logged"

Architecture review
====================
About the modification of gram.y.

I think it is not good that log_min_duration is initialized to
zeros in gram.y when "FREEZE" option is set. Because any VACUUM
queries never use this parameter. I think log_min_duration always
should be initialized to -1.

Looking at this patch this morning, actually I think that my last
patch as well as your suggestion are both wrong. To put it in short
words, log_min_duration should be set only if VACOPT_VERBOSE is
defined in query declaration. So I changed this portion of the patch
this way.
--
Michael

Attachments:

20150206_autovacuum_log_relopts_v3.patchtext/x-diff; charset=US-ASCII; name=20150206_autovacuum_log_relopts_v3.patchDownload
From 5f585f6f1abd83b09416b3a9cc6323b9599c104f 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     |  9 +++++++++
 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              | 11 +++++++++++
 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, 57 insertions(+), 11 deletions(-)

diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 299cce8..533a62f 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -1052,6 +1052,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..2dcf528 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",
+			"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 f1a24f5..f77fc76 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3291,6 +3291,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 6e8b308..f4b6bff 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1508,6 +1508,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 36dac29..43b118a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -9066,6 +9066,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 = $4 ? 0 : -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9082,6 +9083,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 = $4 ? 0 : -1;
 					n->relation = $5;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9098,6 +9100,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 = $4 ? 0 : -1;
 					$$ = (Node *)n;
 				}
 			| VACUUM '(' vacuum_option_list ')'
@@ -9116,6 +9119,10 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->multixact_freeze_min_age = -1;
 						n->multixact_freeze_table_age = -1;
 					}
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						n->log_min_duration = -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *) n;
@@ -9136,6 +9143,10 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->multixact_freeze_min_age = -1;
 						n->multixact_freeze_table_age = -1;
 					}
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						n->log_min_duration = -1;
 					n->relation = $5;
 					n->va_cols = $6;
 					if (n->va_cols != NIL)	/* implies analyze */
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 b1dfa85..95b5e21 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2615,6 +2615,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 vaccum
+										 * 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.2.2

#11Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#10)
1 attachment(s)
Re: Table-level log_autovacuum_min_duration

On Fri, Feb 6, 2015 at 11:14 AM, Michael Paquier wrote:

Looking at this patch this morning, actually I think that my last
patch as well as your suggestion are both wrong. To put it in short
words, log_min_duration should be set only if VACOPT_VERBOSE is
defined in query declaration. So I changed this portion of the patch
this way.

And I forgot to change VacuumStmt for the ANALYZE portion in gram.y...
Patch updated attached.
--
Michael

Attachments:

20150206_autovacuum_log_relopts_v4.patchtext/x-diff; charset=US-ASCII; name=20150206_autovacuum_log_relopts_v4.patchDownload
From 11811d27aabd3ca7e97139a8ba6998438ac07b72 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     |  9 +++++++++
 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              | 28 ++++++++++++++++++++++++++++
 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, 74 insertions(+), 11 deletions(-)

diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 299cce8..533a62f 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -1052,6 +1052,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..2dcf528 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",
+			"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 f1a24f5..f77fc76 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3291,6 +3291,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 6e8b308..f4b6bff 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1508,6 +1508,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 36dac29..48b45c8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -9066,6 +9066,10 @@ 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;
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						n->log_min_duration = -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9082,6 +9086,10 @@ 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;
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						n->log_min_duration = -1;
 					n->relation = $5;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9098,6 +9106,10 @@ 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;
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						n->log_min_duration = -1;
 					$$ = (Node *)n;
 				}
 			| VACUUM '(' vacuum_option_list ')'
@@ -9116,6 +9128,10 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->multixact_freeze_min_age = -1;
 						n->multixact_freeze_table_age = -1;
 					}
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						n->log_min_duration = -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *) n;
@@ -9136,6 +9152,10 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->multixact_freeze_min_age = -1;
 						n->multixact_freeze_table_age = -1;
 					}
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						n->log_min_duration = -1;
 					n->relation = $5;
 					n->va_cols = $6;
 					if (n->va_cols != NIL)	/* implies analyze */
@@ -9167,6 +9187,10 @@ AnalyzeStmt:
 					n->freeze_table_age = -1;
 					n->multixact_freeze_min_age = -1;
 					n->multixact_freeze_table_age = -1;
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						n->log_min_duration = -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9181,6 +9205,10 @@ AnalyzeStmt:
 					n->freeze_table_age = -1;
 					n->multixact_freeze_min_age = -1;
 					n->multixact_freeze_table_age = -1;
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						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 b1dfa85..95b5e21 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2615,6 +2615,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 vaccum
+										 * 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.2.2

#12Naoya Anzai
anzai-naoya@mxu.nes.nec.co.jp
In reply to: Michael Paquier (#11)
Re: Table-level log_autovacuum_min_duration

Thanks for your reply.

Feature test
====================

(snip)

I thought about using a
special value like -2 to define the behavior you are mentioning here,
aka with "-2" disable custom value and GUC parameter but I don't think
it is worth adding as that's an ugly 3 line of code of this type:
if (log_min_duration == -2)
enforce_log_min = -1;

I disscussed about this use case with my co-workers, who said
"that code is not good", then we concluded that it was actually
a rare case. If such a case sometimes happens in fact, I (or someone)
will suggest a different way from this patch to handle this case.

We know there is a practical workaround. :)

Coding review
====================
I think description of log_autovacuum_min_duration in reloptions.c
(line:215) should be like other parameters (match with guc.c). So
it should be "Sets the minimum execution time above which autovacuum
actions will be logged." but not "Log autovacuum execution for
given threshold".

What about that then?
"Minimum execution time above which autovacuum actions will be logged"

That's roughly match. For matching with guc.c, you might be better to
add "Sets the" to that discription.

Architecture review
====================
About the modification of gram.y.

I think it is not good that log_min_duration is initialized to
zeros in gram.y when "FREEZE" option is set. Because any VACUUM
queries never use this parameter. I think log_min_duration always
should be initialized to -1.

Looking at this patch this morning, actually I think that my last
patch as well as your suggestion are both wrong. To put it in short
words, log_min_duration should be set only if VACOPT_VERBOSE is
defined in query declaration. So I changed this portion of the patch
this way.

And I forgot to change VacuumStmt for the ANALYZE portion in gram.y...
Patch updated attached.

Sorry, I could not correctly express my opinion to you. I mean
"log_autovacuum_min_duration" is used only by AutoVacuum, Any VACUUM
queries (including VACUUM VERBOSE) never use this parameter. So,
when someone executes Manual Vacuum, "log_min_duration" always should
be initialized to -1.

ANALYZE should also be the same.

In other words, it is not necessary to initialize "log_min_duration"
to zero when perform the VACUUM(or ANALYZE) VERBOSE. "VERBOSE" is
provided only for changing the log level of Manual VACUUM from
DEBUG2 to INFO.

Regards,
-----
Naoya.

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

#13Michael Paquier
michael.paquier@gmail.com
In reply to: Naoya Anzai (#12)
1 attachment(s)
Re: Table-level log_autovacuum_min_duration

On Mon, Feb 9, 2015 at 3:47 PM, Naoya Anzai
<anzai-naoya@mxu.nes.nec.co.jp> wrote:

Feature test
====================

(snip)

I thought about using a
special value like -2 to define the behavior you are mentioning here,
aka with "-2" disable custom value and GUC parameter but I don't think
it is worth adding as that's an ugly 3 line of code of this type:
if (log_min_duration == -2)
enforce_log_min = -1;

I discussed about this use case with my co-workers, who said
"that code is not good", then we concluded that it was actually
a rare case. If such a case sometimes happens in fact, I (or someone)
will suggest a different way from this patch to handle this case.

We know there is a practical workaround. :)

OK, done.

Coding review
====================
I think description of log_autovacuum_min_duration in reloptions.c
(line:215) should be like other parameters (match with guc.c). So
it should be "Sets the minimum execution time above which autovacuum
actions will be logged." but not "Log autovacuum execution for
given threshold".

What about that then?
"Minimum execution time above which autovacuum actions will be logged"

That's roughly match. For matching with guc.c, you might be better to
add "Sets the" to that discription.

OK, done this way...

And I forgot to change VacuumStmt for the ANALYZE portion in gram.y...
Patch updated attached.

Sorry, I could not correctly express my opinion to you. I mean
"log_autovacuum_min_duration" is used only by AutoVacuum, Any VACUUM
queries (including VACUUM VERBOSE) never use this parameter. So,
when someone executes Manual Vacuum, "log_min_duration" always should
be initialized to -1.

ANALYZE should also be the same.

In other words, it is not necessary to initialize "log_min_duration"
to zero when perform the VACUUM(or ANALYZE) VERBOSE. "VERBOSE" is
provided only for changing the log level of Manual VACUUM from
DEBUG2 to INFO.

Well, I see your point but this is not completely true: we could as
well rely entirely on this parameter instead of VACOPT_VERBOSE to
determine if autovacuum, a vacuum or an analyze are in verbose mode,
and remove VACOPT_VERBOSE, but I can imagine people complaining if
VACOPT_VERBOSE is removed. So let's set it up unconditionally to -1 in
gram.y for now. However if people think that it is fine to remove
VACOPT_VERBOSE, we could use exclusively this parameter in VacuumStmt.
Or even add sanity checks at the top of vacuum() to ensure that
VACOPT_VERBOSE is set only when log_min_duration is positive.
Additional opinions on this matter are welcome.
--
Michael

Attachments:

20150210_autovacuum_log_relopts_v5.patchtext/x-patch; charset=US-ASCII; name=20150210_autovacuum_log_relopts_v5.patchDownload
From 124876c4fa4f17989dfa727d8bdbea49e1263db2 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     | 14 ++++++++++++++
 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    | 22 ++++++++++++++++++++++
 src/bin/psql/tab-complete.c            |  2 ++
 src/include/nodes/parsenodes.h         |  2 ++
 src/include/utils/rel.h                |  1 +
 12 files changed, 71 insertions(+), 11 deletions(-)

diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 299cce8..2382459 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -1052,6 +1052,20 @@ 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. When
+      this parameter is set to <literal>-1</>, which is the default behavior,
+      the value of the GUC parameter <varname>log_autovacuum_min_duration</>
+      is used. When a value of <literal>-2</> is set, logging is entirely
+      disabled, ignoring as well the value defined by the GUC parameter
+      <varname>log_autovacuum_min_duration</>.
+     </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..a975387 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, -2, 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 f1a24f5..f77fc76 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3291,6 +3291,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 6e8b308..f4b6bff 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1508,6 +1508,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 36dac29..8a3951e 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -9066,6 +9066,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;
@@ -9082,6 +9083,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;
@@ -9098,6 +9100,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 ')'
@@ -9116,6 +9119,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;
@@ -9136,6 +9140,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 */
@@ -9167,6 +9172,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;
@@ -9181,6 +9187,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..a5d73d7 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,24 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 			? autovacuum_vac_cost_limit
 			: VacuumCostLimit;
 
+		/*
+		 * -1 for this setting means that the GUC equivalent
+		 * log_autovacuum_min_duration should be used, while a value of
+		 * -2 means that logging is entirely disabled. In other cases
+		 * the value defined is used.
+		 */
+		log_min_duration = Log_autovacuum_min_duration;
+		if (avopts)
+		{
+			Assert(avopts->log_min_duration >= -2);
+			if (avopts->log_min_duration == -2)
+				log_min_duration = -1;
+			else if (avopts->log_min_duration == -1)
+				log_min_duration = Log_autovacuum_min_duration;
+			else
+				log_min_duration = avopts->log_min_duration;
+		}
+
 		/* these do not have autovacuum-specific settings */
 		freeze_min_age = (avopts && avopts->freeze_min_age >= 0)
 			? avopts->freeze_min_age
@@ -2537,6 +2557,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 +2783,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 b1dfa85..95b5e21 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2615,6 +2615,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 vaccum
+										 * 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.0

#14Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#13)
1 attachment(s)
Re: Table-level log_autovacuum_min_duration

An updated patch is attached, I think that previous version broke value
assignment of log_min_duration in table_recheck_autovac: the value in
reltoptions should be used only if it is positive. At the same time I wrote
some more documentation about the fact that a toast table will use the
value of its parent table if nothing is set.
--
Michael

Attachments:

20150210_autovacuum_log_relopts_v6.patchtext/x-patch; charset=US-ASCII; name=20150210_autovacuum_log_relopts_v6.patchDownload
From fc108dff6c2bcef84cd173d146f7882c5afc4420 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     | 22 +++++++++++++++++++---
 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    | 22 ++++++++++++++++++++++
 src/bin/psql/tab-complete.c            |  2 ++
 src/include/nodes/parsenodes.h         |  2 ++
 src/include/utils/rel.h                |  1 +
 12 files changed, 76 insertions(+), 14 deletions(-)

diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 299cce8..ee87b73 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,20 @@ 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. When
+      this parameter is set to <literal>-1</>, which is the default behavior,
+      the value of the GUC parameter <varname>log_autovacuum_min_duration</>
+      is used. When a value of <literal>-2</> is set, logging is entirely
+      disabled, ignoring as well the value defined by the GUC parameter
+      <varname>log_autovacuum_min_duration</>.
+     </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..a975387 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, -2, 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 f1a24f5..f77fc76 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3291,6 +3291,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 6e8b308..f4b6bff 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1508,6 +1508,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 36dac29..8a3951e 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -9066,6 +9066,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;
@@ -9082,6 +9083,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;
@@ -9098,6 +9100,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 ')'
@@ -9116,6 +9119,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;
@@ -9136,6 +9140,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 */
@@ -9167,6 +9172,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;
@@ -9181,6 +9187,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..dce3af1 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,24 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 			? autovacuum_vac_cost_limit
 			: VacuumCostLimit;
 
+		/*
+		 * -1 for this setting means that the GUC equivalent
+		 * log_autovacuum_min_duration should be used, while a value of
+		 * -2 means that logging is entirely disabled. In other cases
+		 * the value defined is used.
+		 */
+		log_min_duration = Log_autovacuum_min_duration;
+		if (avopts)
+		{
+			Assert(avopts->log_min_duration >= -2);
+			if (avopts->log_min_duration == -2)
+				log_min_duration = -1;
+			else if (avopts->log_min_duration == -1)
+				log_min_duration = Log_autovacuum_min_duration;
+			else if (avopts->log_min_duration >= 0)
+				log_min_duration = avopts->log_min_duration;
+		}
+
 		/* these do not have autovacuum-specific settings */
 		freeze_min_age = (avopts && avopts->freeze_min_age >= 0)
 			? avopts->freeze_min_age
@@ -2537,6 +2557,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 +2783,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 b1dfa85..95b5e21 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2615,6 +2615,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 vaccum
+										 * 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.0

#15Naoya Anzai
anzai-naoya@mxu.nes.nec.co.jp
In reply to: Michael Paquier (#14)
Re: Table-level log_autovacuum_min_duration

Hi, Michael-san

An updated patch is attached,

I'm sorry for confusing you.

I think you don't have to implement this code to disable this
feature with using value "-2".Because this use case is a rare case,
and there is a practical workaround using huge value like "2e9".
(You suggested "2e9" to me, didn't you? :) ) So, please remove this code.

Well, I see your point but this is not completely true: we could as
well rely entirely on this parameter instead of VACOPT_VERBOSE to
determine if autovacuum, a vacuum or an analyze are in verbose mode,
and remove VACOPT_VERBOSE, but I can imagine people complaining if
VACOPT_VERBOSE is removed. So let's set it up unconditionally to -1 in
gram.y for now. However if people think that it is fine to remove
VACOPT_VERBOSE, we could use exclusively this parameter in VacuumStmt.
Or even add sanity checks at the top of vacuum() to ensure that
VACOPT_VERBOSE is set only when log_min_duration is positive.
Additional opinions on this matter are welcome.

I understand your point at last. :)

You mean that ...
Log_autovacuum_min_duration assumes a role of VACOPT_VERBOSE.
Even if this parameter never use currently for manual vacuum,
log_autovacuum_min_duration should be set zero(anytime output)
when we executes "VACUUM(or ANALYZE) VERBOSE".
Is my understanding correct? If so,it sounds logical.

If we can abolish VERBOSE option,
I think it's ideal that we will prepare a new parameter like
a log_min_duration_vacuum(and log_min_duration_analyze) which
integrating "VERBOSE feature" and "log_autovacuum_min_duration".

Regards,
---
Naoya Anzai

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

#16Michael Paquier
michael.paquier@gmail.com
In reply to: Naoya Anzai (#15)
Re: Table-level log_autovacuum_min_duration

On Thu, Feb 12, 2015 at 5:44 PM, Naoya Anzai <anzai-naoya@mxu.nes.nec.co.jp>
wrote:

Hi, Michael-san

An updated patch is attached,

I'm sorry for confusing you.

I think you don't have to implement this code to disable this
feature with using value "-2".Because this use case is a rare case,
and there is a practical workaround using huge value like "2e9".
(You suggested "2e9" to me, didn't you? :) ) So, please remove this code.

I will clean up the code.

Well, I see your point but this is not completely true: we could as
well rely entirely on this parameter instead of VACOPT_VERBOSE to
determine if autovacuum, a vacuum or an analyze are in verbose mode,
and remove VACOPT_VERBOSE, but I can imagine people complaining if
VACOPT_VERBOSE is removed. So let's set it up unconditionally to -1 in
gram.y for now. However if people think that it is fine to remove
VACOPT_VERBOSE, we could use exclusively this parameter in VacuumStmt.
Or even add sanity checks at the top of vacuum() to ensure that
VACOPT_VERBOSE is set only when log_min_duration is positive.
Additional opinions on this matter are welcome.

I understand your point at last. :)

You mean that ...
Log_autovacuum_min_duration assumes a role of VACOPT_VERBOSE.
Even if this parameter never use currently for manual vacuum,
log_autovacuum_min_duration should be set zero(anytime output)
when we executes "VACUUM(or ANALYZE) VERBOSE".
Is my understanding correct? If so,it sounds logical.

Yup, that's my opinion. Now I don't know if people would mind to remove
VACOPT_VERBOSE and replace the control it does by log_min_duration in
VacuumStmt. At least both things are overlapping, and log_min_duration
offers more options than the plain VACOPT_VERBOSE.

If we can abolish VERBOSE option,
I think it's ideal that we will prepare a new parameter like
a log_min_duration_vacuum(and log_min_duration_analyze) which
integrating "VERBOSE feature" and "log_autovacuum_min_duration".

What I think you are proposing here is a VERBOSE option that hypothetically
gets activated if a manual VACUUM takes more than a certain amount
specified by those parameters. I doubt this would be useful. In any case
this is unrelated to this patch.
--
Michael

#17Naoya Anzai
anzai-naoya@mxu.nes.nec.co.jp
In reply to: Michael Paquier (#16)
Re: Table-level log_autovacuum_min_duration

You mean that ...
Log_autovacuum_min_duration assumes a role of VACOPT_VERBOSE.
Even if this parameter never use currently for manual vacuum,
log_autovacuum_min_duration should be set zero(anytime output)
when we executes "VACUUM(or ANALYZE) VERBOSE".
Is my understanding correct? If so,it sounds logical.

Yup, that's my opinion. Now I don't know if people would mind to remove
VACOPT_VERBOSE and replace the control it does by log_min_duration in
VacuumStmt. At least both things are overlapping, and log_min_duration
offers more options than the plain VACOPT_VERBOSE.

OK. I agree with you.
Please re-implement as you are thinking.

If we can abolish VERBOSE option,
I think it's ideal that we will prepare a new parameter like
a log_min_duration_vacuum(and log_min_duration_analyze) which
integrating "VERBOSE feature" and "log_autovacuum_min_duration".

What I think you are proposing here is a VERBOSE option that hypothetically
gets activated if a manual VACUUM takes more than a certain amount
specified by those parameters. I doubt this would be useful. In any case
this is unrelated to this patch.

I suspect manual vacuum often executes as "semi-auto vacuum"
by job-scheduler, cron, etc in actual maintenance cases.
Whether auto or manual, I think that's important to output
their logs in the same mechanism.

Sorry, I seem to have wandered from the subject.

Naoya

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

#18Michael Paquier
michael.paquier@gmail.com
In reply to: Naoya Anzai (#17)
Re: Table-level log_autovacuum_min_duration

On Fri, Feb 13, 2015 at 10:16 AM, Naoya Anzai
<anzai-naoya@mxu.nes.nec.co.jp> wrote:

You mean that ...
Log_autovacuum_min_duration assumes a role of VACOPT_VERBOSE.
Even if this parameter never use currently for manual vacuum,
log_autovacuum_min_duration should be set zero(anytime output)
when we executes "VACUUM(or ANALYZE) VERBOSE".
Is my understanding correct? If so,it sounds logical.

Yup, that's my opinion. Now I don't know if people would mind to remove
VACOPT_VERBOSE and replace the control it does by log_min_duration in
VacuumStmt. At least both things are overlapping, and log_min_duration
offers more options than the plain VACOPT_VERBOSE.

OK. I agree with you.
Please re-implement as you are thinking.

OK will do that today.

If we can abolish VERBOSE option,
I think it's ideal that we will prepare a new parameter like
a log_min_duration_vacuum(and log_min_duration_analyze) which
integrating "VERBOSE feature" and "log_autovacuum_min_duration".

What I think you are proposing here is a VERBOSE option that hypothetically
gets activated if a manual VACUUM takes more than a certain amount
specified by those parameters. I doubt this would be useful. In any case
this is unrelated to this patch.

I suspect manual vacuum often executes as "semi-auto vacuum"
by job-scheduler, cron, etc in actual maintenance cases.
Whether auto or manual, I think that's important to output
their logs in the same mechanism.

Sorry, I seem to have wandered from the subject.

No problem. That's a constructive discussion :)
--
Michael

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

#19Michael Paquier
michael.paquier@gmail.com
In reply to: Naoya Anzai (#17)
1 attachment(s)
Re: Table-level log_autovacuum_min_duration

On Fri, Feb 13, 2015 at 10:16 AM, Naoya Anzai
<anzai-naoya@mxu.nes.nec.co.jp> wrote:

You mean that ...
Log_autovacuum_min_duration assumes a role of VACOPT_VERBOSE.
Even if this parameter never use currently for manual vacuum,
log_autovacuum_min_duration should be set zero(anytime output)
when we executes "VACUUM(or ANALYZE) VERBOSE".
Is my understanding correct? If so,it sounds logical.

Yup, that's my opinion. Now I don't know if people would mind to remove
VACOPT_VERBOSE and replace the control it does by log_min_duration in
VacuumStmt. At least both things are overlapping, and log_min_duration
offers more options than the plain VACOPT_VERBOSE.

OK. I agree with you.
Please re-implement as you are thinking.

Thanks. Attached is an updated patch will all those things implemented.

If we can abolish VERBOSE option,
I think it's ideal that we will prepare a new parameter like
a log_min_duration_vacuum(and log_min_duration_analyze) which
integrating "VERBOSE feature" and "log_autovacuum_min_duration".

What I think you are proposing here is a VERBOSE option that hypothetically
gets activated if a manual VACUUM takes more than a certain amount
specified by those parameters. I doubt this would be useful. In any case
this is unrelated to this patch.

I suspect manual vacuum often executes as "semi-auto vacuum"
by job-scheduler, cron, etc in actual maintenance cases.
Whether auto or manual, I think that's important to output
their logs in the same mechanism.

Sorry, I seem to have wandered from the subject.

This patch is a step in this direction as it enables any backend-side
code to set up VacuumStmt with a custom timestamp value to output logs
after a given timing, for example in background workers. For the
client-side of things, I am unsure if we'd actually want it, we should
always VERBOSE when this option is invoked through a query, and not
add any hypothetical condition on it...

Btw, after hacking on this it happens that we cannot completely remove
VACOPT_VERBOSE as gram.y needs to take into account options with
parenthesis :)
But we can limit its use to the query parser only, similarly for VACOPT_FREEZE.
--
Michael

Attachments:

20150213_autovacuum_log_relopts_v7.patchtext/x-patch; charset=US-ASCII; name=20150213_autovacuum_log_relopts_v7.patchDownload
From 95c9503331662f94775958f76cf1cf108e2f9eb4 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         | 14 +++++++-------
 src/backend/commands/vacuum.c          |  4 ++--
 src/backend/commands/vacuumlazy.c      | 10 +++++-----
 src/backend/nodes/copyfuncs.c          |  1 +
 src/backend/nodes/equalfuncs.c         |  1 +
 src/backend/parser/gram.y              | 23 +++++++++++++----------
 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, 67 insertions(+), 27 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..1a0fa03 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -124,7 +124,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
 	BlockNumber relpages = 0;
 
 	/* Select logging level */
-	if (vacstmt->options & VACOPT_VERBOSE)
+	if (vacstmt->log_min_duration >= 0)
 		elevel = INFO;
 	else
 		elevel = DEBUG2;
@@ -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..be45205 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",
@@ -1313,7 +1313,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
 
 		/* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
 		cluster_rel(relid, InvalidOid, false,
-					(vacstmt->options & VACOPT_VERBOSE) != 0);
+					(vacstmt->log_min_duration >= 0));
 	}
 	else
 		lazy_vacuum_rel(onerel, vacstmt, vac_strategy);
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 7d9e49e..47a234d 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -194,13 +194,13 @@ 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();
 	}
 
-	if (vacstmt->options & VACOPT_VERBOSE)
+	if (vacstmt->log_min_duration >= 0)
 		elevel = INFO;
 	else
 		elevel = DEBUG2;
@@ -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 f1a24f5..f77fc76 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3291,6 +3291,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 6e8b308..f4b6bff 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1508,6 +1508,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 36dac29..74b97e3 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -9060,12 +9060,11 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 					n->options = VACOPT_VACUUM;
 					if ($2)
 						n->options |= VACOPT_FULL;
-					if ($4)
-						n->options |= VACOPT_VERBOSE;
 					n->freeze_min_age = $3 ? 0 : -1;
 					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 = $4 ? 0 : -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9076,12 +9075,11 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 					n->options = VACOPT_VACUUM;
 					if ($2)
 						n->options |= VACOPT_FULL;
-					if ($4)
-						n->options |= VACOPT_VERBOSE;
 					n->freeze_min_age = $3 ? 0 : -1;
 					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 = $4 ? 0 : -1;
 					n->relation = $5;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9092,12 +9090,11 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 					n->options |= VACOPT_VACUUM;
 					if ($2)
 						n->options |= VACOPT_FULL;
-					if ($4)
-						n->options |= VACOPT_VERBOSE;
 					n->freeze_min_age = $3 ? 0 : -1;
 					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 = $4 ? 0 : -1;
 					$$ = (Node *)n;
 				}
 			| VACUUM '(' vacuum_option_list ')'
@@ -9116,6 +9113,10 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->multixact_freeze_min_age = -1;
 						n->multixact_freeze_table_age = -1;
 					}
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						n->log_min_duration = -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *) n;
@@ -9136,6 +9137,10 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->multixact_freeze_min_age = -1;
 						n->multixact_freeze_table_age = -1;
 					}
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						n->log_min_duration = -1;
 					n->relation = $5;
 					n->va_cols = $6;
 					if (n->va_cols != NIL)	/* implies analyze */
@@ -9161,12 +9166,11 @@ AnalyzeStmt:
 				{
 					VacuumStmt *n = makeNode(VacuumStmt);
 					n->options = VACOPT_ANALYZE;
-					if ($2)
-						n->options |= VACOPT_VERBOSE;
 					n->freeze_min_age = -1;
 					n->freeze_table_age = -1;
 					n->multixact_freeze_min_age = -1;
 					n->multixact_freeze_table_age = -1;
+					n->log_min_duration = $2 ? 0 : -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9175,12 +9179,11 @@ AnalyzeStmt:
 				{
 					VacuumStmt *n = makeNode(VacuumStmt);
 					n->options = VACOPT_ANALYZE;
-					if ($2)
-						n->options |= VACOPT_VERBOSE;
 					n->freeze_min_age = -1;
 					n->freeze_table_age = -1;
 					n->multixact_freeze_min_age = -1;
 					n->multixact_freeze_table_age = -1;
+					n->log_min_duration = $2 ? 0 : -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 b1dfa85..c6ab198 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2615,6 +2615,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.0

#20Naoya Anzai
anzai-naoya@mxu.nes.nec.co.jp
In reply to: Michael Paquier (#19)
Re: Table-level log_autovacuum_min_duration

Hi, Michael

I found that definition of VERBOSE and log_autovacuum is not
pretty match. For example, "VERBOSE" can output logs of
scanning indices and scanning detail of analyze, but
log_autovacuum can't output them.

Please see following sequences.

1. execute these queries.

DROP TABLE t1;
CREATE TABLE t1(id integer primary key,name text);
ALTER TABLE t1 SET (log_autovacuum_min_duration=0);
ALTER TABLE t1 ALTER COLUMN name SET STORAGE EXTERNAL;
INSERT INTO t1 SELECT GENERATE_SERIES(1,100),repeat('a',3000);
UPDATE t1 SET name='update';

2. after a while, output the following logs by autovacuum movement.
(For your convenience, I put the "### TYPE ###" prefix on each logs.)

### VERBOSE ###INFO: vacuuming "public.t1"
### VERBOSE ###INFO: scanned index "t1_pkey" to remove 33 row versions
### VERBOSE ###DETAIL: CPU 0.00s/0.00u sec elapsed 0.00 sec.
### VERBOSE ###INFO: "t1": removed 33 row versions in 1 pages
### VERBOSE ###DETAIL: CPU 0.00s/0.00u sec elapsed 0.00 sec.
### VERBOSE ###INFO: index "t1_pkey" now contains 100 row versions in 2 pages
### VERBOSE ###DETAIL: 33 index row versions were removed.
### VERBOSE ### 0 index pages have been deleted, 0 are currently reusable.
### VERBOSE ### CPU 0.00s/0.00u sec elapsed 0.00 sec.
### VERBOSE ###INFO: "t1": found 100 removable, 100 nonremovable row versions in 2 out of 2 pages
### VERBOSE ###DETAIL: 0 dead row versions cannot be removed yet.
### VERBOSE ### There were 0 unused item pointers.
### VERBOSE ### Skipped 0 pages due to buffer pins.
### VERBOSE ### 0 pages are entirely empty.
### VERBOSE ### CPU 0.00s/0.00u sec elapsed 0.00 sec.
### LOG_AVAC ###LOG: automatic vacuum of table "naoya.public.t1": index scans: 1
### LOG_AVAC ### pages: 0 removed, 2 remain, 0 skipped due to pins
### LOG_AVAC ### tuples: 100 removed, 100 remain, 0 are dead but not yet removable
### LOG_AVAC ### buffer usage: 47 hits, 4 misses, 4 dirtied
### LOG_AVAC ### avg read rate: 14.192 MB/s, avg write rate: 14.192 MB/s
### LOG_AVAC ### system usage: CPU 0.00s/0.00u sec elapsed 0.00 sec
### VERBOSE ###INFO: analyzing "public.t1"
### VERBOSE ###INFO: "t1": scanned 2 of 2 pages, containing 100 live rows and 0 dead rows; 100 rows in sample, 100 estimated total rows
### LOG_AVAC ###LOG: automatic analyze of table "naoya.public.t1" system usage: CPU 0.00s/0.00u sec elapsed 0.04 sec
### VERBOSE ###INFO: vacuuming "pg_toast.pg_toast_72882"
### VERBOSE ###INFO: scanned index "pg_toast_72882_index" to remove 200 row versions
### VERBOSE ###DETAIL: CPU 0.00s/0.00u sec elapsed 0.00 sec.
### VERBOSE ###INFO: "pg_toast_72882": removed 200 row versions in 50 pages
### VERBOSE ###DETAIL: CPU 0.00s/0.00u sec elapsed 0.00 sec.
### VERBOSE ###INFO: index "pg_toast_72882_index" now contains 0 row versions in 2 pages
### VERBOSE ###DETAIL: 200 index row versions were removed.
### VERBOSE ### 0 index pages have been deleted, 0 are currently reusable.
### VERBOSE ### CPU 0.00s/0.00u sec elapsed 0.00 sec.
### VERBOSE ###INFO: "pg_toast_72882": found 200 removable, 0 nonremovable row versions in 50 out of 50 pages
### VERBOSE ###DETAIL: 0 dead row versions cannot be removed yet.
### VERBOSE ### There were 0 unused item pointers.
### VERBOSE ### Skipped 0 pages due to buffer pins.
### VERBOSE ### 0 pages are entirely empty.
### VERBOSE ### CPU 0.00s/0.00u sec elapsed 0.00 sec.
### VERBOSE ###INFO: "pg_toast_72882": truncated 50 to 0 pages
### VERBOSE ###DETAIL: CPU 0.00s/0.00u sec elapsed 0.03 sec.
### LOG_AVAC ###LOG: automatic vacuum of table "naoya.pg_toast.pg_toast_72882": index scans: 1
### LOG_AVAC ### pages: 50 removed, 0 remain, 0 skipped due to pins
### LOG_AVAC ### tuples: 200 removed, 0 remain, 0 are dead but not yet removable
### LOG_AVAC ### buffer usage: 223 hits, 2 misses, 1 dirtied
### LOG_AVAC ### avg read rate: 0.457 MB/s, avg write rate: 0.228 MB/s
### LOG_AVAC ### system usage: CPU 0.00s/0.00u sec elapsed 0.03 sec

I think VACOPT_VERBOSE should not be easily replaced to log_min_duration>=0.

And, in this v7 patch looks that VERBOSE log is always output
in INFO-Level when log_autovacuum_min_duration is set to 0.
Is this your point?

Regards,
---
Naoya

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

#21Michael Paquier
michael.paquier@gmail.com
In reply to: Naoya Anzai (#20)
1 attachment(s)
Re: Table-level log_autovacuum_min_duration

On Tue, Feb 17, 2015 at 5:16 PM, Naoya Anzai wrote:

I found that definition of VERBOSE and log_autovacuum is not
pretty match. For example, "VERBOSE" can output logs of
scanning indices and scanning detail of analyze, but
log_autovacuum can't output them.
[...]

Doh. I forgot to add !IsAutoVacuumWorkerProcess() in the checks with
VACOPT_VERBOSE that I previously removed...

And, in this v7 patch looks that VERBOSE log is always output
in INFO-Level when log_autovacuum_min_duration is set to 0.
Is this your point?

That's my bug, fixed in the attached.
--
Michael

Attachments:

20150217_autovacuum_log_relopts_v8.patchtext/x-diff; charset=US-ASCII; name=20150217_autovacuum_log_relopts_v8.patchDownload
From c05ff55823dc7eae340447a945364d340f87efbe 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         | 14 +++++++-------
 src/backend/commands/vacuum.c          |  5 +++--
 src/backend/commands/vacuumlazy.c      | 10 +++++-----
 src/backend/nodes/copyfuncs.c          |  1 +
 src/backend/nodes/equalfuncs.c         |  1 +
 src/backend/parser/gram.y              | 23 +++++++++++++----------
 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, 68 insertions(+), 27 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..de805e3 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -124,7 +124,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
 	BlockNumber relpages = 0;
 
 	/* Select logging level */
-	if (vacstmt->options & VACOPT_VERBOSE)
+	if (!IsAutoVacuumWorkerProcess() && vacstmt->log_min_duration >= 0)
 		elevel = INFO;
 	else
 		elevel = DEBUG2;
@@ -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..c267972 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",
@@ -1313,7 +1313,8 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
 
 		/* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
 		cluster_rel(relid, InvalidOid, false,
-					(vacstmt->options & VACOPT_VERBOSE) != 0);
+					(!IsAutoVacuumWorkerProcess() &&
+					 vacstmt->log_min_duration >= 0));
 	}
 	else
 		lazy_vacuum_rel(onerel, vacstmt, vac_strategy);
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 7d9e49e..651a05f 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -194,13 +194,13 @@ 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();
 	}
 
-	if (vacstmt->options & VACOPT_VERBOSE)
+	if (!IsAutoVacuumWorkerProcess() && vacstmt->log_min_duration >= 0)
 		elevel = INFO;
 	else
 		elevel = DEBUG2;
@@ -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 f1a24f5..f77fc76 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3291,6 +3291,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 6e8b308..f4b6bff 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1508,6 +1508,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 36dac29..74b97e3 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -9060,12 +9060,11 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 					n->options = VACOPT_VACUUM;
 					if ($2)
 						n->options |= VACOPT_FULL;
-					if ($4)
-						n->options |= VACOPT_VERBOSE;
 					n->freeze_min_age = $3 ? 0 : -1;
 					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 = $4 ? 0 : -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9076,12 +9075,11 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 					n->options = VACOPT_VACUUM;
 					if ($2)
 						n->options |= VACOPT_FULL;
-					if ($4)
-						n->options |= VACOPT_VERBOSE;
 					n->freeze_min_age = $3 ? 0 : -1;
 					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 = $4 ? 0 : -1;
 					n->relation = $5;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9092,12 +9090,11 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 					n->options |= VACOPT_VACUUM;
 					if ($2)
 						n->options |= VACOPT_FULL;
-					if ($4)
-						n->options |= VACOPT_VERBOSE;
 					n->freeze_min_age = $3 ? 0 : -1;
 					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 = $4 ? 0 : -1;
 					$$ = (Node *)n;
 				}
 			| VACUUM '(' vacuum_option_list ')'
@@ -9116,6 +9113,10 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->multixact_freeze_min_age = -1;
 						n->multixact_freeze_table_age = -1;
 					}
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						n->log_min_duration = -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *) n;
@@ -9136,6 +9137,10 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->multixact_freeze_min_age = -1;
 						n->multixact_freeze_table_age = -1;
 					}
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						n->log_min_duration = -1;
 					n->relation = $5;
 					n->va_cols = $6;
 					if (n->va_cols != NIL)	/* implies analyze */
@@ -9161,12 +9166,11 @@ AnalyzeStmt:
 				{
 					VacuumStmt *n = makeNode(VacuumStmt);
 					n->options = VACOPT_ANALYZE;
-					if ($2)
-						n->options |= VACOPT_VERBOSE;
 					n->freeze_min_age = -1;
 					n->freeze_table_age = -1;
 					n->multixact_freeze_min_age = -1;
 					n->multixact_freeze_table_age = -1;
+					n->log_min_duration = $2 ? 0 : -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9175,12 +9179,11 @@ AnalyzeStmt:
 				{
 					VacuumStmt *n = makeNode(VacuumStmt);
 					n->options = VACOPT_ANALYZE;
-					if ($2)
-						n->options |= VACOPT_VERBOSE;
 					n->freeze_min_age = -1;
 					n->freeze_table_age = -1;
 					n->multixact_freeze_min_age = -1;
 					n->multixact_freeze_table_age = -1;
+					n->log_min_duration = $2 ? 0 : -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 b1dfa85..c6ab198 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2615,6 +2615,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.0

#22Naoya Anzai
anzai-naoya@mxu.nes.nec.co.jp
In reply to: Michael Paquier (#21)
Re: Table-level log_autovacuum_min_duration

Hi, Michael

I thought about VACOPT_VERBOSE again.

As a result, I think you should not delete VACOPT_VERBOSE.

According to the last mail I have posted, the difference of
manual-vacuum log and auto-vacuum log exists clearly.
So, at least you should not touch the mechanism of VACOPT_VERBOSE
until both vacuum log formats are unified to a same format.

If you agree my think, please undo your removing VACOPT_VERBOSE work.

Regards,
---
Naoya

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

#23Michael Paquier
michael.paquier@gmail.com
In reply to: Naoya Anzai (#22)
Re: Table-level log_autovacuum_min_duration

On Thu, Feb 19, 2015 at 2:13 PM, Naoya Anzai
<anzai-naoya@mxu.nes.nec.co.jp> wrote:

As a result, I think you should not delete VACOPT_VERBOSE.

In v8 it is not deleted. It is still declared, and its use is isolated
in gram.y, similarly to VACOPT_FREEZE.

According to the last mail I have posted, the difference of
manual-vacuum log and auto-vacuum log exists clearly.

Did you test the latest patch v8? I have added checks in it to see if
a process is an autovacuum worker to control elevel and the extra logs
of v7 do not show up.

So, at least you should not touch the mechanism of VACOPT_VERBOSE
until both vacuum log formats are unified to a same format.

If you mean that we should have the same kind of log outputs for
autovacuum and manual vacuum, I think that this is not going to
happen. Autovacuum entries are kept less verbose on purpose, contract
that v7 clealy broke.

If you agree my think, please undo your removing VACOPT_VERBOSE work.

Well, I don't agree :) And I am guessing that you did not look at v8
as well. Centralizing the control of logs using log_min_duration is
more extensible than simply having VACOPT_VERBOSE.
--
Michael

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

#24Fujii Masao
masao.fujii@gmail.com
In reply to: Michael Paquier (#23)
Re: Table-level log_autovacuum_min_duration

On Thu, Feb 19, 2015 at 3:32 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Feb 19, 2015 at 2:13 PM, Naoya Anzai
<anzai-naoya@mxu.nes.nec.co.jp> wrote:

As a result, I think you should not delete VACOPT_VERBOSE.

In v8 it is not deleted. It is still declared, and its use is isolated
in gram.y, similarly to VACOPT_FREEZE.

According to the last mail I have posted, the difference of
manual-vacuum log and auto-vacuum log exists clearly.

Did you test the latest patch v8? I have added checks in it to see if
a process is an autovacuum worker to control elevel and the extra logs
of v7 do not show up.

So, at least you should not touch the mechanism of VACOPT_VERBOSE
until both vacuum log formats are unified to a same format.

If you mean that we should have the same kind of log outputs for
autovacuum and manual vacuum, I think that this is not going to
happen. Autovacuum entries are kept less verbose on purpose, contract
that v7 clealy broke.

If you agree my think, please undo your removing VACOPT_VERBOSE work.

Well, I don't agree :) And I am guessing that you did not look at v8
as well. Centralizing the control of logs using log_min_duration is
more extensible than simply having VACOPT_VERBOSE.

With the patch, VACUUM ANALYZE VERBOSE doesn't emit any verbose message.
Why did you remove that functionality?

Regards,

--
Fujii Masao

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

#25Michael Paquier
michael.paquier@gmail.com
In reply to: Fujii Masao (#24)
1 attachment(s)
Re: Table-level log_autovacuum_min_duration

On Thu, Mar 5, 2015 at 7:10 PM, Fujii Masao wrote:

With the patch, VACUUM ANALYZE VERBOSE doesn't emit any verbose message.
Why did you remove that functionality?

Oops. Sorry about that. In gram.y, the combination of VacuumStmt with
AnalyzeStmt overwrote the value of log_min_duration incorrectly. I
also found another bug related to logging of ANALYZE not working
correctly because of the use of IsAutoVacuumWorkerProcess() instead of
VACOPT_VERBOSE (this is reducing the diffs of the patch btw). All
those things are fixed in the attached.
--
Michael

Attachments:

20150305_autovacuum_log_relopts_v9.patchapplication/x-patch; name=20150305_autovacuum_log_relopts_v9.patchDownload
From 0de9f41aaa27947a16fe8ad75f54e3a5cdf701b5 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              | 16 ++++++++++++++++
 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, 67 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..36d02eb 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 = $4 ? 0 : -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 = $4 ? 0 : -1;
 					n->relation = $5;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9106,6 +9108,10 @@ 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;
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						n->log_min_duration = -1;
 					$$ = (Node *)n;
 				}
 			| VACUUM '(' vacuum_option_list ')'
@@ -9124,6 +9130,10 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->multixact_freeze_min_age = -1;
 						n->multixact_freeze_table_age = -1;
 					}
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						n->log_min_duration = -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *) n;
@@ -9144,6 +9154,10 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
 						n->multixact_freeze_min_age = -1;
 						n->multixact_freeze_table_age = -1;
 					}
+					if (n->options & VACOPT_VERBOSE)
+						n->log_min_duration = 0;
+					else
+						n->log_min_duration = -1;
 					n->relation = $5;
 					n->va_cols = $6;
 					if (n->va_cols != NIL)	/* implies analyze */
@@ -9175,6 +9189,7 @@ AnalyzeStmt:
 					n->freeze_table_age = -1;
 					n->multixact_freeze_min_age = -1;
 					n->multixact_freeze_table_age = -1;
+					n->log_min_duration = $2 ? 0 : -1;
 					n->relation = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
@@ -9189,6 +9204,7 @@ AnalyzeStmt:
 					n->freeze_table_age = -1;
 					n->multixact_freeze_min_age = -1;
 					n->multixact_freeze_table_age = -1;
+					n->log_min_duration = $2 ? 0 : -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

#26Fujii Masao
masao.fujii@gmail.com
In reply to: Michael Paquier (#25)
Re: Table-level log_autovacuum_min_duration

On Thu, Mar 5, 2015 at 9:49 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Mar 5, 2015 at 7:10 PM, Fujii Masao wrote:

With the patch, VACUUM ANALYZE VERBOSE doesn't emit any verbose message.
Why did you remove that functionality?

Oops. Sorry about that. In gram.y, the combination of VacuumStmt with
AnalyzeStmt overwrote the value of log_min_duration incorrectly. I
also found another bug related to logging of ANALYZE not working
correctly because of the use of IsAutoVacuumWorkerProcess() instead of
VACOPT_VERBOSE (this is reducing the diffs of the patch btw). All
those things are fixed in the attached.

Thanks for updating the patch!

Why does log_min_duration need to be set even when manual VACUUM command is
executed? Per the latest version of the patch, log_min_duration is checked only
when the process is autovacuum worker. So ISTM that log_min_duration doesn't
need to be set in gram.y. It's even confusing to me. Or if you're going to
implement something like "VACUUM VERBOSE DURATION n" (i.e., verbose message
is output if n seconds have been elapsed), that might be necessary, though...

Regards,

--
Fujii Masao

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

#27Michael Paquier
michael.paquier@gmail.com
In reply to: Fujii Masao (#26)
1 attachment(s)
Re: Table-level log_autovacuum_min_duration

On Fri, Mar 6, 2015 at 12:44 PM, Fujii Masao <masao.fujii@gmail.com> wrote:

On Thu, Mar 5, 2015 at 9:49 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Mar 5, 2015 at 7:10 PM, Fujii Masao wrote:

With the patch, VACUUM ANALYZE VERBOSE doesn't emit any verbose message.
Why did you remove that functionality?

Oops. Sorry about that. In gram.y, the combination of VacuumStmt with
AnalyzeStmt overwrote the value of log_min_duration incorrectly. I
also found another bug related to logging of ANALYZE not working
correctly because of the use of IsAutoVacuumWorkerProcess() instead of
VACOPT_VERBOSE (this is reducing the diffs of the patch btw). All
those things are fixed in the attached.

Thanks for updating the patch!

Why does log_min_duration need to be set even when manual VACUUM command is
executed? Per the latest version of the patch, log_min_duration is checked only
when the process is autovacuum worker. So ISTM that log_min_duration doesn't
need to be set in gram.y. It's even confusing to me. Or if you're going to
implement something like "VACUUM VERBOSE DURATION n" (i.e., verbose message
is output if n seconds have been elapsed), that might be necessary, though...

Thanks for reminding. The DURATION-like clause was exactly a point
mentioned by Anzai-san upthread, and it made sense to me to be in-line
with the other parameters controlling the freeze (discussion somewhat
related to that =>
/messages/by-id/CAB7nPqRZX7Pv2B-R7xHmAh52tfjAQGfy9btqwFstgQgXks=iSw@mail.gmail.com)
but we can live without it for this patch as VACOPT_VERBOSE is used
only by manual VACUUM and not by autovacuum to choose the log elevel.
--
Michael

Attachments:

20150306_autovacuum_log_relopts_v10.patchapplication/x-patch; name=20150306_autovacuum_log_relopts_v10.patchDownload
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

#28Fujii Masao
masao.fujii@gmail.com
In reply to: Michael Paquier (#27)
Re: Table-level log_autovacuum_min_duration

On Fri, Mar 6, 2015 at 1:07 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Fri, Mar 6, 2015 at 12:44 PM, Fujii Masao <masao.fujii@gmail.com> wrote:

On Thu, Mar 5, 2015 at 9:49 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Mar 5, 2015 at 7:10 PM, Fujii Masao wrote:

With the patch, VACUUM ANALYZE VERBOSE doesn't emit any verbose message.
Why did you remove that functionality?

Oops. Sorry about that. In gram.y, the combination of VacuumStmt with
AnalyzeStmt overwrote the value of log_min_duration incorrectly. I
also found another bug related to logging of ANALYZE not working
correctly because of the use of IsAutoVacuumWorkerProcess() instead of
VACOPT_VERBOSE (this is reducing the diffs of the patch btw). All
those things are fixed in the attached.

Thanks for updating the patch!

Why does log_min_duration need to be set even when manual VACUUM command is
executed? Per the latest version of the patch, log_min_duration is checked only
when the process is autovacuum worker. So ISTM that log_min_duration doesn't
need to be set in gram.y. It's even confusing to me. Or if you're going to
implement something like "VACUUM VERBOSE DURATION n" (i.e., verbose message
is output if n seconds have been elapsed), that might be necessary, though...

Thanks for reminding. The DURATION-like clause was exactly a point
mentioned by Anzai-san upthread, and it made sense to me to be in-line
with the other parameters controlling the freeze (discussion somewhat
related to that =>
/messages/by-id/CAB7nPqRZX7Pv2B-R7xHmAh52tfjAQGfy9btqwFstgQgXks=iSw@mail.gmail.com)
but we can live without it for this patch as VACOPT_VERBOSE is used
only by manual VACUUM and not by autovacuum to choose the log elevel.

Are you planning to update the patch so that it's based on the commit 0d83138?

Regards,

--
Fujii Masao

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

#29Michael Paquier
michael.paquier@gmail.com
In reply to: Fujii Masao (#28)
Re: Table-level log_autovacuum_min_duration

On Thu, Mar 19, 2015 at 12:23 PM, Fujii Masao <masao.fujii@gmail.com> wrote:

Are you planning to update the patch so that it's based on the commit 0d83138?

Yes... Very soon.
--
Michael

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

#30Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#29)
1 attachment(s)
Re: Table-level log_autovacuum_min_duration

On Thu, Mar 19, 2015 at 12:40 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Mar 19, 2015 at 12:23 PM, Fujii Masao <masao.fujii@gmail.com> wrote:

Are you planning to update the patch so that it's based on the commit 0d83138?

Yes... Very soon.

And here is the rebased patch.
--
Michael

Attachments:

20150319_autovacuum_log_relopts_v11.patchtext/x-diff; charset=US-ASCII; name=20150319_autovacuum_log_relopts_v11.patchDownload
From 9dbc9eb2660bf026a0fdb6b3c87be762d65f9b70 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Thu, 19 Mar 2015 13:35:47 +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         | 35 ++++++++++++++++++++--------------
 src/backend/commands/vacuum.c          |  7 +++++--
 src/backend/commands/vacuumlazy.c      |  8 ++++----
 src/backend/postmaster/autovacuum.c    |  7 +++++++
 src/bin/psql/tab-complete.c            |  2 ++
 src/include/commands/vacuum.h          |  5 ++++-
 src/include/utils/rel.h                |  1 +
 9 files changed, 68 insertions(+), 24 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 75b45f7..424af3a 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -85,7 +85,8 @@ static MemoryContext anl_context = NULL;
 static BufferAccessStrategy vac_strategy;
 
 
-static void do_analyze_rel(Relation onerel, int options, List *va_cols,
+static void do_analyze_rel(Relation onerel, int options,
+			   VacuumParams *params, List *va_cols,
 			   AcquireSampleRowsFunc acquirefunc, BlockNumber relpages,
 			   bool inh, bool in_outer_xact, int elevel);
 static void BlockSampler_Init(BlockSampler bs, BlockNumber nblocks,
@@ -115,14 +116,17 @@ static Datum ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull);
  *	analyze_rel() -- analyze one relation
  */
 void
-analyze_rel(Oid relid, RangeVar *relation, int options, List *va_cols,
-			bool in_outer_xact, BufferAccessStrategy bstrategy)
+analyze_rel(Oid relid, RangeVar *relation, int options,
+			VacuumParams *params, List *va_cols, bool in_outer_xact,
+			BufferAccessStrategy bstrategy)
 {
 	Relation	onerel;
 	int			elevel;
 	AcquireSampleRowsFunc acquirefunc = NULL;
 	BlockNumber relpages = 0;
 
+	Assert(params != NULL);
+
 	/* Select logging level */
 	if (options & VACOPT_VERBOSE)
 		elevel = INFO;
@@ -151,7 +155,7 @@ analyze_rel(Oid relid, RangeVar *relation, int options, List *va_cols,
 	else
 	{
 		onerel = NULL;
-		if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+		if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 			ereport(LOG,
 					(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
 				  errmsg("skipping analyze of \"%s\" --- lock not available",
@@ -266,14 +270,14 @@ analyze_rel(Oid relid, RangeVar *relation, int options, List *va_cols,
 	/*
 	 * Do the normal non-recursive ANALYZE.
 	 */
-	do_analyze_rel(onerel, options, va_cols, acquirefunc, relpages,
+	do_analyze_rel(onerel, options, params, va_cols, acquirefunc, relpages,
 				   false, in_outer_xact, elevel);
 
 	/*
 	 * If there are child tables, do recursive ANALYZE.
 	 */
 	if (onerel->rd_rel->relhassubclass)
-		do_analyze_rel(onerel, options, va_cols, acquirefunc, relpages,
+		do_analyze_rel(onerel, options, params, va_cols, acquirefunc, relpages,
 					   true, in_outer_xact, elevel);
 
 	/*
@@ -302,9 +306,10 @@ analyze_rel(Oid relid, RangeVar *relation, int options, List *va_cols,
  * acquirefunc for each child table.
  */
 static void
-do_analyze_rel(Relation onerel, int options, List *va_cols,
-			   AcquireSampleRowsFunc acquirefunc, BlockNumber relpages,
-			   bool inh, bool in_outer_xact, int elevel)
+do_analyze_rel(Relation onerel, int options, VacuumParams *params,
+			   List *va_cols, AcquireSampleRowsFunc acquirefunc,
+			   BlockNumber relpages, bool inh, bool in_outer_xact,
+			   int elevel)
 {
 	int			attr_cnt,
 				tcnt,
@@ -327,6 +332,8 @@ do_analyze_rel(Relation onerel, int options, List *va_cols,
 	int			save_sec_context;
 	int			save_nestlevel;
 
+	Assert(params != NULL);
+
 	if (inh)
 		ereport(elevel,
 				(errmsg("analyzing \"%s.%s\" inheritance tree",
@@ -360,10 +367,10 @@ do_analyze_rel(Relation onerel, int options, List *va_cols,
 	save_nestlevel = NewGUCNestLevel();
 
 	/* measure elapsed time iff autovacuum logging requires it */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 	{
 		pg_rusage_init(&ru0);
-		if (Log_autovacuum_min_duration > 0)
+		if (params->log_min_duration > 0)
 			starttime = GetCurrentTimestamp();
 	}
 
@@ -648,11 +655,11 @@ do_analyze_rel(Relation onerel, int options, List *va_cols,
 	vac_close_indexes(nindexes, Irel, NoLock);
 
 	/* Log the action if appropriate */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 	{
-		if (Log_autovacuum_min_duration == 0 ||
+		if (params->log_min_duration == 0 ||
 			TimestampDifferenceExceeds(starttime, GetCurrentTimestamp(),
-									   Log_autovacuum_min_duration))
+									   params->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 bd57b68..7ead161 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -114,6 +114,9 @@ ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
 	/* user-invoked vacuum is never "for wraparound" */
 	params.is_wraparound = false;
 
+	/* user-invoked vacuum never uses this parameter */
+	params.log_min_duration = -1;
+
 	/* Now go through the common routine */
 	vacuum(vacstmt->options, vacstmt->relation, InvalidOid, &params,
 		   vacstmt->va_cols, NULL, isTopLevel);
@@ -304,7 +307,7 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
 					PushActiveSnapshot(GetTransactionSnapshot());
 				}
 
-				analyze_rel(relid, relation, options,
+				analyze_rel(relid, relation, options, params,
 							va_cols, in_outer_xact, vac_strategy);
 
 				if (use_own_xacts)
@@ -1233,7 +1236,7 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 	else
 	{
 		onerel = NULL;
-		if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+		if (IsAutoVacuumWorkerProcess() && params->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 cd5ca4c..c3d6e59 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -196,7 +196,7 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 	Assert(params != NULL);
 
 	/* measure elapsed time iff autovacuum logging requires it */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 	{
 		pg_rusage_init(&ru0);
 		starttime = GetCurrentTimestamp();
@@ -328,13 +328,13 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 						 vacrelstats->new_dead_tuples);
 
 	/* and log the action if appropriate */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 	{
 		TimestampTz endtime = GetCurrentTimestamp();
 
-		if (Log_autovacuum_min_duration == 0 ||
+		if (params->log_min_duration == 0 ||
 			TimestampDifferenceExceeds(starttime, endtime,
-									   Log_autovacuum_min_duration))
+									   params->log_min_duration))
 		{
 			StringInfoData	buf;
 			TimestampDifference(starttime, endtime, &secs, &usecs);
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 5ccae24..72ca9e0 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2480,6 +2480,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
@@ -2502,6 +2503,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
@@ -2532,6 +2538,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 		tab->at_params.multixact_freeze_min_age = multixact_freeze_min_age;
 		tab->at_params.multixact_freeze_table_age = multixact_freeze_table_age;
 		tab->at_params.is_wraparound = wraparound;
+		tab->at_params.log_min_duration = log_min_duration;
 		tab->at_vacuum_cost_limit = vac_cost_limit;
 		tab->at_vacuum_cost_delay = vac_cost_delay;
 		tab->at_relname = NULL;
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/commands/vacuum.h b/src/include/commands/vacuum.h
index 9fd2516..71f0165 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -142,6 +142,9 @@ typedef struct VacuumParams
 	int		multixact_freeze_table_age;	/* multixact age at which to
 										 * scan whole table */
 	bool	is_wraparound;		/* force a for-wraparound vacuum */
+	int		log_min_duration;	/* minimum execution threshold in ms at
+								 * which  verbose logs are activated,
+								 * -1 to use default */
 } VacuumParams;
 
 /* GUC parameters */
@@ -191,7 +194,7 @@ extern void lazy_vacuum_rel(Relation onerel, int options,
 
 /* in commands/analyze.c */
 extern void analyze_rel(Oid relid, RangeVar *relation, int options,
-			List *va_cols, bool in_outer_xact,
+			VacuumParams *params, List *va_cols, bool in_outer_xact,
 			BufferAccessStrategy bstrategy);
 extern bool std_typanalyze(VacAttrStats *stats);
 extern double anl_random_fract(void);
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.2

#31Fujii Masao
masao.fujii@gmail.com
In reply to: Michael Paquier (#30)
Re: Table-level log_autovacuum_min_duration

On Thu, Mar 19, 2015 at 1:43 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Mar 19, 2015 at 12:40 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Mar 19, 2015 at 12:23 PM, Fujii Masao <masao.fujii@gmail.com> wrote:

Are you planning to update the patch so that it's based on the commit 0d83138?

Yes... Very soon.

And here is the rebased patch.

Thanks for rebasing the patch! Looks good to me.

One concern about this patch is; currently log_autovacuum_min_duration can be
changed even while autovacuum worker is running. So, for example, when
the admin notices that autovacuum is taking very long time, he or she can
enable logging of autovacuum activity on the fly. But this patch completely
prevents us from doing that, because, with the patch, autovacuum worker always
picks up the latest setting value at its starting time and then keeps using it
to the end. Probably I can live with this. But does anyone has other thought?

Regards,

--
Fujii Masao

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

#32Michael Paquier
michael.paquier@gmail.com
In reply to: Fujii Masao (#31)
Re: Table-level log_autovacuum_min_duration

On Mon, Mar 23, 2015 at 1:54 PM, Fujii Masao <masao.fujii@gmail.com> wrote:

On Thu, Mar 19, 2015 at 1:43 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Mar 19, 2015 at 12:40 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Mar 19, 2015 at 12:23 PM, Fujii Masao <masao.fujii@gmail.com> wrote:

Are you planning to update the patch so that it's based on the commit 0d83138?

Yes... Very soon.

And here is the rebased patch.

Thanks for rebasing the patch! Looks good to me.

One concern about this patch is; currently log_autovacuum_min_duration can be
changed even while autovacuum worker is running. So, for example, when
the admin notices that autovacuum is taking very long time, he or she can
enable logging of autovacuum activity on the fly. But this patch completely
prevents us from doing that, because, with the patch, autovacuum worker always
picks up the latest setting value at its starting time and then keeps using it
to the end. Probably I can live with this. But does anyone has other thought?

In AutoVacWorkerMain, I am reading the following:

* Currently, we don't pay attention to postgresql.conf changes that
* happen during a single daemon iteration, so we can ignore SIGHUP.
*/
pqsignal(SIGHUP, SIG_IGN);

So a worker does not see changes in postgresql.conf once it is run and
processes a database, no? The launcher does run ProcessConfigFile()
when SIGHUP shows up though.
Regards,
--
Michael

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

#33Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Michael Paquier (#32)
Re: Table-level log_autovacuum_min_duration

Michael Paquier wrote:

In AutoVacWorkerMain, I am reading the following:

* Currently, we don't pay attention to postgresql.conf changes that
* happen during a single daemon iteration, so we can ignore SIGHUP.
*/
pqsignal(SIGHUP, SIG_IGN);

So a worker does not see changes in postgresql.conf once it is run and
processes a database, no? The launcher does run ProcessConfigFile()
when SIGHUP shows up though.

Maybe this is something that we should change. For example, I wonder if
this can also affect the cost-delay balancing heuristics; if two
backends run the rebalance with different GUC settings because
postgresql.conf changed in between each of them starting, would the
settings bounce back and forth. I think it's worth reconsidering this.
(Don't really remember in detail how it works; maybe it's fine now.)

In any case, for log_autovacuum_min_duration it also seems worth keeping
reasonably close track of GUC changes. I think reading them just before
starting vacuum of a new relation should be enough.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

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

#34Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#33)
Re: Table-level log_autovacuum_min_duration

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

Michael Paquier wrote:

So a worker does not see changes in postgresql.conf once it is run and
processes a database, no? The launcher does run ProcessConfigFile()
when SIGHUP shows up though.

Maybe this is something that we should change.

Yeah, checking for SIGHUP in the worker outer loop (ie once per table)
seems like a reasonable thing.

regards, tom lane

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

#35Jeff Janes
jeff.janes@gmail.com
In reply to: Tom Lane (#34)
Re: Table-level log_autovacuum_min_duration

On Mon, Mar 23, 2015 at 7:07 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

Michael Paquier wrote:

So a worker does not see changes in postgresql.conf once it is run and
processes a database, no? The launcher does run ProcessConfigFile()
when SIGHUP shows up though.

Maybe this is something that we should change.

Yeah, checking for SIGHUP in the worker outer loop (ie once per table)
seems like a reasonable thing.

regards, tom lane

Could it be done more often? Maybe every time it is about to do a
cost_delay sleep?

I've certainly regretted the inability to
change autovacuum_vacuum_cost_delay mid-table on more than one occasion.

This was mostly during doing testing work, but still I'm sure other people
have run into this problem, perhaps without knowing it.

Cheers,

Jeff

#36Tom Lane
tgl@sss.pgh.pa.us
In reply to: Jeff Janes (#35)
Re: Table-level log_autovacuum_min_duration

Jeff Janes <jeff.janes@gmail.com> writes:

On Mon, Mar 23, 2015 at 7:07 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Yeah, checking for SIGHUP in the worker outer loop (ie once per table)
seems like a reasonable thing.

Could it be done more often? Maybe every time it is about to do a
cost_delay sleep?

That sounds risky from here. Normal backends don't check it more often
than once per command.

regards, tom lane

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

#37Michael Paquier
michael.paquier@gmail.com
In reply to: Tom Lane (#34)
Re: Table-level log_autovacuum_min_duration

On Mon, Mar 23, 2015 at 11:07 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

Michael Paquier wrote:

So a worker does not see changes in postgresql.conf once it is run and
processes a database, no? The launcher does run ProcessConfigFile()
when SIGHUP shows up though.

Maybe this is something that we should change.

Yeah, checking for SIGHUP in the worker outer loop (ie once per table)
seems like a reasonable thing.

That sounds fine to me as well. A patch would not be complicated, but
is this portion really 9.5 material?

Also, this is a discussion wider than only
log_autovacuum_min_duration, as autovacuum cost parameters are also
available as reloptions.
--
Michael

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

#38Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Michael Paquier (#37)
Re: Table-level log_autovacuum_min_duration

Michael Paquier wrote:

On Mon, Mar 23, 2015 at 11:07 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

Michael Paquier wrote:

So a worker does not see changes in postgresql.conf once it is run and
processes a database, no? The launcher does run ProcessConfigFile()
when SIGHUP shows up though.

Maybe this is something that we should change.

Yeah, checking for SIGHUP in the worker outer loop (ie once per table)
seems like a reasonable thing.

That sounds fine to me as well. A patch would not be complicated, but
is this portion really 9.5 material?

IMO yes.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

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

#39Michael Paquier
michael.paquier@gmail.com
In reply to: Alvaro Herrera (#38)
2 attachment(s)
Re: Table-level log_autovacuum_min_duration

On Mon, Mar 23, 2015 at 7:46 PM, Alvaro Herrera wrote:

Michael Paquier wrote:

On Mon, Mar 23, 2015 at 11:07 PM, Tom Lane wrote:

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

Michael Paquier wrote:

So a worker does not see changes in postgresql.conf once it is run and
processes a database, no? The launcher does run ProcessConfigFile()
when SIGHUP shows up though.

Maybe this is something that we should change.

Yeah, checking for SIGHUP in the worker outer loop (ie once per table)
seems like a reasonable thing.

That sounds fine to me as well. A patch would not be complicated, but
is this portion really 9.5 material?

IMO yes.

So, attached are two patches:
- 0001 enables SIGHUP tracking in do_autovacuum(), which is checked
before processing one table. I reused avl_sighup_handler for the
worker, renaming it av_sighup_handler..
- 0002 is the patch to add log_autovacuum_min_duration as a reloption.
There is nothing really changed, the value of
log_autovacuum_min_duration being picked up at startup with
table_recheck_autovac() is used until the end for one table.
Regards,
--
Michael

Attachments:

0001-Enable-tracking-of-SIGHUP-in-autovacuum-workers.patchapplication/x-patch; name=0001-Enable-tracking-of-SIGHUP-in-autovacuum-workers.patchDownload
From deebd45d5883c54d933ea03edc9cedf68179b653 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Wed, 25 Mar 2015 15:30:35 +0900
Subject: [PATCH 1/2] Enable tracking of SIGHUP in autovacuum workers

This check is done in the outer loop of the worker process before processing
one table, and this is useful for example to change dynamically autovacuum
cost parameters whiel processing one database with a worker.
---
 src/backend/postmaster/autovacuum.c | 26 +++++++++++++++++++-------
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 5ccae24..004a074 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -311,7 +311,7 @@ static PgStat_StatTabEntry *get_pgstat_tabentry_relid(Oid relid, bool isshared,
 						  PgStat_StatDBEntry *shared,
 						  PgStat_StatDBEntry *dbentry);
 static void autovac_report_activity(autovac_table *tab);
-static void avl_sighup_handler(SIGNAL_ARGS);
+static void av_sighup_handler(SIGNAL_ARGS);
 static void avl_sigusr2_handler(SIGNAL_ARGS);
 static void avl_sigterm_handler(SIGNAL_ARGS);
 static void autovac_refresh_stats(void);
@@ -419,7 +419,7 @@ AutoVacLauncherMain(int argc, char *argv[])
 	 * backend, so we use the same signal handling.  See equivalent code in
 	 * tcop/postgres.c.
 	 */
-	pqsignal(SIGHUP, avl_sighup_handler);
+	pqsignal(SIGHUP, av_sighup_handler);
 	pqsignal(SIGINT, StatementCancelHandler);
 	pqsignal(SIGTERM, avl_sigterm_handler);
 
@@ -1329,7 +1329,7 @@ AutoVacWorkerFailed(void)
 
 /* SIGHUP: set flag to re-read config file at next convenient time */
 static void
-avl_sighup_handler(SIGNAL_ARGS)
+av_sighup_handler(SIGNAL_ARGS)
 {
 	int			save_errno = errno;
 
@@ -1460,11 +1460,8 @@ AutoVacWorkerMain(int argc, char *argv[])
 	 * Set up signal handlers.  We operate on databases much like a regular
 	 * backend, so we use the same signal handling.  See equivalent code in
 	 * tcop/postgres.c.
-	 *
-	 * Currently, we don't pay attention to postgresql.conf changes that
-	 * happen during a single daemon iteration, so we can ignore SIGHUP.
 	 */
-	pqsignal(SIGHUP, SIG_IGN);
+	pqsignal(SIGHUP, av_sighup_handler);
 
 	/*
 	 * SIGINT is used to signal canceling the current table's vacuum; SIGTERM
@@ -2164,6 +2161,21 @@ do_autovacuum(void)
 		CHECK_FOR_INTERRUPTS();
 
 		/*
+		 * Check for SIGHUP before processing each collected table, this
+		 * is useful to balance dynamically autovacuum-related parameters,
+		 * like cost parameters, while running process on a database.
+		 */
+		if (got_SIGHUP)
+		{
+			got_SIGHUP = false;
+			ProcessConfigFile(PGC_SIGHUP);
+
+			/* shutdown requested in config file? */
+			if (!AutoVacuumingActive())
+				break;
+		}
+
+		/*
 		 * hold schedule lock from here until we're sure that this table still
 		 * needs vacuuming.  We also need the AutovacuumLock to walk the
 		 * worker array, but we'll let go of that one quickly.
-- 
2.3.4

0002-Make-log_autovacuum_min_duration-a-relation-option.patchapplication/x-patch; name=0002-Make-log_autovacuum_min_duration-a-relation-option.patchDownload
From a1fe1a615a714082bcb6debe959d134e3acaf635 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Wed, 25 Mar 2015 15:51:48 +0900
Subject: [PATCH 2/2] 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         | 35 ++++++++++++++++++++--------------
 src/backend/commands/vacuum.c          |  7 +++++--
 src/backend/commands/vacuumlazy.c      |  8 ++++----
 src/backend/postmaster/autovacuum.c    |  7 +++++++
 src/bin/psql/tab-complete.c            |  2 ++
 src/include/commands/vacuum.h          |  5 ++++-
 src/include/utils/rel.h                |  1 +
 9 files changed, 68 insertions(+), 24 deletions(-)

diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 324d593..f1ad1b5 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -881,9 +881,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>
@@ -1061,6 +1063,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 366c4af..ac3a71e 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -85,7 +85,8 @@ static MemoryContext anl_context = NULL;
 static BufferAccessStrategy vac_strategy;
 
 
-static void do_analyze_rel(Relation onerel, int options, List *va_cols,
+static void do_analyze_rel(Relation onerel, int options,
+			   VacuumParams *params, List *va_cols,
 			   AcquireSampleRowsFunc acquirefunc, BlockNumber relpages,
 			   bool inh, bool in_outer_xact, int elevel);
 static void BlockSampler_Init(BlockSampler bs, BlockNumber nblocks,
@@ -115,14 +116,17 @@ static Datum ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull);
  *	analyze_rel() -- analyze one relation
  */
 void
-analyze_rel(Oid relid, RangeVar *relation, int options, List *va_cols,
-			bool in_outer_xact, BufferAccessStrategy bstrategy)
+analyze_rel(Oid relid, RangeVar *relation, int options,
+			VacuumParams *params, List *va_cols, bool in_outer_xact,
+			BufferAccessStrategy bstrategy)
 {
 	Relation	onerel;
 	int			elevel;
 	AcquireSampleRowsFunc acquirefunc = NULL;
 	BlockNumber relpages = 0;
 
+	Assert(params != NULL);
+
 	/* Select logging level */
 	if (options & VACOPT_VERBOSE)
 		elevel = INFO;
@@ -151,7 +155,7 @@ analyze_rel(Oid relid, RangeVar *relation, int options, List *va_cols,
 	else
 	{
 		onerel = NULL;
-		if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+		if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 			ereport(LOG,
 					(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
 				  errmsg("skipping analyze of \"%s\" --- lock not available",
@@ -266,14 +270,14 @@ analyze_rel(Oid relid, RangeVar *relation, int options, List *va_cols,
 	/*
 	 * Do the normal non-recursive ANALYZE.
 	 */
-	do_analyze_rel(onerel, options, va_cols, acquirefunc, relpages,
+	do_analyze_rel(onerel, options, params, va_cols, acquirefunc, relpages,
 				   false, in_outer_xact, elevel);
 
 	/*
 	 * If there are child tables, do recursive ANALYZE.
 	 */
 	if (onerel->rd_rel->relhassubclass)
-		do_analyze_rel(onerel, options, va_cols, acquirefunc, relpages,
+		do_analyze_rel(onerel, options, params, va_cols, acquirefunc, relpages,
 					   true, in_outer_xact, elevel);
 
 	/*
@@ -301,9 +305,10 @@ analyze_rel(Oid relid, RangeVar *relation, int options, List *va_cols,
  * appropriate acquirefunc for each child table.
  */
 static void
-do_analyze_rel(Relation onerel, int options, List *va_cols,
-			   AcquireSampleRowsFunc acquirefunc, BlockNumber relpages,
-			   bool inh, bool in_outer_xact, int elevel)
+do_analyze_rel(Relation onerel, int options, VacuumParams *params,
+			   List *va_cols, AcquireSampleRowsFunc acquirefunc,
+			   BlockNumber relpages, bool inh, bool in_outer_xact,
+			   int elevel)
 {
 	int			attr_cnt,
 				tcnt,
@@ -326,6 +331,8 @@ do_analyze_rel(Relation onerel, int options, List *va_cols,
 	int			save_sec_context;
 	int			save_nestlevel;
 
+	Assert(params != NULL);
+
 	if (inh)
 		ereport(elevel,
 				(errmsg("analyzing \"%s.%s\" inheritance tree",
@@ -359,10 +366,10 @@ do_analyze_rel(Relation onerel, int options, List *va_cols,
 	save_nestlevel = NewGUCNestLevel();
 
 	/* measure elapsed time iff autovacuum logging requires it */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 	{
 		pg_rusage_init(&ru0);
-		if (Log_autovacuum_min_duration > 0)
+		if (params->log_min_duration > 0)
 			starttime = GetCurrentTimestamp();
 	}
 
@@ -647,11 +654,11 @@ do_analyze_rel(Relation onerel, int options, List *va_cols,
 	vac_close_indexes(nindexes, Irel, NoLock);
 
 	/* Log the action if appropriate */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 	{
-		if (Log_autovacuum_min_duration == 0 ||
+		if (params->log_min_duration == 0 ||
 			TimestampDifferenceExceeds(starttime, GetCurrentTimestamp(),
-									   Log_autovacuum_min_duration))
+									   params->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 bd57b68..7ead161 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -114,6 +114,9 @@ ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
 	/* user-invoked vacuum is never "for wraparound" */
 	params.is_wraparound = false;
 
+	/* user-invoked vacuum never uses this parameter */
+	params.log_min_duration = -1;
+
 	/* Now go through the common routine */
 	vacuum(vacstmt->options, vacstmt->relation, InvalidOid, &params,
 		   vacstmt->va_cols, NULL, isTopLevel);
@@ -304,7 +307,7 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
 					PushActiveSnapshot(GetTransactionSnapshot());
 				}
 
-				analyze_rel(relid, relation, options,
+				analyze_rel(relid, relation, options, params,
 							va_cols, in_outer_xact, vac_strategy);
 
 				if (use_own_xacts)
@@ -1233,7 +1236,7 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 	else
 	{
 		onerel = NULL;
-		if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+		if (IsAutoVacuumWorkerProcess() && params->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 cd5ca4c..c3d6e59 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -196,7 +196,7 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 	Assert(params != NULL);
 
 	/* measure elapsed time iff autovacuum logging requires it */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 	{
 		pg_rusage_init(&ru0);
 		starttime = GetCurrentTimestamp();
@@ -328,13 +328,13 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 						 vacrelstats->new_dead_tuples);
 
 	/* and log the action if appropriate */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 	{
 		TimestampTz endtime = GetCurrentTimestamp();
 
-		if (Log_autovacuum_min_duration == 0 ||
+		if (params->log_min_duration == 0 ||
 			TimestampDifferenceExceeds(starttime, endtime,
-									   Log_autovacuum_min_duration))
+									   params->log_min_duration))
 		{
 			StringInfoData	buf;
 			TimestampDifference(starttime, endtime, &secs, &usecs);
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 004a074..1dc0bdc 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2492,6 +2492,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
@@ -2514,6 +2515,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
@@ -2544,6 +2550,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 		tab->at_params.multixact_freeze_min_age = multixact_freeze_min_age;
 		tab->at_params.multixact_freeze_table_age = multixact_freeze_table_age;
 		tab->at_params.is_wraparound = wraparound;
+		tab->at_params.log_min_duration = log_min_duration;
 		tab->at_vacuum_cost_limit = vac_cost_limit;
 		tab->at_vacuum_cost_delay = vac_cost_delay;
 		tab->at_relname = NULL;
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/commands/vacuum.h b/src/include/commands/vacuum.h
index 9fd2516..71f0165 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -142,6 +142,9 @@ typedef struct VacuumParams
 	int		multixact_freeze_table_age;	/* multixact age at which to
 										 * scan whole table */
 	bool	is_wraparound;		/* force a for-wraparound vacuum */
+	int		log_min_duration;	/* minimum execution threshold in ms at
+								 * which  verbose logs are activated,
+								 * -1 to use default */
 } VacuumParams;
 
 /* GUC parameters */
@@ -191,7 +194,7 @@ extern void lazy_vacuum_rel(Relation onerel, int options,
 
 /* in commands/analyze.c */
 extern void analyze_rel(Oid relid, RangeVar *relation, int options,
-			List *va_cols, bool in_outer_xact,
+			VacuumParams *params, List *va_cols, bool in_outer_xact,
 			BufferAccessStrategy bstrategy);
 extern bool std_typanalyze(VacAttrStats *stats);
 extern double anl_random_fract(void);
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.4

#40Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Michael Paquier (#39)
Re: Table-level log_autovacuum_min_duration

Michael Paquier wrote:

So, attached are two patches:
- 0001 enables SIGHUP tracking in do_autovacuum(), which is checked
before processing one table. I reused avl_sighup_handler for the
worker, renaming it av_sighup_handler..
- 0002 is the patch to add log_autovacuum_min_duration as a reloption.
There is nothing really changed, the value of
log_autovacuum_min_duration being picked up at startup with
table_recheck_autovac() is used until the end for one table.

Thanks.

You added this in the worker loop processing each table:

/*
* Check for config changes before processing each collected table.
*/
if (got_SIGHUP)
{
got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);

/* shutdown requested in config file? */
if (!AutoVacuumingActive())
break;
}

I think bailing out if autovac is disabled is a good idea; for instance,
if this is a for-wraparound vacuum, we should keep running. My first
reaction is that this part of the test ought to be moved down a
screenful or so, until we've ran the recheck step and we have the
for-wraparound flag in hand; only bail out if that one is not set. But
on the other hand, maybe the worker will process some tables that are
not for-wraparound in between some other tables that are, so that might
turn out to be a bad idea as well. (Though I'm not 100% that it works
that way; consider commit f51ead09df for instance.) Maybe the test to
use for this is something along the lines of "if autovacuum was enabled
before and is no longer enabled, bail out, otherwise keep running".
This implies that we need to keep track of whether autovac was enabled
at the start of the worker run.

Another thing, but not directly related to this patch: while looking at
the doc changes, I noticed that we don't have index entries for the
reloptions we have; for instance, the word "fillfactor" doesn't appear
in the bookindex.html page at all. Having these things all crammed in
the CREATE TABLE page seems somewhat poor. I think we could improve on
this by having a listing of settable parameters in a separate section,
and have CREATE TABLE, ALTER TABLE, and the Server Configuration chapter
link to that; we could also have index entries for these items.

Of course, the simpler fix is to just add a few <indexterm> tags.

As a note, there is no point to "Assert(foo != NULL)" tests when foo is
later dereferenced in the same routine: the crash is going to happen
anyway at the dereference, so it's just a LOC uselessly wasted.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

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

#41Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Michael Paquier (#39)
Re: Table-level log_autovacuum_min_duration
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -881,9 +881,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>

I think this could use some wordsmithing; I didn't like listing the
parameters explicitely, and Jaime Casanova suggested not using the terms
"inherit" and "parent table" in this context. So I came up with this:

Note that the TOAST table uses the parameter values defined for the main
table, for each parameter applicable to TOAST tables and for which no
value is set in the TOAST table itself.

Thoughts?

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

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

#42Michael Paquier
michael.paquier@gmail.com
In reply to: Alvaro Herrera (#40)
2 attachment(s)
Re: Table-level log_autovacuum_min_duration

On Fri, Apr 3, 2015 at 5:57 AM, Alvaro Herrera wrote:

You added this in the worker loop processing each table:

/*
* Check for config changes before processing each collected table.
*/
if (got_SIGHUP)
{
got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);

/* shutdown requested in config file? */
if (!AutoVacuumingActive())
break;
}

I think bailing out if autovac is disabled is a good idea; for instance,
if this is a for-wraparound vacuum, we should keep running. My first
reaction is that this part of the test ought to be moved down a
screenful or so, until we've ran the recheck step and we have the
for-wraparound flag in hand; only bail out if that one is not set. But
on the other hand, maybe the worker will process some tables that are
not for-wraparound in between some other tables that are, so that might
turn out to be a bad idea as well. (Though I'm not 100% that it works
that way; consider commit f51ead09df for instance.) Maybe the test to
use for this is something along the lines of "if autovacuum was enabled
before and is no longer enabled, bail out, otherwise keep running".
This implies that we need to keep track of whether autovac was enabled
at the start of the worker run.

I did consider the case of wraparound but came to the conclusion that
bailing out as fast as possible was the idea. But well, I guess that
we could play it safe and not add this check after all. The main
use-case of this change is now to be able to do re-balancing of the
cost parameters. We could still decide later if a worker could bail
out earlier or not, depending on what.

Another thing, but not directly related to this patch: while looking at
the doc changes, I noticed that we don't have index entries for the
reloptions we have; for instance, the word "fillfactor" doesn't appear
in the bookindex.html page at all. Having these things all crammed in
the CREATE TABLE page seems somewhat poor. I think we could improve on
this by having a listing of settable parameters in a separate section,
and have CREATE TABLE, ALTER TABLE, and the Server Configuration chapter
link to that; we could also have index entries for these items.
Of course, the simpler fix is to just add a few <indexterm> tags.

This sounds like a good idea, and this refactoring would meritate a
different patch and a different thread. I guess that it should be a
new section in Server Configuration then, named "Relation Options",
with two different subsections for index options and table options.

As a note, there is no point to "Assert(foo != NULL)" tests when foo is
later dereferenced in the same routine: the crash is going to happen
anyway at the dereference, so it's just a LOC uselessly wasted.

Check. I still think that it is useful in this case though... But removed.

I think this could use some wordsmithing; I didn't like listing the
parameters explicitely, and Jaime Casanova suggested not using the terms
"inherit" and "parent table" in this context. So I came up with this:
Note that the TOAST table uses the parameter values defined for the main
table, for each parameter applicable to TOAST tables and for which no
value is set in the TOAST table itself.

Fine for me.
--
Michael

Attachments:

0001-Add-palloc_extended-and-pg_malloc_extended-for-front.patchtext/x-diff; charset=US-ASCII; name=0001-Add-palloc_extended-and-pg_malloc_extended-for-front.patchDownload
From f43fc9a5a5e0ffb246782010b8860829b8304593 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Fri, 3 Apr 2015 14:21:12 +0900
Subject: [PATCH 1/2] Add palloc_extended and pg_malloc_extended for frontend
 and backend

This interface can be used to control at a lower level memory allocation
using an interface similar to MemoryContextAllocExtended, but this time
applied to CurrentMemoryContext on backend side, while on frontend side
a similar interface is available.
---
 src/backend/utils/mmgr/mcxt.c    | 37 ++++++++++++++++++++++++++++++++++
 src/common/fe_memutils.c         | 43 ++++++++++++++++++++++++++++++----------
 src/include/common/fe_memutils.h | 11 ++++++++++
 src/include/utils/palloc.h       |  1 +
 4 files changed, 81 insertions(+), 11 deletions(-)

diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index e2fbfd4..c42a6b6 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -864,6 +864,43 @@ palloc0(Size size)
 	return ret;
 }
 
+void *
+palloc_extended(Size size, int flags)
+{
+	/* duplicates MemoryContextAllocExtended to avoid increased overhead */
+	void	   *ret;
+
+	AssertArg(MemoryContextIsValid(CurrentMemoryContext));
+	AssertNotInCriticalSection(CurrentMemoryContext);
+
+	if (((flags & MCXT_ALLOC_HUGE) != 0 && !AllocHugeSizeIsValid(size)) ||
+		((flags & MCXT_ALLOC_HUGE) == 0 && !AllocSizeIsValid(size)))
+		elog(ERROR, "invalid memory alloc request size %zu", size);
+
+	CurrentMemoryContext->isReset = false;
+
+	ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size);
+	if (ret == NULL)
+	{
+		if ((flags & MCXT_ALLOC_NO_OOM) == 0)
+		{
+			MemoryContextStats(TopMemoryContext);
+			ereport(ERROR,
+					(errcode(ERRCODE_OUT_OF_MEMORY),
+					 errmsg("out of memory"),
+					 errdetail("Failed on request of size %zu.", size)));
+		}
+		return NULL;
+	}
+
+	VALGRIND_MEMPOOL_ALLOC(CurrentMemoryContext, ret, size);
+
+	if ((flags & MCXT_ALLOC_ZERO) != 0)
+		MemSetAligned(ret, 0, size);
+
+	return ret;
+}
+
 /*
  * pfree
  *		Release an allocated chunk.
diff --git a/src/common/fe_memutils.c b/src/common/fe_memutils.c
index 345221e..527f9c8 100644
--- a/src/common/fe_memutils.c
+++ b/src/common/fe_memutils.c
@@ -19,8 +19,8 @@
 
 #include "postgres_fe.h"
 
-void *
-pg_malloc(size_t size)
+static inline void *
+pg_malloc_internal(size_t size, int flags)
 {
 	void	   *tmp;
 
@@ -28,22 +28,37 @@ pg_malloc(size_t size)
 	if (size == 0)
 		size = 1;
 	tmp = malloc(size);
-	if (!tmp)
+	if (tmp == NULL)
 	{
-		fprintf(stderr, _("out of memory\n"));
-		exit(EXIT_FAILURE);
+		if ((flags & MCXT_ALLOC_NO_OOM) == 0)
+		{
+			fprintf(stderr, _("out of memory\n"));
+			exit(EXIT_FAILURE);
+		}
+		return NULL;
 	}
+
+	if ((flags & MCXT_ALLOC_ZERO) != 0)
+		MemSet(tmp, 0, size);
 	return tmp;
 }
 
 void *
+pg_malloc(size_t size)
+{
+	return pg_malloc_internal(size, 0);
+}
+
+void *
 pg_malloc0(size_t size)
 {
-	void	   *tmp;
+	return pg_malloc_internal(size, MCXT_ALLOC_ZERO);
+}
 
-	tmp = pg_malloc(size);
-	MemSet(tmp, 0, size);
-	return tmp;
+void *
+pg_malloc_extended(size_t size, int flags)
+{
+	return pg_malloc_internal(size, flags);
 }
 
 void *
@@ -100,13 +115,19 @@ pg_free(void *ptr)
 void *
 palloc(Size size)
 {
-	return pg_malloc(size);
+	return pg_malloc_internal(size, 0);
 }
 
 void *
 palloc0(Size size)
 {
-	return pg_malloc0(size);
+	return pg_malloc_internal(size, MCXT_ALLOC_ZERO);
+}
+
+void *
+palloc_extended(Size size, int flags)
+{
+	return pg_malloc_internal(size, flags);
 }
 
 void
diff --git a/src/include/common/fe_memutils.h b/src/include/common/fe_memutils.h
index db7cb3e..6466167 100644
--- a/src/include/common/fe_memutils.h
+++ b/src/include/common/fe_memutils.h
@@ -9,10 +9,20 @@
 #ifndef FE_MEMUTILS_H
 #define FE_MEMUTILS_H
 
+/*
+ * Flags for pg_malloc_extended and palloc_extended, deliberately named
+ * the same as the backend flags.
+ */
+#define MCXT_ALLOC_HUGE			0x01	/* allow huge allocation (> 1 GB)
+										 * not actually used for frontends */
+#define MCXT_ALLOC_NO_OOM		0x02	/* no failure if out-of-memory */
+#define MCXT_ALLOC_ZERO			0x04	/* zero allocated memory */
+
 /* "Safe" memory allocation functions --- these exit(1) on failure */
 extern char *pg_strdup(const char *in);
 extern void *pg_malloc(size_t size);
 extern void *pg_malloc0(size_t size);
+extern void *pg_malloc_extended(size_t size, int flags);
 extern void *pg_realloc(void *pointer, size_t size);
 extern void pg_free(void *pointer);
 
@@ -20,6 +30,7 @@ extern void pg_free(void *pointer);
 extern char *pstrdup(const char *in);
 extern void *palloc(Size size);
 extern void *palloc0(Size size);
+extern void *palloc_extended(Size size, int flags);
 extern void *repalloc(void *pointer, Size size);
 extern void pfree(void *pointer);
 
diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h
index 2cf5129..9861f0d 100644
--- a/src/include/utils/palloc.h
+++ b/src/include/utils/palloc.h
@@ -76,6 +76,7 @@ extern void *MemoryContextAllocExtended(MemoryContext context,
 
 extern void *palloc(Size size);
 extern void *palloc0(Size size);
+extern void *palloc_extended(Size size, int flags);
 extern void *repalloc(void *pointer, Size size);
 extern void pfree(void *pointer);
 
-- 
2.3.5

0002-Rework-handling-of-OOM-when-allocating-record-buffer.patchtext/x-diff; charset=US-ASCII; name=0002-Rework-handling-of-OOM-when-allocating-record-buffer.patchDownload
From d85e506baa258643ac820b6f21ac3c86247b3cc2 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Fri, 3 Apr 2015 14:28:21 +0900
Subject: [PATCH 2/2] Rework handling of OOM when allocating record buffer in
 XLOG reader

Commit 2c03216 has replaced the memory allocation of the read buffer by a
palloc while it was a malloc previously. However palloc fails unconditionally
if an out-of-memory error shows up instead of returning NULL as a malloc
would do. The broken logic is fixed using palloc_extended which is available
for both frontend and backends, a routine able to not fail should an OOM occur
when doing an allocation.
---
 src/backend/access/transam/xlogreader.c   | 9 ++++++++-
 src/backend/replication/logical/logical.c | 5 +++++
 src/bin/pg_rewind/parsexlog.c             | 6 ++++++
 3 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index ba7dfcc..f3cf6a5 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -146,7 +146,14 @@ allocate_recordbuf(XLogReaderState *state, uint32 reclength)
 
 	if (state->readRecordBuf)
 		pfree(state->readRecordBuf);
-	state->readRecordBuf = (char *) palloc(newSize);
+
+	state->readRecordBuf = (char *) palloc_extended(newSize,
+													MCXT_ALLOC_NO_OOM);
+	if (state->readRecordBuf == NULL)
+	{
+		state->readRecordBufSize = 0;
+		return false;
+	}
 	state->readRecordBufSize = newSize;
 	return true;
 }
diff --git a/src/backend/replication/logical/logical.c b/src/backend/replication/logical/logical.c
index 30baa45..774ebbc 100644
--- a/src/backend/replication/logical/logical.c
+++ b/src/backend/replication/logical/logical.c
@@ -163,6 +163,11 @@ StartupDecodingContext(List *output_plugin_options,
 	ctx->slot = slot;
 
 	ctx->reader = XLogReaderAllocate(read_page, ctx);
+	if (!ctx->reader)
+		ereport(ERROR,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory")));
+
 	ctx->reader->private_data = ctx;
 
 	ctx->reorder = ReorderBufferAllocate();
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
index 0787ca1..3cf96ab 100644
--- a/src/bin/pg_rewind/parsexlog.c
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -70,6 +70,8 @@ extractPageMap(const char *datadir, XLogRecPtr startpoint, TimeLineID tli,
 	private.datadir = datadir;
 	private.tli = tli;
 	xlogreader = XLogReaderAllocate(&SimpleXLogPageRead, &private);
+	if (xlogreader == NULL)
+		pg_fatal("out of memory");
 
 	do
 	{
@@ -121,6 +123,8 @@ readOneRecord(const char *datadir, XLogRecPtr ptr, TimeLineID tli)
 	private.datadir = datadir;
 	private.tli = tli;
 	xlogreader = XLogReaderAllocate(&SimpleXLogPageRead, &private);
+	if (xlogreader == NULL)
+		pg_fatal("out of memory");
 
 	record = XLogReadRecord(xlogreader, ptr, &errormsg);
 	if (record == NULL)
@@ -171,6 +175,8 @@ findLastCheckpoint(const char *datadir, XLogRecPtr forkptr, TimeLineID tli,
 	private.datadir = datadir;
 	private.tli = tli;
 	xlogreader = XLogReaderAllocate(&SimpleXLogPageRead, &private);
+	if (xlogreader == NULL)
+		pg_fatal("out of memory");
 
 	searchptr = forkptr;
 	for (;;)
-- 
2.3.5

#43Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#42)
2 attachment(s)
Re: Table-level log_autovacuum_min_duration

On Fri, Apr 3, 2015 at 3:26 PM, Michael Paquier wrote:

[...]
Fine for me.

And here are the correct patches. Sorry for that.
--
Michael

Attachments:

0001-Enable-tracking-of-SIGHUP-in-autovacuum-workers.patchtext/x-diff; charset=US-ASCII; name=0001-Enable-tracking-of-SIGHUP-in-autovacuum-workers.patchDownload
From e6f1134dc96045893ba1e4eaa4f887ecf4a6fcbd Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Fri, 3 Apr 2015 15:16:49 +0900
Subject: [PATCH 1/2] Enable tracking of SIGHUP in autovacuum workers

This check is done in the outer loop of the worker process before processing
one table, and this is useful for example to change dynamically autovacuum
cost parameters whiel processing one database with a worker.
---
 src/backend/postmaster/autovacuum.c | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 080c3e6..9d2ba94 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -311,7 +311,7 @@ static PgStat_StatTabEntry *get_pgstat_tabentry_relid(Oid relid, bool isshared,
 						  PgStat_StatDBEntry *shared,
 						  PgStat_StatDBEntry *dbentry);
 static void autovac_report_activity(autovac_table *tab);
-static void avl_sighup_handler(SIGNAL_ARGS);
+static void av_sighup_handler(SIGNAL_ARGS);
 static void avl_sigusr2_handler(SIGNAL_ARGS);
 static void avl_sigterm_handler(SIGNAL_ARGS);
 static void autovac_refresh_stats(void);
@@ -419,7 +419,7 @@ AutoVacLauncherMain(int argc, char *argv[])
 	 * backend, so we use the same signal handling.  See equivalent code in
 	 * tcop/postgres.c.
 	 */
-	pqsignal(SIGHUP, avl_sighup_handler);
+	pqsignal(SIGHUP, av_sighup_handler);
 	pqsignal(SIGINT, StatementCancelHandler);
 	pqsignal(SIGTERM, avl_sigterm_handler);
 
@@ -1329,7 +1329,7 @@ AutoVacWorkerFailed(void)
 
 /* SIGHUP: set flag to re-read config file at next convenient time */
 static void
-avl_sighup_handler(SIGNAL_ARGS)
+av_sighup_handler(SIGNAL_ARGS)
 {
 	int			save_errno = errno;
 
@@ -1460,11 +1460,8 @@ AutoVacWorkerMain(int argc, char *argv[])
 	 * Set up signal handlers.  We operate on databases much like a regular
 	 * backend, so we use the same signal handling.  See equivalent code in
 	 * tcop/postgres.c.
-	 *
-	 * Currently, we don't pay attention to postgresql.conf changes that
-	 * happen during a single daemon iteration, so we can ignore SIGHUP.
 	 */
-	pqsignal(SIGHUP, SIG_IGN);
+	pqsignal(SIGHUP, av_sighup_handler);
 
 	/*
 	 * SIGINT is used to signal canceling the current table's vacuum; SIGTERM
@@ -2164,6 +2161,17 @@ do_autovacuum(void)
 		CHECK_FOR_INTERRUPTS();
 
 		/*
+		 * Check for SIGHUP before processing each collected table, this
+		 * is useful to balance dynamically autovacuum-related parameters,
+		 * like cost parameters, while running process on a database.
+		 */
+		if (got_SIGHUP)
+		{
+			got_SIGHUP = false;
+			ProcessConfigFile(PGC_SIGHUP);
+		}
+
+		/*
 		 * hold schedule lock from here until we're sure that this table still
 		 * needs vacuuming.  We also need the AutovacuumLock to walk the
 		 * worker array, but we'll let go of that one quickly.
-- 
2.3.5

0002-Make-log_autovacuum_min_duration-a-relation-option.patchtext/x-diff; charset=US-ASCII; name=0002-Make-log_autovacuum_min_duration-a-relation-option.patchDownload
From 9b41184ebbe391a7f3e3ae8129c40105f431c972 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Fri, 3 Apr 2015 15:24:36 +0900
Subject: [PATCH 2/2] 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     | 15 ++++++++++++---
 src/backend/access/common/reloptions.c | 10 ++++++++++
 src/backend/commands/analyze.c         | 31 +++++++++++++++++--------------
 src/backend/commands/vacuum.c          |  7 +++++--
 src/backend/commands/vacuumlazy.c      |  8 ++++----
 src/backend/postmaster/autovacuum.c    |  7 +++++++
 src/bin/psql/tab-complete.c            |  2 ++
 src/include/commands/vacuum.h          |  5 ++++-
 src/include/utils/rel.h                |  1 +
 9 files changed, 62 insertions(+), 24 deletions(-)

diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 324d593..4dc0052 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -881,9 +881,9 @@ 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 uses the parameter values defined for the main
+    table, for each parameter applicable to TOAST tables and for which no
+    value is set in the TOAST table itself.
    </para>
 
    <variablelist>
@@ -1061,6 +1061,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 d4d1914..15ec0ad 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -85,7 +85,8 @@ static MemoryContext anl_context = NULL;
 static BufferAccessStrategy vac_strategy;
 
 
-static void do_analyze_rel(Relation onerel, int options, List *va_cols,
+static void do_analyze_rel(Relation onerel, int options,
+			   VacuumParams *params, List *va_cols,
 			   AcquireSampleRowsFunc acquirefunc, BlockNumber relpages,
 			   bool inh, bool in_outer_xact, int elevel);
 static void BlockSampler_Init(BlockSampler bs, BlockNumber nblocks,
@@ -115,8 +116,9 @@ static Datum ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull);
  *	analyze_rel() -- analyze one relation
  */
 void
-analyze_rel(Oid relid, RangeVar *relation, int options, List *va_cols,
-			bool in_outer_xact, BufferAccessStrategy bstrategy)
+analyze_rel(Oid relid, RangeVar *relation, int options,
+			VacuumParams *params, List *va_cols, bool in_outer_xact,
+			BufferAccessStrategy bstrategy)
 {
 	Relation	onerel;
 	int			elevel;
@@ -151,7 +153,7 @@ analyze_rel(Oid relid, RangeVar *relation, int options, List *va_cols,
 	else
 	{
 		onerel = NULL;
-		if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+		if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 			ereport(LOG,
 					(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
 				  errmsg("skipping analyze of \"%s\" --- lock not available",
@@ -266,14 +268,14 @@ analyze_rel(Oid relid, RangeVar *relation, int options, List *va_cols,
 	/*
 	 * Do the normal non-recursive ANALYZE.
 	 */
-	do_analyze_rel(onerel, options, va_cols, acquirefunc, relpages,
+	do_analyze_rel(onerel, options, params, va_cols, acquirefunc, relpages,
 				   false, in_outer_xact, elevel);
 
 	/*
 	 * If there are child tables, do recursive ANALYZE.
 	 */
 	if (onerel->rd_rel->relhassubclass)
-		do_analyze_rel(onerel, options, va_cols, acquirefunc, relpages,
+		do_analyze_rel(onerel, options, params, va_cols, acquirefunc, relpages,
 					   true, in_outer_xact, elevel);
 
 	/*
@@ -301,9 +303,10 @@ analyze_rel(Oid relid, RangeVar *relation, int options, List *va_cols,
  * appropriate acquirefunc for each child table.
  */
 static void
-do_analyze_rel(Relation onerel, int options, List *va_cols,
-			   AcquireSampleRowsFunc acquirefunc, BlockNumber relpages,
-			   bool inh, bool in_outer_xact, int elevel)
+do_analyze_rel(Relation onerel, int options, VacuumParams *params,
+			   List *va_cols, AcquireSampleRowsFunc acquirefunc,
+			   BlockNumber relpages, bool inh, bool in_outer_xact,
+			   int elevel)
 {
 	int			attr_cnt,
 				tcnt,
@@ -359,10 +362,10 @@ do_analyze_rel(Relation onerel, int options, List *va_cols,
 	save_nestlevel = NewGUCNestLevel();
 
 	/* measure elapsed time iff autovacuum logging requires it */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 	{
 		pg_rusage_init(&ru0);
-		if (Log_autovacuum_min_duration > 0)
+		if (params->log_min_duration > 0)
 			starttime = GetCurrentTimestamp();
 	}
 
@@ -647,11 +650,11 @@ do_analyze_rel(Relation onerel, int options, List *va_cols,
 	vac_close_indexes(nindexes, Irel, NoLock);
 
 	/* Log the action if appropriate */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 	{
-		if (Log_autovacuum_min_duration == 0 ||
+		if (params->log_min_duration == 0 ||
 			TimestampDifferenceExceeds(starttime, GetCurrentTimestamp(),
-									   Log_autovacuum_min_duration))
+									   params->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 bd57b68..7ead161 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -114,6 +114,9 @@ ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
 	/* user-invoked vacuum is never "for wraparound" */
 	params.is_wraparound = false;
 
+	/* user-invoked vacuum never uses this parameter */
+	params.log_min_duration = -1;
+
 	/* Now go through the common routine */
 	vacuum(vacstmt->options, vacstmt->relation, InvalidOid, &params,
 		   vacstmt->va_cols, NULL, isTopLevel);
@@ -304,7 +307,7 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
 					PushActiveSnapshot(GetTransactionSnapshot());
 				}
 
-				analyze_rel(relid, relation, options,
+				analyze_rel(relid, relation, options, params,
 							va_cols, in_outer_xact, vac_strategy);
 
 				if (use_own_xacts)
@@ -1233,7 +1236,7 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
 	else
 	{
 		onerel = NULL;
-		if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+		if (IsAutoVacuumWorkerProcess() && params->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 cd5ca4c..c3d6e59 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -196,7 +196,7 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 	Assert(params != NULL);
 
 	/* measure elapsed time iff autovacuum logging requires it */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 	{
 		pg_rusage_init(&ru0);
 		starttime = GetCurrentTimestamp();
@@ -328,13 +328,13 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
 						 vacrelstats->new_dead_tuples);
 
 	/* and log the action if appropriate */
-	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+	if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
 	{
 		TimestampTz endtime = GetCurrentTimestamp();
 
-		if (Log_autovacuum_min_duration == 0 ||
+		if (params->log_min_duration == 0 ||
 			TimestampDifferenceExceeds(starttime, endtime,
-									   Log_autovacuum_min_duration))
+									   params->log_min_duration))
 		{
 			StringInfoData	buf;
 			TimestampDifference(starttime, endtime, &secs, &usecs);
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 9d2ba94..754e2b2 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2488,6 +2488,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
@@ -2510,6 +2511,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
@@ -2540,6 +2546,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 		tab->at_params.multixact_freeze_min_age = multixact_freeze_min_age;
 		tab->at_params.multixact_freeze_table_age = multixact_freeze_table_age;
 		tab->at_params.is_wraparound = wraparound;
+		tab->at_params.log_min_duration = log_min_duration;
 		tab->at_vacuum_cost_limit = vac_cost_limit;
 		tab->at_vacuum_cost_delay = vac_cost_delay;
 		tab->at_relname = NULL;
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 5a4fd5c..38fde39 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/commands/vacuum.h b/src/include/commands/vacuum.h
index 9fd2516..71f0165 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -142,6 +142,9 @@ typedef struct VacuumParams
 	int		multixact_freeze_table_age;	/* multixact age at which to
 										 * scan whole table */
 	bool	is_wraparound;		/* force a for-wraparound vacuum */
+	int		log_min_duration;	/* minimum execution threshold in ms at
+								 * which  verbose logs are activated,
+								 * -1 to use default */
 } VacuumParams;
 
 /* GUC parameters */
@@ -191,7 +194,7 @@ extern void lazy_vacuum_rel(Relation onerel, int options,
 
 /* in commands/analyze.c */
 extern void analyze_rel(Oid relid, RangeVar *relation, int options,
-			List *va_cols, bool in_outer_xact,
+			VacuumParams *params, List *va_cols, bool in_outer_xact,
 			BufferAccessStrategy bstrategy);
 extern bool std_typanalyze(VacAttrStats *stats);
 extern double anl_random_fract(void);
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.5

#44Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Michael Paquier (#43)
Re: Table-level log_autovacuum_min_duration

Michael Paquier wrote:

On Fri, Apr 3, 2015 at 3:26 PM, Michael Paquier wrote:

[...]
Fine for me.

And here are the correct patches. Sorry for that.

Thanks, pushed. I added one extra comment to the SIGHUP patch in the
place where you previously had the exit.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

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

#45Michael Paquier
michael.paquier@gmail.com
In reply to: Alvaro Herrera (#44)
Re: Table-level log_autovacuum_min_duration

On Fri, Apr 3, 2015 at 11:59 PM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Michael Paquier wrote:

On Fri, Apr 3, 2015 at 3:26 PM, Michael Paquier wrote:

[...]
Fine for me.

And here are the correct patches. Sorry for that.

Thanks, pushed. I added one extra comment to the SIGHUP patch in the
place where you previously had the exit.

Thanks!
--
Michael

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