Re: reducing statistics write overhead - Mailing list pgsql-hackers

From Martin Pihlak
Subject Re: reducing statistics write overhead
Date
Msg-id 4976FEA2.3090203@gmail.com
Whole thread Raw
In response to Re: reducing statistics write overhead  (Martin Pihlak <martin.pihlak@gmail.com>)
List pgsql-hackers
I wrote:
> I was thinking that the launcher should only request fresh stats at wakeup,
> the workers could then reuse that file. This could be implemented by calling
> pgstat_clear_snapshot only at launcher wakeup and setting max stats age to
> to autovacuum_naptime for the workers.
>

Attached is a patch that increases the autovacuum stats age tolerance to
autovacuum_naptime. This is handled by autovac_refresh_stats() by not clearing
the stats snapshot unless nap time elapsed or explicitly forced by an error
or SIGHUP.

For the time being, I left the table vacuum recheck in place. Removing the
table_recheck_autovac function requires some further work. I have started on
this, but decided to defer until it is clear whether the whole approach is
acceptable or not.

regards,
Martin

*** a/src/backend/postmaster/autovacuum.c
--- b/src/backend/postmaster/autovacuum.c
***************
*** 44,54 ****
   * Note that there can be more than one worker in a database concurrently.
   * They will store the table they are currently vacuuming in shared memory, so
   * that other workers avoid being blocked waiting for the vacuum lock for that
!  * table.  They will also reload the pgstats data just before vacuuming each
!  * table, to avoid vacuuming a table that was just finished being vacuumed by
!  * another worker and thus is no longer noted in shared memory.  However,
!  * there is a window (caused by pgstat delay) on which a worker may choose a
!  * table that was already vacuumed; this is a bug in the current design.
   *
   * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
--- 44,53 ----
   * Note that there can be more than one worker in a database concurrently.
   * They will store the table they are currently vacuuming in shared memory, so
   * that other workers avoid being blocked waiting for the vacuum lock for that
!  * table. There is a possibility that a worker might pick up a table that was
!  * already vacuumed by another process. This isn't really a problem, as the
!  * odds of this happening are low and the revacuum is made cheap by the use of
!  * visibility map.
   *
   * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
***************
*** 120,128 **** int            autovacuum_vac_cost_limit;

  int            Log_autovacuum_min_duration = -1;

- /* how long to keep pgstat data in the launcher, in milliseconds */
- #define STATS_READ_DELAY 1000
-

  /* Flags to tell if we are in an autovacuum process */
  static bool am_autovacuum_launcher = false;
--- 119,124 ----
***************
*** 298,304 **** static void avl_sighup_handler(SIGNAL_ARGS);
  static void avl_sigusr1_handler(SIGNAL_ARGS);
  static void avl_sigterm_handler(SIGNAL_ARGS);
  static void avl_quickdie(SIGNAL_ARGS);
! static void autovac_refresh_stats(void);



--- 294,300 ----
  static void avl_sigusr1_handler(SIGNAL_ARGS);
  static void avl_sigterm_handler(SIGNAL_ARGS);
  static void avl_quickdie(SIGNAL_ARGS);
! static void autovac_refresh_stats(bool force);



***************
*** 500,509 **** AutoVacLauncherMain(int argc, char *argv[])
          DatabaseList = NULL;

          /*
!          * Make sure pgstat also considers our stat data as gone.  Note: we
!          * mustn't use autovac_refresh_stats here.
           */
!         pgstat_clear_snapshot();

          /* Now we can allow interrupts again */
          RESUME_INTERRUPTS();
--- 496,504 ----
          DatabaseList = NULL;

          /*
!          * Make sure pgstat also considers our stat data as gone.
           */
!         autovac_refresh_stats(true);

          /* Now we can allow interrupts again */
          RESUME_INTERRUPTS();
***************
*** 598,603 **** AutoVacLauncherMain(int argc, char *argv[])
--- 593,601 ----
          if (got_SIGTERM)
              break;

+         /* Refresh stats. Force it, if reloaded via SIGHUP */
+         autovac_refresh_stats(got_SIGHUP);
+
          if (got_SIGHUP)
          {
              got_SIGHUP = false;
***************
*** 851,859 **** rebuild_database_list(Oid newdb)
      int            nelems;
      HTAB       *dbhash;

-     /* use fresh stats */
-     autovac_refresh_stats();
-
      newcxt = AllocSetContextCreate(AutovacMemCxt,
                                     "AV dblist",
                                     ALLOCSET_DEFAULT_MINSIZE,
--- 849,854 ----
***************
*** 1078,1086 **** do_start_worker(void)
                                     ALLOCSET_DEFAULT_MAXSIZE);
      oldcxt = MemoryContextSwitchTo(tmpcxt);

-     /* use fresh stats */
-     autovac_refresh_stats();
-
      /* Get a list of databases */
      dblist = get_database_list();

--- 1073,1078 ----
***************
*** 2145,2158 **** do_autovacuum(void)
          }

          /*
!          * Check whether pgstat data still says we need to vacuum this table.
!          * It could have changed if something else processed the table while
!          * we weren't looking.
!          *
!          * Note: we have a special case in pgstat code to ensure that the stats
!          * we read are as up-to-date as possible, to avoid the problem that
!          * somebody just finished vacuuming this table.  The window to the race
!          * condition is not closed but it is very small.
           */
          MemoryContextSwitchTo(AutovacMemCxt);
          tab = table_recheck_autovac(relid, table_toast_map);
--- 2137,2145 ----
          }

          /*
!          * Check whether we still need to vacuum this table.  It could have
!          * changed if something else processed the table while we weren't
!          * looking.
           */
          MemoryContextSwitchTo(AutovacMemCxt);
          tab = table_recheck_autovac(relid, table_toast_map);
***************
*** 2377,2385 **** table_recheck_autovac(Oid relid, HTAB *table_toast_map)
      PgStat_StatDBEntry *dbentry;
      bool        wraparound;

-     /* use fresh stats */
-     autovac_refresh_stats();
-
      shared = pgstat_fetch_stat_dbentry(InvalidOid);
      dbentry = pgstat_fetch_stat_dbentry(MyDatabaseId);

--- 2364,2369 ----
***************
*** 2816,2844 **** AutoVacuumShmemInit(void)
   *        Refresh pgstats data for an autovacuum process
   *
   * Cause the next pgstats read operation to obtain fresh data, but throttle
!  * such refreshing in the autovacuum launcher.    This is mostly to avoid
!  * rereading the pgstats files too many times in quick succession when there
!  * are many databases.
!  *
!  * Note: we avoid throttling in the autovac worker, as it would be
!  * counterproductive in the recheck logic.
   */
  static void
! autovac_refresh_stats(void)
  {
!     if (IsAutoVacuumLauncherProcess())
!     {
!         static TimestampTz last_read = 0;
!         TimestampTz current_time;

!         current_time = GetCurrentTimestamp();

!         if (!TimestampDifferenceExceeds(last_read, current_time,
!                                         STATS_READ_DELAY))
!             return;

!         last_read = current_time;
!     }

      pgstat_clear_snapshot();
  }
--- 2800,2822 ----
   *        Refresh pgstats data for an autovacuum process
   *
   * Cause the next pgstats read operation to obtain fresh data, but throttle
!  * the refreshing so it only occurs at autovacuum_naptime intervals. This is
!  * mostly to avoid rereading the pgstats files too many times in quick
!  * succession when there are many databases.
   */
  static void
! autovac_refresh_stats(bool force)
  {
!     static TimestampTz last_read = 0;
!     TimestampTz current_time;

!     current_time = GetCurrentTimestamp();

!     if (!force && !TimestampDifferenceExceeds(last_read, current_time,
!                                               autovacuum_naptime * 1000))
!         return;

!     last_read = current_time;

      pgstat_clear_snapshot();
  }
*** a/src/backend/postmaster/pgstat.c
--- b/src/backend/postmaster/pgstat.c
***************
*** 3388,3396 **** backend_read_statsfile(void)
       * We set the minimum acceptable timestamp to PGSTAT_STAT_INTERVAL msec
       * before now.  This indirectly ensures that the collector needn't write
       * the file more often than PGSTAT_STAT_INTERVAL.  In an autovacuum
!      * worker, however, we want a lower delay to avoid using stale data, so we
!      * use PGSTAT_RETRY_DELAY (since the number of worker is low, this
!      * shouldn't be a problem).
       *
       * Note that we don't recompute min_ts after sleeping; so we might end up
       * accepting a file a bit older than PGSTAT_STAT_INTERVAL.  In practice
--- 3388,3395 ----
       * We set the minimum acceptable timestamp to PGSTAT_STAT_INTERVAL msec
       * before now.  This indirectly ensures that the collector needn't write
       * the file more often than PGSTAT_STAT_INTERVAL.  In an autovacuum
!      * worker, however, we can reuse the stats file that was requested by the
!      * launcher.
       *
       * Note that we don't recompute min_ts after sleeping; so we might end up
       * accepting a file a bit older than PGSTAT_STAT_INTERVAL.  In practice
***************
*** 3400,3406 **** backend_read_statsfile(void)
       */
      if (IsAutoVacuumWorkerProcess())
          min_ts = TimestampTzPlusMilliseconds(GetCurrentTimestamp(),
!                                              -PGSTAT_RETRY_DELAY);
      else
          min_ts = TimestampTzPlusMilliseconds(GetCurrentTimestamp(),
                                               -PGSTAT_STAT_INTERVAL);
--- 3399,3405 ----
       */
      if (IsAutoVacuumWorkerProcess())
          min_ts = TimestampTzPlusMilliseconds(GetCurrentTimestamp(),
!                                              -autovacuum_naptime * 1000);
      else
          min_ts = TimestampTzPlusMilliseconds(GetCurrentTimestamp(),
                                               -PGSTAT_STAT_INTERVAL);

pgsql-hackers by date:

Previous
From: Gregory Stark
Date:
Subject: Re: FWD: Re: Updated backslash consistency patch
Next
From: Simon Riggs
Date:
Subject: Re: rmgr hooks (v2)