]> granicus.if.org Git - postgresql/commitdiff
Ensure ANALYZE phase is not skipped because of canceled truncate.
authorKevin Grittner <kgrittn@postgresql.org>
Mon, 29 Apr 2013 18:05:56 +0000 (13:05 -0500)
committerKevin Grittner <kgrittn@postgresql.org>
Mon, 29 Apr 2013 18:05:56 +0000 (13:05 -0500)
Patch b19e4250b45e91c9cbdd18d35ea6391ab5961c8d attempted to
preserve existing behavior regarding statistics generation in the
case that a truncation attempt was canceled due to lock conflicts.
It failed to do this accurately in two regards: (1) autovacuum had
previously generated statistics if the truncate attempt failed to
initially get the lock rather than having started the attempt, and
(2) the VACUUM ANALYZE command had always generated statistics.

Both of these changes were unintended, and are reverted by this
patch.  On review, there seems to be consensus that the previous
failure to generate statistics when the truncate was terminated
was more an unfortunate consequence of how that effort was
previously terminated than a feature we want to keep; so this
patch generates statistics even when an autovacuum truncation
attempt terminates early.  Another unintended change which is kept
on the basis that it is an improvement is that when a VACUUM
command is truncating, it will the new heuristic for avoiding
blocking other processes, rather than keeping an
AccessExclusiveLock on the table for however long the truncation
takes.

Per multiple reports, with some renaming per patch by Jeff Janes.

Backpatch to 9.0, where problem was created.

src/backend/commands/vacuumlazy.c

index 0401b7f911f218e805470c037c3ce130ea4498c8..9254bcd68688ecd0a8ca5a6f164734ea340188e2 100644 (file)
@@ -75,9 +75,9 @@
  * that the potential for improvement was great enough to merit the cost of
  * supporting them.
  */
-#define AUTOVACUUM_TRUNCATE_LOCK_CHECK_INTERVAL                20      /* ms */
-#define AUTOVACUUM_TRUNCATE_LOCK_WAIT_INTERVAL         50      /* ms */
-#define AUTOVACUUM_TRUNCATE_LOCK_TIMEOUT                       5000            /* ms */
+#define VACUUM_TRUNCATE_LOCK_CHECK_INTERVAL            20      /* ms */
+#define VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL             50      /* ms */
+#define VACUUM_TRUNCATE_LOCK_TIMEOUT                   5000            /* ms */
 
 /*
  * Guesstimation of number of dead tuples per page.  This is used to
@@ -272,17 +272,10 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
                                                vacrelstats->hasindex,
                                                new_frozen_xid);
 
-       /*
-        * Report results to the stats collector, too. An early terminated
-        * lazy_truncate_heap attempt suppresses the message and also cancels the
-        * execution of ANALYZE, if that was ordered.
-        */
-       if (!vacrelstats->lock_waiter_detected)
-               pgstat_report_vacuum(RelationGetRelid(onerel),
-                                                        onerel->rd_rel->relisshared,
-                                                        new_rel_tuples);
-       else
-               vacstmt->options &= ~VACOPT_ANALYZE;
+       /* report results to the stats collector, too */
+       pgstat_report_vacuum(RelationGetRelid(onerel),
+                                                 onerel->rd_rel->relisshared,
+                                                 new_rel_tuples);
 
        /* and log the action if appropriate */
        if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
@@ -1306,28 +1299,21 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
                         */
                        CHECK_FOR_INTERRUPTS();
 
-                       if (++lock_retry > (AUTOVACUUM_TRUNCATE_LOCK_TIMEOUT /
-                                                               AUTOVACUUM_TRUNCATE_LOCK_WAIT_INTERVAL))
+                       if (++lock_retry > (VACUUM_TRUNCATE_LOCK_TIMEOUT /
+                                                               VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL))
                        {
                                /*
                                 * We failed to establish the lock in the specified number of
-                                * retries. This means we give up truncating. Suppress the
-                                * ANALYZE step. Doing an ANALYZE at this point will reset the
-                                * dead_tuple_count in the stats collector, so we will not get
-                                * called by the autovacuum launcher again to do the truncate.
+                                * retries. This means we give up truncating.
                                 */
                                vacrelstats->lock_waiter_detected = true;
-                               ereport(LOG,
-                                               (errmsg("automatic vacuum of table \"%s.%s.%s\": "
-                                                               "could not (re)acquire exclusive "
-                                                               "lock for truncate scan",
-                                                               get_database_name(MyDatabaseId),
-                                                       get_namespace_name(RelationGetNamespace(onerel)),
+                               ereport(elevel,
+                                               (errmsg("\"%s\": stopping truncate due to conflicting lock request",
                                                                RelationGetRelationName(onerel))));
                                return;
                        }
 
-                       pg_usleep(AUTOVACUUM_TRUNCATE_LOCK_WAIT_INTERVAL);
+                       pg_usleep(VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL);
                }
 
                /*
@@ -1407,8 +1393,6 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
 {
        BlockNumber blkno;
        instr_time      starttime;
-       instr_time      currenttime;
-       instr_time      elapsed;
 
        /* Initialize the starttime if we check for conflicting lock requests */
        INSTR_TIME_SET_CURRENT(starttime);
@@ -1426,24 +1410,26 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
                /*
                 * Check if another process requests a lock on our relation. We are
                 * holding an AccessExclusiveLock here, so they will be waiting. We
-                * only do this in autovacuum_truncate_lock_check millisecond
-                * intervals, and we only check if that interval has elapsed once
-                * every 32 blocks to keep the number of system calls and actual
-                * shared lock table lookups to a minimum.
+                * only do this once per VACUUM_TRUNCATE_LOCK_CHECK_INTERVAL, and we
+                * only check if that interval has elapsed once every 32 blocks to
+                * keep the number of system calls and actual shared lock table
+                * lookups to a minimum.
                 */
                if ((blkno % 32) == 0)
                {
+                       instr_time      currenttime;
+                       instr_time      elapsed;
+
                        INSTR_TIME_SET_CURRENT(currenttime);
                        elapsed = currenttime;
                        INSTR_TIME_SUBTRACT(elapsed, starttime);
                        if ((INSTR_TIME_GET_MICROSEC(elapsed) / 1000)
-                               >= AUTOVACUUM_TRUNCATE_LOCK_CHECK_INTERVAL)
+                               >= VACUUM_TRUNCATE_LOCK_CHECK_INTERVAL)
                        {
                                if (LockHasWaitersRelation(onerel, AccessExclusiveLock))
                                {
                                        ereport(elevel,
-                                                       (errmsg("\"%s\": suspending truncate "
-                                                                       "due to conflicting lock request",
+                                                       (errmsg("\"%s\": suspending truncate due to conflicting lock request",
                                                                        RelationGetRelationName(onerel))));
 
                                        vacrelstats->lock_waiter_detected = true;