Avoid having autovacuum workers wait for relation locks.

Waiting for relation locks can lead to starvation - it pins down an
autovacuum worker for as long as the lock is held.  But if we're doing
an anti-wraparound vacuum, then we still wait; maintenance can no longer
be put off.

To assist with troubleshooting, if log_autovacuum_min_duration >= 0,
we log whenever an autovacuum or autoanalyze is skipped for this reason.

Per a gripe by Josh Berkus, and ensuing discussion.
This commit is contained in:
Robert Haas 2011-02-07 22:04:29 -05:00
parent 47082fa875
commit 32896c40ca
5 changed files with 61 additions and 15 deletions

View File

@ -4080,7 +4080,10 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
all autovacuum actions. Minus-one (the default) disables logging
autovacuum actions. For example, if you set this to
<literal>250ms</literal> then all automatic vacuums and analyzes that run
250ms or longer will be logged. Enabling this parameter can be helpful
250ms or longer will be logged. In addition, when this parameter is
set to any value other than <literal>-1</literal>, a message will be
logged if an autovacuum action is skipped due to the existence of a
conflicting lock. Enabling this parameter can be helpful
in tracking autovacuum activity. This setting can only be set in
the <filename>postgresql.conf</> file or on the server command line.
</para>

View File

@ -36,6 +36,7 @@
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "utils/acl.h"
@ -148,7 +149,19 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
* matter if we ever try to accumulate stats on dead tuples.) If the rel
* has been dropped since we last saw it, we don't need to process it.
*/
onerel = try_relation_open(relid, ShareUpdateExclusiveLock);
if (!(vacstmt->options & VACOPT_NOWAIT))
onerel = try_relation_open(relid, ShareUpdateExclusiveLock);
else if (ConditionalLockRelationOid(relid, ShareUpdateExclusiveLock))
onerel = try_relation_open(relid, NoLock);
else
{
onerel = NULL;
if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
ereport(LOG,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available",
vacstmt->relation->relname)));
}
if (!onerel)
return;

View File

@ -61,7 +61,7 @@ static BufferAccessStrategy vac_strategy;
/* non-export function prototypes */
static List *get_rel_oids(Oid relid, const RangeVar *vacrel);
static void vac_truncate_clog(TransactionId frozenXID);
static void vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast,
static bool vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast,
bool for_wraparound, bool *scanned_all);
@ -226,8 +226,11 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
bool scanned_all = false;
if (vacstmt->options & VACOPT_VACUUM)
vacuum_rel(relid, vacstmt, do_toast, for_wraparound,
&scanned_all);
{
if (!vacuum_rel(relid, vacstmt, do_toast, for_wraparound,
&scanned_all))
continue;
}
if (vacstmt->options & VACOPT_ANALYZE)
{
@ -764,7 +767,7 @@ vac_truncate_clog(TransactionId frozenXID)
*
* At entry and exit, we are not inside a transaction.
*/
static void
static bool
vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
bool *scanned_all)
{
@ -835,14 +838,29 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
*
* There's a race condition here: the rel may have gone away since the
* last time we saw it. If so, we don't need to vacuum it.
*
* If we've been asked not to wait for the relation lock, acquire it
* first in non-blocking mode, before calling try_relation_open().
*/
onerel = try_relation_open(relid, lmode);
if (!(vacstmt->options & VACOPT_NOWAIT))
onerel = try_relation_open(relid, lmode);
else if (ConditionalLockRelationOid(relid, lmode))
onerel = try_relation_open(relid, NoLock);
else
{
onerel = NULL;
if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
ereport(LOG,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available",
vacstmt->relation->relname)));
}
if (!onerel)
{
PopActiveSnapshot();
CommitTransactionCommand();
return;
return false;
}
/*
@ -873,7 +891,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
relation_close(onerel, lmode);
PopActiveSnapshot();
CommitTransactionCommand();
return;
return false;
}
/*
@ -890,7 +908,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
relation_close(onerel, lmode);
PopActiveSnapshot();
CommitTransactionCommand();
return;
return false;
}
/*
@ -905,7 +923,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
relation_close(onerel, lmode);
PopActiveSnapshot();
CommitTransactionCommand();
return;
return false;
}
/*
@ -989,6 +1007,9 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
* Now release the session-level lock on the master table.
*/
UnlockRelationIdForSession(&onerelid, lmode);
/* Report that we really did it. */
return true;
}

View File

@ -2671,19 +2671,27 @@ autovacuum_do_vac_analyze(autovac_table *tab,
BufferAccessStrategy bstrategy)
{
VacuumStmt vacstmt;
RangeVar rangevar;
/* Set up command parameters --- use a local variable instead of palloc */
/* Set up command parameters --- use local variables instead of palloc */
MemSet(&vacstmt, 0, sizeof(vacstmt));
MemSet(&rangevar, 0, sizeof(rangevar));
rangevar.schemaname = tab->at_nspname;
rangevar.relname = tab->at_relname;
rangevar.location = -1;
vacstmt.type = T_VacuumStmt;
vacstmt.options = 0;
if (!tab->at_wraparound)
vacstmt.options = VACOPT_NOWAIT;
if (tab->at_dovacuum)
vacstmt.options |= VACOPT_VACUUM;
if (tab->at_doanalyze)
vacstmt.options |= VACOPT_ANALYZE;
vacstmt.freeze_min_age = tab->at_freeze_min_age;
vacstmt.freeze_table_age = tab->at_freeze_table_age;
vacstmt.relation = NULL; /* not used since we pass a relid */
/* we pass the OID, but might need this anyway for an error message */
vacstmt.relation = &rangevar;
vacstmt.va_cols = NIL;
/* Let pgstat know what we're doing */

View File

@ -2332,7 +2332,8 @@ typedef enum VacuumOption
VACOPT_ANALYZE = 1 << 1, /* do ANALYZE */
VACOPT_VERBOSE = 1 << 2, /* print progress info */
VACOPT_FREEZE = 1 << 3, /* FREEZE option */
VACOPT_FULL = 1 << 4 /* FULL (non-concurrent) vacuum */
VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */
VACOPT_NOWAIT = 1 << 5
} VacuumOption;
typedef struct VacuumStmt