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:
parent
47082fa875
commit
32896c40ca
@ -4080,7 +4080,10 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
|
|||||||
all autovacuum actions. Minus-one (the default) disables logging
|
all autovacuum actions. Minus-one (the default) disables logging
|
||||||
autovacuum actions. For example, if you set this to
|
autovacuum actions. For example, if you set this to
|
||||||
<literal>250ms</literal> then all automatic vacuums and analyzes that run
|
<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
|
in tracking autovacuum activity. This setting can only be set in
|
||||||
the <filename>postgresql.conf</> file or on the server command line.
|
the <filename>postgresql.conf</> file or on the server command line.
|
||||||
</para>
|
</para>
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include "pgstat.h"
|
#include "pgstat.h"
|
||||||
#include "postmaster/autovacuum.h"
|
#include "postmaster/autovacuum.h"
|
||||||
#include "storage/bufmgr.h"
|
#include "storage/bufmgr.h"
|
||||||
|
#include "storage/lmgr.h"
|
||||||
#include "storage/proc.h"
|
#include "storage/proc.h"
|
||||||
#include "storage/procarray.h"
|
#include "storage/procarray.h"
|
||||||
#include "utils/acl.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
|
* 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.
|
* 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)
|
if (!onerel)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ static BufferAccessStrategy vac_strategy;
|
|||||||
/* non-export function prototypes */
|
/* non-export function prototypes */
|
||||||
static List *get_rel_oids(Oid relid, const RangeVar *vacrel);
|
static List *get_rel_oids(Oid relid, const RangeVar *vacrel);
|
||||||
static void vac_truncate_clog(TransactionId frozenXID);
|
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);
|
bool for_wraparound, bool *scanned_all);
|
||||||
|
|
||||||
|
|
||||||
@ -226,8 +226,11 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
|
|||||||
bool scanned_all = false;
|
bool scanned_all = false;
|
||||||
|
|
||||||
if (vacstmt->options & VACOPT_VACUUM)
|
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)
|
if (vacstmt->options & VACOPT_ANALYZE)
|
||||||
{
|
{
|
||||||
@ -764,7 +767,7 @@ vac_truncate_clog(TransactionId frozenXID)
|
|||||||
*
|
*
|
||||||
* At entry and exit, we are not inside a transaction.
|
* 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,
|
vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
|
||||||
bool *scanned_all)
|
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
|
* 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.
|
* 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)
|
if (!onerel)
|
||||||
{
|
{
|
||||||
PopActiveSnapshot();
|
PopActiveSnapshot();
|
||||||
CommitTransactionCommand();
|
CommitTransactionCommand();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -873,7 +891,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
|
|||||||
relation_close(onerel, lmode);
|
relation_close(onerel, lmode);
|
||||||
PopActiveSnapshot();
|
PopActiveSnapshot();
|
||||||
CommitTransactionCommand();
|
CommitTransactionCommand();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -890,7 +908,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
|
|||||||
relation_close(onerel, lmode);
|
relation_close(onerel, lmode);
|
||||||
PopActiveSnapshot();
|
PopActiveSnapshot();
|
||||||
CommitTransactionCommand();
|
CommitTransactionCommand();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -905,7 +923,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
|
|||||||
relation_close(onerel, lmode);
|
relation_close(onerel, lmode);
|
||||||
PopActiveSnapshot();
|
PopActiveSnapshot();
|
||||||
CommitTransactionCommand();
|
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.
|
* Now release the session-level lock on the master table.
|
||||||
*/
|
*/
|
||||||
UnlockRelationIdForSession(&onerelid, lmode);
|
UnlockRelationIdForSession(&onerelid, lmode);
|
||||||
|
|
||||||
|
/* Report that we really did it. */
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2671,19 +2671,27 @@ autovacuum_do_vac_analyze(autovac_table *tab,
|
|||||||
BufferAccessStrategy bstrategy)
|
BufferAccessStrategy bstrategy)
|
||||||
{
|
{
|
||||||
VacuumStmt vacstmt;
|
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(&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.type = T_VacuumStmt;
|
||||||
vacstmt.options = 0;
|
if (!tab->at_wraparound)
|
||||||
|
vacstmt.options = VACOPT_NOWAIT;
|
||||||
if (tab->at_dovacuum)
|
if (tab->at_dovacuum)
|
||||||
vacstmt.options |= VACOPT_VACUUM;
|
vacstmt.options |= VACOPT_VACUUM;
|
||||||
if (tab->at_doanalyze)
|
if (tab->at_doanalyze)
|
||||||
vacstmt.options |= VACOPT_ANALYZE;
|
vacstmt.options |= VACOPT_ANALYZE;
|
||||||
vacstmt.freeze_min_age = tab->at_freeze_min_age;
|
vacstmt.freeze_min_age = tab->at_freeze_min_age;
|
||||||
vacstmt.freeze_table_age = tab->at_freeze_table_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;
|
vacstmt.va_cols = NIL;
|
||||||
|
|
||||||
/* Let pgstat know what we're doing */
|
/* Let pgstat know what we're doing */
|
||||||
|
@ -2332,7 +2332,8 @@ typedef enum VacuumOption
|
|||||||
VACOPT_ANALYZE = 1 << 1, /* do ANALYZE */
|
VACOPT_ANALYZE = 1 << 1, /* do ANALYZE */
|
||||||
VACOPT_VERBOSE = 1 << 2, /* print progress info */
|
VACOPT_VERBOSE = 1 << 2, /* print progress info */
|
||||||
VACOPT_FREEZE = 1 << 3, /* FREEZE option */
|
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;
|
} VacuumOption;
|
||||||
|
|
||||||
typedef struct VacuumStmt
|
typedef struct VacuumStmt
|
||||||
|
Loading…
x
Reference in New Issue
Block a user