]> granicus.if.org Git - postgresql/commitdiff
Avoid integer overflow issues in autovacuum.
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 13 Jun 2007 21:24:56 +0000 (21:24 +0000)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 13 Jun 2007 21:24:56 +0000 (21:24 +0000)
src/backend/postmaster/autovacuum.c
src/backend/utils/misc/guc.c

index 82a2bc71a0c6e7fb3b386c0cdbf0412c7a474668..46eb38e8e4701516e262807ff8aa15f36063c14a 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.49 2007/06/08 21:21:28 alvherre Exp $
+ *       $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.50 2007/06/13 21:24:55 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,6 +18,7 @@
 
 #include <signal.h>
 #include <sys/types.h>
+#include <sys/time.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -73,6 +74,10 @@ int                  autovacuum_vac_cost_limit;
 
 int                    Log_autovacuum = -1;
 
+
+/* maximum sleep duration in the launcher, in seconds */
+#define AV_SLEEP_QUANTUM 10
+
 /* Flags to tell if we are in an autovacuum process */
 static bool am_autovacuum_launcher = false;
 static bool am_autovacuum_worker = false;
@@ -197,7 +202,8 @@ NON_EXEC_STATIC void AutoVacWorkerMain(int argc, char *argv[]);
 NON_EXEC_STATIC void AutoVacLauncherMain(int argc, char *argv[]);
 
 static Oid do_start_worker(void);
-static uint64 launcher_determine_sleep(bool canlaunch, bool recursing);
+static void launcher_determine_sleep(bool canlaunch, bool recursing,
+                                                struct timeval *nap);
 static void launch_worker(TimestampTz now);
 static List *get_database_list(void);
 static void rebuild_database_list(Oid newdb);
@@ -487,7 +493,7 @@ AutoVacLauncherMain(int argc, char *argv[])
 
        for (;;)
        {
-               uint64          micros;
+               struct timeval nap;
                bool    can_launch;
                TimestampTz current_time = 0;
 
@@ -498,11 +504,39 @@ AutoVacLauncherMain(int argc, char *argv[])
                if (!PostmasterIsAlive(true))
                        exit(1);
 
-               micros = launcher_determine_sleep(AutoVacuumShmem->av_freeWorkers !=
-                                                                                 INVALID_OFFSET, false);
+               launcher_determine_sleep(AutoVacuumShmem->av_freeWorkers !=
+                                                                INVALID_OFFSET, false, &nap);
+
+               /*
+                * Sleep for a while according to schedule.  We only sleep in
+                * AV_SLEEP_QUANTUM second intervals, in order to promptly notice
+                * postmaster death.
+                */
+               while (nap.tv_sec > 0 || nap.tv_usec > 0)
+               {
+                       uint32  sleeptime;
+
+                       sleeptime = nap.tv_usec;
+                       nap.tv_usec = 0;
 
-               /* Sleep for a while according to schedule */
-               pg_usleep(micros);
+                       if (nap.tv_sec > 0)
+                       {
+                               sleeptime += Min(nap.tv_sec, AV_SLEEP_QUANTUM) * 1000000;
+                               nap.tv_sec -= Min(nap.tv_sec, AV_SLEEP_QUANTUM);
+                       }
+                       
+                       pg_usleep(sleeptime);
+
+                       /*
+                        * Emergency bailout if postmaster has died.  This is to avoid the
+                        * necessity for manual cleanup of all postmaster children.
+                        */
+                       if (!PostmasterIsAlive(true))
+                               exit(1);
+
+                       if (avlauncher_shutdown_request || got_SIGHUP || got_SIGUSR1)
+                               break;
+               }
 
                /* the normal shutdown case */
                if (avlauncher_shutdown_request)
@@ -647,16 +681,15 @@ AutoVacLauncherMain(int argc, char *argv[])
 }
 
 /*
- * Determine the time to sleep, in microseconds, based on the database list.
+ * Determine the time to sleep, based on the database list.
  *
  * The "canlaunch" parameter indicates whether we can start a worker right now,
- * for example due to the workers being all busy.
+ * for example due to the workers being all busy.  If this is false, we will
+ * cause a long sleep, which will be interrupted when a worker exits.
  */
-static uint64
-launcher_determine_sleep(bool canlaunch, bool recursing)
+static void
+launcher_determine_sleep(bool canlaunch, bool recursing, struct timeval *nap)
 {
-       long    secs;
-       int             usecs;
        Dlelem *elem;
 
        /*
@@ -667,23 +700,28 @@ launcher_determine_sleep(bool canlaunch, bool recursing)
         */
        if (!canlaunch)
        {
-               secs = autovacuum_naptime;
-               usecs = 0;
+               nap->tv_sec = autovacuum_naptime;
+               nap->tv_usec = 0;
        }
        else if ((elem = DLGetTail(DatabaseList)) != NULL)
        {
                avl_dbase  *avdb = DLE_VAL(elem);
                TimestampTz     current_time = GetCurrentTimestamp();
                TimestampTz     next_wakeup;
+               long    secs;
+               int             usecs;
 
                next_wakeup = avdb->adl_next_worker;
                TimestampDifference(current_time, next_wakeup, &secs, &usecs);
+
+               nap->tv_sec = secs;
+               nap->tv_usec = usecs;
        }
        else
        {
                /* list is empty, sleep for whole autovacuum_naptime seconds  */
-               secs = autovacuum_naptime;
-               usecs = 0;
+               nap->tv_sec = autovacuum_naptime;
+               nap->tv_usec = 0;
        }
 
        /*
@@ -696,20 +734,19 @@ launcher_determine_sleep(bool canlaunch, bool recursing)
         * We only recurse once.  rebuild_database_list should always return times
         * in the future, but it seems best not to trust too much on that.
         */
-       if (secs == 0L && usecs == 0 && !recursing)
+       if (nap->tv_sec == 0L && nap->tv_usec == 0 && !recursing)
        {
                rebuild_database_list(InvalidOid);
-               return launcher_determine_sleep(canlaunch, true);
+               launcher_determine_sleep(canlaunch, true, nap);
+               return;
        }
 
        /* 100ms is the smallest time we'll allow the launcher to sleep */
-       if (secs <= 0L && usecs <= 100000)
+       if (nap->tv_sec <= 0L && nap->tv_usec <= 100000)
        {
-               secs = 0L;
-               usecs = 100000; /* 100 ms */
+               nap->tv_sec = 0L;
+               nap->tv_usec = 100000;  /* 100 ms */
        }
-
-       return secs * 1000000 + usecs;
 }
 
 /*
index 387c4ae1531052dfae4b709bdc15eb95853963fa..e808220b8fb0bf07569f42c6d87a2c456899aed0 100644 (file)
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.396 2007/06/08 18:23:52 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.397 2007/06/13 21:24:56 alvherre Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -1645,7 +1645,7 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_S
                },
                &autovacuum_naptime,
-               60, 1, INT_MAX, NULL, NULL
+               60, 1, INT_MAX / 1000, NULL, NULL
        },
        {
                {"autovacuum_vacuum_threshold", PGC_SIGHUP, AUTOVACUUM,