]> granicus.if.org Git - postgresql/commitdiff
Avoid having autovacuum workers wait for relation locks.
authorRobert Haas <rhaas@postgresql.org>
Tue, 8 Feb 2011 03:04:29 +0000 (22:04 -0500)
committerRobert Haas <rhaas@postgresql.org>
Tue, 8 Feb 2011 03:04:29 +0000 (22:04 -0500)
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.

doc/src/sgml/config.sgml
src/backend/commands/analyze.c
src/backend/commands/vacuum.c
src/backend/postmaster/autovacuum.c
src/include/nodes/parsenodes.h

index 2d8396e4e9942af1ea8282eb408ae8bfbb4b06ab..5a43774b33dc73d6a066874ac30ed22705638172 100644 (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>
index 7bc5f111f4d8ea6abba49661aba61d25f5aa688d..4c106dd8c572fc51ec9d06bfd04ee1e70ae6efa7 100644 (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;
 
index 566371167459b350d28e40739b267f7306e055d4..1651aa94dc22f4e2ff9991381f64da871252fa9d 100644 (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;
 }
 
 
index 7bacf9b2def8ffd71cd132df4b1593610ed04887..7307c4177c65d541a2741aa1c01377d427240388 100644 (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 */
index 483f22591ea1d8bb2e66cbefeaddaa2e46f38a29..d7d1b0a6fdf72c6b009e1d0ad4c2ef7bb4b44b78 100644 (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