Skip to content

Commit 32896c4

Browse files
committed
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.
1 parent 47082fa commit 32896c4

File tree

5 files changed

+61
-15
lines changed

5 files changed

+61
-15
lines changed

doc/src/sgml/config.sgml

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

src/backend/commands/analyze.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "pgstat.h"
3737
#include "postmaster/autovacuum.h"
3838
#include "storage/bufmgr.h"
39+
#include "storage/lmgr.h"
3940
#include "storage/proc.h"
4041
#include "storage/procarray.h"
4142
#include "utils/acl.h"
@@ -148,7 +149,19 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
148149
* matter if we ever try to accumulate stats on dead tuples.) If the rel
149150
* has been dropped since we last saw it, we don't need to process it.
150151
*/
151-
onerel = try_relation_open(relid, ShareUpdateExclusiveLock);
152+
if (!(vacstmt->options & VACOPT_NOWAIT))
153+
onerel = try_relation_open(relid, ShareUpdateExclusiveLock);
154+
else if (ConditionalLockRelationOid(relid, ShareUpdateExclusiveLock))
155+
onerel = try_relation_open(relid, NoLock);
156+
else
157+
{
158+
onerel = NULL;
159+
if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
160+
ereport(LOG,
161+
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
162+
errmsg("skipping analyze of \"%s\" --- lock not available",
163+
vacstmt->relation->relname)));
164+
}
152165
if (!onerel)
153166
return;
154167

src/backend/commands/vacuum.c

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ static BufferAccessStrategy vac_strategy;
6161
/* non-export function prototypes */
6262
static List *get_rel_oids(Oid relid, const RangeVar *vacrel);
6363
static void vac_truncate_clog(TransactionId frozenXID);
64-
static void vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast,
64+
static bool vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast,
6565
bool for_wraparound, bool *scanned_all);
6666

6767

@@ -226,8 +226,11 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
226226
bool scanned_all = false;
227227

228228
if (vacstmt->options & VACOPT_VACUUM)
229-
vacuum_rel(relid, vacstmt, do_toast, for_wraparound,
230-
&scanned_all);
229+
{
230+
if (!vacuum_rel(relid, vacstmt, do_toast, for_wraparound,
231+
&scanned_all))
232+
continue;
233+
}
231234

232235
if (vacstmt->options & VACOPT_ANALYZE)
233236
{
@@ -764,7 +767,7 @@ vac_truncate_clog(TransactionId frozenXID)
764767
*
765768
* At entry and exit, we are not inside a transaction.
766769
*/
767-
static void
770+
static bool
768771
vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
769772
bool *scanned_all)
770773
{
@@ -835,14 +838,29 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
835838
*
836839
* There's a race condition here: the rel may have gone away since the
837840
* last time we saw it. If so, we don't need to vacuum it.
841+
*
842+
* If we've been asked not to wait for the relation lock, acquire it
843+
* first in non-blocking mode, before calling try_relation_open().
838844
*/
839-
onerel = try_relation_open(relid, lmode);
845+
if (!(vacstmt->options & VACOPT_NOWAIT))
846+
onerel = try_relation_open(relid, lmode);
847+
else if (ConditionalLockRelationOid(relid, lmode))
848+
onerel = try_relation_open(relid, NoLock);
849+
else
850+
{
851+
onerel = NULL;
852+
if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
853+
ereport(LOG,
854+
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
855+
errmsg("skipping vacuum of \"%s\" --- lock not available",
856+
vacstmt->relation->relname)));
857+
}
840858

841859
if (!onerel)
842860
{
843861
PopActiveSnapshot();
844862
CommitTransactionCommand();
845-
return;
863+
return false;
846864
}
847865

848866
/*
@@ -873,7 +891,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
873891
relation_close(onerel, lmode);
874892
PopActiveSnapshot();
875893
CommitTransactionCommand();
876-
return;
894+
return false;
877895
}
878896

879897
/*
@@ -890,7 +908,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
890908
relation_close(onerel, lmode);
891909
PopActiveSnapshot();
892910
CommitTransactionCommand();
893-
return;
911+
return false;
894912
}
895913

896914
/*
@@ -905,7 +923,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
905923
relation_close(onerel, lmode);
906924
PopActiveSnapshot();
907925
CommitTransactionCommand();
908-
return;
926+
return false;
909927
}
910928

911929
/*
@@ -989,6 +1007,9 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
9891007
* Now release the session-level lock on the master table.
9901008
*/
9911009
UnlockRelationIdForSession(&onerelid, lmode);
1010+
1011+
/* Report that we really did it. */
1012+
return true;
9921013
}
9931014

9941015

src/backend/postmaster/autovacuum.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2671,19 +2671,27 @@ autovacuum_do_vac_analyze(autovac_table *tab,
26712671
BufferAccessStrategy bstrategy)
26722672
{
26732673
VacuumStmt vacstmt;
2674+
RangeVar rangevar;
26742675

2675-
/* Set up command parameters --- use a local variable instead of palloc */
2676+
/* Set up command parameters --- use local variables instead of palloc */
26762677
MemSet(&vacstmt, 0, sizeof(vacstmt));
2678+
MemSet(&rangevar, 0, sizeof(rangevar));
2679+
2680+
rangevar.schemaname = tab->at_nspname;
2681+
rangevar.relname = tab->at_relname;
2682+
rangevar.location = -1;
26772683

26782684
vacstmt.type = T_VacuumStmt;
2679-
vacstmt.options = 0;
2685+
if (!tab->at_wraparound)
2686+
vacstmt.options = VACOPT_NOWAIT;
26802687
if (tab->at_dovacuum)
26812688
vacstmt.options |= VACOPT_VACUUM;
26822689
if (tab->at_doanalyze)
26832690
vacstmt.options |= VACOPT_ANALYZE;
26842691
vacstmt.freeze_min_age = tab->at_freeze_min_age;
26852692
vacstmt.freeze_table_age = tab->at_freeze_table_age;
2686-
vacstmt.relation = NULL; /* not used since we pass a relid */
2693+
/* we pass the OID, but might need this anyway for an error message */
2694+
vacstmt.relation = &rangevar;
26872695
vacstmt.va_cols = NIL;
26882696

26892697
/* Let pgstat know what we're doing */

src/include/nodes/parsenodes.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2332,7 +2332,8 @@ typedef enum VacuumOption
23322332
VACOPT_ANALYZE = 1 << 1, /* do ANALYZE */
23332333
VACOPT_VERBOSE = 1 << 2, /* print progress info */
23342334
VACOPT_FREEZE = 1 << 3, /* FREEZE option */
2335-
VACOPT_FULL = 1 << 4 /* FULL (non-concurrent) vacuum */
2335+
VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */
2336+
VACOPT_NOWAIT = 1 << 5
23362337
} VacuumOption;
23372338

23382339
typedef struct VacuumStmt

0 commit comments

Comments
 (0)