Add option SKIP_LOCKED to VACUUM and ANALYZE
authorMichael Paquier
Thu, 4 Oct 2018 00:00:33 +0000 (09:00 +0900)
committerMichael Paquier
Thu, 4 Oct 2018 00:00:33 +0000 (09:00 +0900)
When specified, this option allows VACUUM to skip the work on a relation
if there is a conflicting lock on it when trying to open it at the
beginning of its processing.

Similarly to autovacuum, this comes with a couple of limitations while
the relation is processed which can cause the process to still block:
- when opening the relation indexes.
- when acquiring row samples for table inheritance trees, partition trees
or certain types of foreign tables, and that a lock is taken on some
leaves of such trees.

Author: Nathan Bossart
Reviewed-by: Michael Paquier, Andres Freund, Masahiko Sawada
Discussion: https://postgr.es/m/9EF7EBE4-720D-4CF1-9D0E-4403D7E92990@amazon.com
Discussion: https://postgr.es/m/20171201160907[email protected]

doc/src/sgml/ref/analyze.sgml
doc/src/sgml/ref/vacuum.sgml
src/backend/commands/vacuum.c
src/backend/parser/gram.y
src/include/nodes/parsenodes.h
src/test/isolation/expected/vacuum-skip-locked.out [new file with mode: 0644]
src/test/isolation/isolation_schedule
src/test/isolation/specs/vacuum-skip-locked.spec [new file with mode: 0644]
src/test/regress/expected/vacuum.out
src/test/regress/sql/vacuum.sql

index 10b3a9a67339594257cba3e536f91fa4656a5670..fea7f4652159773836268e4670a151eadf140400 100644 (file)
@@ -27,6 +27,7 @@ ANALYZE [ VERBOSE ] [ table_and_columns
 where option can be one of:
 
     VERBOSE
+    SKIP_LOCKED
 
 and table_and_columns is:
 
@@ -76,6 +77,24 @@ ANALYZE [ VERBOSE ] [ table_and_columns
     
    
 
+   
+    SKIP_LOCKED
+    
+     
+      Specifies that ANALYZE should not wait for any
+      conflicting locks to be released when beginning work on a relation:
+      if a relation cannot be locked immediately without waiting, the relation
+      is skipped.  Note that even with this option, ANALYZE
+      may still block when opening the relation's indexes or when acquiring
+      sample rows from partitions, table inheritance children, and some
+      types of foreign tables.  Also, while ANALYZE
+      ordinarily processes all partitions of specified partitioned tables,
+      this option will cause ANALYZE to skip all
+      partitions if there is a conflicting lock on the partitioned table.
+     
+    
+   
+
    
     table_name
     
index b760e8ede182441773d0d79a8e8f5444970768a3..fd911f5776695d3b5aa0a57b16ac8c4088b2cc8c 100644 (file)
@@ -31,6 +31,7 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ 
     VERBOSE
     ANALYZE
     DISABLE_PAGE_SKIPPING
+    SKIP_LOCKED
 
 and table_and_columns is:
 
@@ -160,6 +161,26 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ 
     
    
 
+   
+    SKIP_LOCKED
+    
+     
+      Specifies that VACUUM should not wait for any
+      conflicting locks to be released when beginning work on a relation:
+      if a relation cannot be locked immediately without waiting, the relation
+      is skipped.  Note that even with this option,
+      VACUUM may still block when opening the relation's
+      indexes.  Additionally, VACUUM ANALYZE may still
+      block when acquiring sample rows from partitions, table inheritance
+      children, and some types of foreign tables.  Also, while
+      VACUUM ordinarily processes all partitions of
+      specified partitioned tables, this option will cause
+      VACUUM to skip all partitions if there is a
+      conflicting lock on the partitioned table.
+     
+    
+   
+
    
     table_name
     
index 4e3823b0f0a93ed952ff0ac33c6ace7d111030b9..a86963fc86a88f74ca16488dc165c98615a2e451 100644 (file)
@@ -622,6 +622,7 @@ expand_vacuum_rel(VacuumRelation *vrel, int options)
        HeapTuple   tuple;
        Form_pg_class classForm;
        bool        include_parts;
+       int         rvr_opts;
 
        /*
         * Since autovacuum workers supply OIDs when calling vacuum(), no
@@ -634,7 +635,30 @@ expand_vacuum_rel(VacuumRelation *vrel, int options)
         * below, as well as find_all_inheritors's expectation that the caller
         * holds some lock on the starting relation.
         */
-       relid = RangeVarGetRelid(vrel->relation, AccessShareLock, false);
+       rvr_opts = (options & VACOPT_SKIP_LOCKED) ? RVR_SKIP_LOCKED : 0;
+       relid = RangeVarGetRelidExtended(vrel->relation,
+                                        AccessShareLock,
+                                        rvr_opts,
+                                        NULL, NULL);
+
+       /*
+        * If the lock is unavailable, emit the same log statement that
+        * vacuum_rel() and analyze_rel() would.
+        */
+       if (!OidIsValid(relid))
+       {
+           if (options & VACOPT_VACUUM)
+               ereport(WARNING,
+                       (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+                        errmsg("skipping vacuum of \"%s\" --- lock not available",
+                               vrel->relation->relname)));
+           else
+               ereport(WARNING,
+                       (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+                        errmsg("skipping analyze of \"%s\" --- lock not available",
+                               vrel->relation->relname)));
+           return vacrels;
+       }
 
        /*
         * To check whether the relation is a partitioned table and its
index ce60e99cff5e3618bd5da0982c31c18f2a46200d..6d23bfb0b35cf9dcd8b6f01a456b07543792233b 100644 (file)
@@ -10538,6 +10538,8 @@ vacuum_option_elem:
                {
                    if (strcmp($1, "disable_page_skipping") == 0)
                        $$ = VACOPT_DISABLE_PAGE_SKIPPING;
+                   else if (strcmp($1, "skip_locked") == 0)
+                       $$ = VACOPT_SKIP_LOCKED;
                    else
                        ereport(ERROR,
                                (errcode(ERRCODE_SYNTAX_ERROR),
@@ -10571,6 +10573,16 @@ analyze_option_list:
 
 analyze_option_elem:
            VERBOSE             { $$ = VACOPT_VERBOSE; }
+           | IDENT
+               {
+                   if (strcmp($1, "skip_locked") == 0)
+                       $$ = VACOPT_SKIP_LOCKED;
+                   else
+                       ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("unrecognized ANALYZE option \"%s\"", $1),
+                                    parser_errposition(@1)));
+               }
        ;
 
 analyze_keyword:
index 200df8e6595fc0ddebf6c200339c1bc23130662e..aa4a0dba2a64050d43d42d0718d8d5f973251f56 100644 (file)
@@ -3154,8 +3154,7 @@ typedef enum VacuumOption
    VACOPT_VERBOSE = 1 << 2,    /* print progress info */
    VACOPT_FREEZE = 1 << 3,     /* FREEZE option */
    VACOPT_FULL = 1 << 4,       /* FULL (non-concurrent) vacuum */
-   VACOPT_SKIP_LOCKED = 1 << 5,    /* skip if cannot get lock (autovacuum
-                                    * only) */
+   VACOPT_SKIP_LOCKED = 1 << 5,    /* skip if cannot get lock */
    VACOPT_SKIPTOAST = 1 << 6,  /* don't process the TOAST table, if any */
    VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7   /* don't skip any pages */
 } VacuumOption;
diff --git a/src/test/isolation/expected/vacuum-skip-locked.out b/src/test/isolation/expected/vacuum-skip-locked.out
new file mode 100644 (file)
index 0000000..95ca456
--- /dev/null
@@ -0,0 +1,171 @@
+Parsed test spec with 2 sessions
+
+starting permutation: lock_share vac_specified commit
+step lock_share: 
+   BEGIN;
+   LOCK part1 IN SHARE MODE;
+
+WARNING:  skipping vacuum of "part1" --- lock not available
+step vac_specified: VACUUM (SKIP_LOCKED) part1, part2;
+step commit: 
+   COMMIT;
+
+
+starting permutation: lock_share vac_all_parts commit
+step lock_share: 
+   BEGIN;
+   LOCK part1 IN SHARE MODE;
+
+step vac_all_parts: VACUUM (SKIP_LOCKED) parted;
+step commit: 
+   COMMIT;
+
+
+starting permutation: lock_share analyze_specified commit
+step lock_share: 
+   BEGIN;
+   LOCK part1 IN SHARE MODE;
+
+WARNING:  skipping analyze of "part1" --- lock not available
+step analyze_specified: ANALYZE (SKIP_LOCKED) part1, part2;
+step commit: 
+   COMMIT;
+
+
+starting permutation: lock_share analyze_all_parts commit
+step lock_share: 
+   BEGIN;
+   LOCK part1 IN SHARE MODE;
+
+step analyze_all_parts: ANALYZE (SKIP_LOCKED) parted;
+step commit: 
+   COMMIT;
+
+
+starting permutation: lock_share vac_analyze_specified commit
+step lock_share: 
+   BEGIN;
+   LOCK part1 IN SHARE MODE;
+
+WARNING:  skipping vacuum of "part1" --- lock not available
+step vac_analyze_specified: VACUUM (ANALYZE, SKIP_LOCKED) part1, part2;
+step commit: 
+   COMMIT;
+
+
+starting permutation: lock_share vac_analyze_all_parts commit
+step lock_share: 
+   BEGIN;
+   LOCK part1 IN SHARE MODE;
+
+step vac_analyze_all_parts: VACUUM (ANALYZE, SKIP_LOCKED) parted;
+step commit: 
+   COMMIT;
+
+
+starting permutation: lock_share vac_full_specified commit
+step lock_share: 
+   BEGIN;
+   LOCK part1 IN SHARE MODE;
+
+WARNING:  skipping vacuum of "part1" --- lock not available
+step vac_full_specified: VACUUM (SKIP_LOCKED, FULL) part1, part2;
+step commit: 
+   COMMIT;
+
+
+starting permutation: lock_share vac_full_all_parts commit
+step lock_share: 
+   BEGIN;
+   LOCK part1 IN SHARE MODE;
+
+step vac_full_all_parts: VACUUM (SKIP_LOCKED, FULL) parted;
+step commit: 
+   COMMIT;
+
+
+starting permutation: lock_access_exclusive vac_specified commit
+step lock_access_exclusive: 
+   BEGIN;
+   LOCK part1 IN ACCESS EXCLUSIVE MODE;
+
+WARNING:  skipping vacuum of "part1" --- lock not available
+step vac_specified: VACUUM (SKIP_LOCKED) part1, part2;
+step commit: 
+   COMMIT;
+
+
+starting permutation: lock_access_exclusive vac_all_parts commit
+step lock_access_exclusive: 
+   BEGIN;
+   LOCK part1 IN ACCESS EXCLUSIVE MODE;
+
+step vac_all_parts: VACUUM (SKIP_LOCKED) parted;
+step commit: 
+   COMMIT;
+
+
+starting permutation: lock_access_exclusive analyze_specified commit
+step lock_access_exclusive: 
+   BEGIN;
+   LOCK part1 IN ACCESS EXCLUSIVE MODE;
+
+WARNING:  skipping analyze of "part1" --- lock not available
+step analyze_specified: ANALYZE (SKIP_LOCKED) part1, part2;
+step commit: 
+   COMMIT;
+
+
+starting permutation: lock_access_exclusive analyze_all_parts commit
+step lock_access_exclusive: 
+   BEGIN;
+   LOCK part1 IN ACCESS EXCLUSIVE MODE;
+
+step analyze_all_parts: ANALYZE (SKIP_LOCKED) parted; 
+step commit: 
+   COMMIT;
+
+step analyze_all_parts: <... completed>
+
+starting permutation: lock_access_exclusive vac_analyze_specified commit
+step lock_access_exclusive: 
+   BEGIN;
+   LOCK part1 IN ACCESS EXCLUSIVE MODE;
+
+WARNING:  skipping vacuum of "part1" --- lock not available
+step vac_analyze_specified: VACUUM (ANALYZE, SKIP_LOCKED) part1, part2;
+step commit: 
+   COMMIT;
+
+
+starting permutation: lock_access_exclusive vac_analyze_all_parts commit
+step lock_access_exclusive: 
+   BEGIN;
+   LOCK part1 IN ACCESS EXCLUSIVE MODE;
+
+step vac_analyze_all_parts: VACUUM (ANALYZE, SKIP_LOCKED) parted; 
+step commit: 
+   COMMIT;
+
+step vac_analyze_all_parts: <... completed>
+
+starting permutation: lock_access_exclusive vac_full_specified commit
+step lock_access_exclusive: 
+   BEGIN;
+   LOCK part1 IN ACCESS EXCLUSIVE MODE;
+
+WARNING:  skipping vacuum of "part1" --- lock not available
+step vac_full_specified: VACUUM (SKIP_LOCKED, FULL) part1, part2;
+step commit: 
+   COMMIT;
+
+
+starting permutation: lock_access_exclusive vac_full_all_parts commit
+step lock_access_exclusive: 
+   BEGIN;
+   LOCK part1 IN ACCESS EXCLUSIVE MODE;
+
+step vac_full_all_parts: VACUUM (SKIP_LOCKED, FULL) parted;
+step commit: 
+   COMMIT;
+
index c23b401225def847a0c717d58612217fc7cf9ba7..dd57a96e788e8a5bd8934d18ff917a3bdc6e6849 100644 (file)
@@ -67,6 +67,7 @@ test: vacuum-reltuples
 test: timeouts
 test: vacuum-concurrent-drop
 test: vacuum-conflict
+test: vacuum-skip-locked
 test: predicate-hash
 test: predicate-gist
 test: predicate-gin
diff --git a/src/test/isolation/specs/vacuum-skip-locked.spec b/src/test/isolation/specs/vacuum-skip-locked.spec
new file mode 100644 (file)
index 0000000..4d59b29
--- /dev/null
@@ -0,0 +1,59 @@
+# Test for SKIP_LOCKED option of VACUUM and ANALYZE commands.
+#
+# This also verifies that log messages are not emitted for skipped relations
+# that were not specified in the VACUUM or ANALYZE command.
+
+setup
+{
+   CREATE TABLE parted (a INT) PARTITION BY LIST (a);
+   CREATE TABLE part1 PARTITION OF parted FOR VALUES IN (1);
+   CREATE TABLE part2 PARTITION OF parted FOR VALUES IN (2);
+}
+
+teardown
+{
+   DROP TABLE IF EXISTS parted;
+}
+
+session "s1"
+step "lock_share"
+{
+   BEGIN;
+   LOCK part1 IN SHARE MODE;
+}
+step "lock_access_exclusive"
+{
+   BEGIN;
+   LOCK part1 IN ACCESS EXCLUSIVE MODE;
+}
+step "commit"
+{
+   COMMIT;
+}
+
+session "s2"
+step "vac_specified"       { VACUUM (SKIP_LOCKED) part1, part2; }
+step "vac_all_parts"       { VACUUM (SKIP_LOCKED) parted; }
+step "analyze_specified"   { ANALYZE (SKIP_LOCKED) part1, part2; }
+step "analyze_all_parts"   { ANALYZE (SKIP_LOCKED) parted; }
+step "vac_analyze_specified"   { VACUUM (ANALYZE, SKIP_LOCKED) part1, part2; }
+step "vac_analyze_all_parts"   { VACUUM (ANALYZE, SKIP_LOCKED) parted; }
+step "vac_full_specified"  { VACUUM (SKIP_LOCKED, FULL) part1, part2; }
+step "vac_full_all_parts"  { VACUUM (SKIP_LOCKED, FULL) parted; }
+
+permutation "lock_share" "vac_specified" "commit"
+permutation "lock_share" "vac_all_parts" "commit"
+permutation "lock_share" "analyze_specified" "commit"
+permutation "lock_share" "analyze_all_parts" "commit"
+permutation "lock_share" "vac_analyze_specified" "commit"
+permutation "lock_share" "vac_analyze_all_parts" "commit"
+permutation "lock_share" "vac_full_specified" "commit"
+permutation "lock_share" "vac_full_all_parts" "commit"
+permutation "lock_access_exclusive" "vac_specified" "commit"
+permutation "lock_access_exclusive" "vac_all_parts" "commit"
+permutation "lock_access_exclusive" "analyze_specified" "commit"
+permutation "lock_access_exclusive" "analyze_all_parts" "commit"
+permutation "lock_access_exclusive" "vac_analyze_specified" "commit"
+permutation "lock_access_exclusive" "vac_analyze_all_parts" "commit"
+permutation "lock_access_exclusive" "vac_full_specified" "commit"
+permutation "lock_access_exclusive" "vac_full_all_parts" "commit"
index 5993a902472480cb556d924ed33f6447395937f6..fa9d663abdaf7de2d488ddbebbb6838ec2ab6b52 100644 (file)
@@ -115,10 +115,20 @@ ERROR:  column "does_not_exist" of relation "vacparted" does not exist
 -- parenthesized syntax for ANALYZE
 ANALYZE (VERBOSE) does_not_exist;
 ERROR:  relation "does_not_exist" does not exist
-ANALYZE (nonexistant-arg) does_not_exist;
-ERROR:  syntax error at or near "nonexistant"
-LINE 1: ANALYZE (nonexistant-arg) does_not_exist;
+ANALYZE (nonexistent-arg) does_not_exist;
+ERROR:  unrecognized ANALYZE option "nonexistent"
+LINE 1: ANALYZE (nonexistent-arg) does_not_exist;
                  ^
+-- ensure argument order independence, and that SKIP_LOCKED on non-existing
+-- relation still errors out.
+ANALYZE (SKIP_LOCKED, VERBOSE) does_not_exist;
+ERROR:  relation "does_not_exist" does not exist
+ANALYZE (VERBOSE, SKIP_LOCKED) does_not_exist;
+ERROR:  relation "does_not_exist" does not exist
+-- SKIP_LOCKED option
+VACUUM (SKIP_LOCKED) vactst;
+VACUUM (SKIP_LOCKED, FULL) vactst;
+ANALYZE (SKIP_LOCKED) vactst;
 DROP TABLE vaccluster;
 DROP TABLE vactst;
 DROP TABLE vacparted;
index 7f74da3cbd8359a41818dccf7015f6abb21e82f1..9defa0d8b2cb33be2c074327f5c15b973508dc97 100644 (file)
@@ -91,7 +91,17 @@ ANALYZE vactst (i), vacparted (does_not_exist);
 
 -- parenthesized syntax for ANALYZE
 ANALYZE (VERBOSE) does_not_exist;
-ANALYZE (nonexistant-arg) does_not_exist;
+ANALYZE (nonexistent-arg) does_not_exist;
+
+-- ensure argument order independence, and that SKIP_LOCKED on non-existing
+-- relation still errors out.
+ANALYZE (SKIP_LOCKED, VERBOSE) does_not_exist;
+ANALYZE (VERBOSE, SKIP_LOCKED) does_not_exist;
+
+-- SKIP_LOCKED option
+VACUUM (SKIP_LOCKED) vactst;
+VACUUM (SKIP_LOCKED, FULL) vactst;
+ANALYZE (SKIP_LOCKED) vactst;
 
 DROP TABLE vaccluster;
 DROP TABLE vactst;