Add VACUUM/ANALYZE BUFFER_USAGE_LIMIT option
authorDavid Rowley <[email protected]>
Thu, 6 Apr 2023 23:40:31 +0000 (11:40 +1200)
committerDavid Rowley <[email protected]>
Thu, 6 Apr 2023 23:40:31 +0000 (11:40 +1200)
Add new options to the VACUUM and ANALYZE commands called
BUFFER_USAGE_LIMIT to allow users more control over how large to make the
buffer access strategy that is used to limit the usage of buffers in
shared buffers.  Larger rings can allow VACUUM to run more quickly but
have the drawback of VACUUM possibly evicting more buffers from shared
buffers that might be useful for other queries running on the database.

Here we also add a new GUC named vacuum_buffer_usage_limit which controls
how large to make the access strategy when it's not specified in the
VACUUM/ANALYZE command.  This defaults to 256KB, which is the same size as
the access strategy was prior to this change.  This setting also
controls how large to make the buffer access strategy for autovacuum.

Per idea by Andres Freund.

Author: Melanie Plageman
Reviewed-by: David Rowley
Reviewed-by: Andres Freund
Reviewed-by: Justin Pryzby
Reviewed-by: Bharath Rupireddy
Discussion: https://postgr.es/m/20230111182720[email protected]

17 files changed:
doc/src/sgml/config.sgml
doc/src/sgml/ref/analyze.sgml
doc/src/sgml/ref/vacuum.sgml
src/backend/commands/vacuum.c
src/backend/commands/vacuumparallel.c
src/backend/postmaster/autovacuum.c
src/backend/storage/buffer/README
src/backend/storage/buffer/freelist.c
src/backend/utils/init/globals.c
src/backend/utils/misc/guc_tables.c
src/backend/utils/misc/postgresql.conf.sample
src/bin/psql/tab-complete.c
src/include/miscadmin.h
src/include/storage/bufmgr.h
src/include/utils/guc_hooks.h
src/test/regress/expected/vacuum.out
src/test/regress/sql/vacuum.sql

index bcc49aec45dc5bf93b9d229bad5af42a269b09a4..992e94400128f86f2df1536676fd35cf72b76902 100644 (file)
@@ -2001,6 +2001,36 @@ include_dir 'conf.d'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-vacuum-buffer-usage-limit" xreflabel="vacuum_buffer_usage_limit">
+      <term>
+       <varname>vacuum_buffer_usage_limit</varname> (<type>integer</type>)
+       <indexterm>
+        <primary><varname>vacuum_buffer_usage_limit</varname> configuration parameter</primary>
+       </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Specifies the size of the
+        <glossterm linkend="glossary-buffer-access-strategy">Buffer Access Strategy</glossterm>
+        used by the <command>VACUUM</command> and <command>ANALYZE</command>
+        commands.  A setting of <literal>0</literal> will allow the operation
+        to use any number of <varname>shared_buffers</varname>.  Otherwise
+        valid sizes range from <literal>128 KB</literal> to
+        <literal>16 GB</literal>.  If the specified size would exceed 1/8 the
+        size of <varname>shared_buffers</varname>, the size is silently capped
+        to that value.  The default value is <literal>256 KB</literal>.  If
+        this value is specified without units, it is taken as kilobytes.  This
+        parameter can be set at any time.  It can be overridden for
+        <xref linkend="sql-vacuum"/> and <xref linkend="sql-analyze"/>
+        when passing the <option>BUFFER_USAGE_LIMIT</option> option.  Higher
+        settings can allow <command>VACUUM</command> and
+        <command>ANALYZE</command> to run more quickly, but having too large a
+        setting may cause too many other useful pages to be evicted from
+        shared buffers.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-logical-decoding-work-mem" xreflabel="logical_decoding_work_mem">
       <term><varname>logical_decoding_work_mem</varname> (<type>integer</type>)
       <indexterm>
index 2f94e89cb0e3164dc9c130f89b3a525ff97e2c68..94e30f1ce742d2550e494667e6e3a5505dba0c17 100644 (file)
@@ -28,6 +28,7 @@ ANALYZE [ VERBOSE ] [ <replaceable class="parameter">table_and_columns</replacea
 
     VERBOSE [ <replaceable class="parameter">boolean</replaceable> ]
     SKIP_LOCKED [ <replaceable class="parameter">boolean</replaceable> ]
+    BUFFER_USAGE_LIMIT [ <replaceable class="parameter">string</replaceable> ]
 
 <phrase>and <replaceable class="parameter">table_and_columns</replaceable> is:</phrase>
 
@@ -95,6 +96,25 @@ ANALYZE [ VERBOSE ] [ <replaceable class="parameter">table_and_columns</replacea
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>BUFFER_USAGE_LIMIT</literal></term>
+    <listitem>
+     <para>
+      Specifies the
+      <glossterm linkend="glossary-buffer-access-strategy">Buffer Access Strategy</glossterm>
+      ring buffer size for <command>ANALYZE</command>.  This size is used to
+      calculate the number of shared buffers which will be reused as part of
+      this strategy.  <literal>0</literal> disables use of a
+      <literal>Buffer Access Strategy</literal>.   When this option is not
+      specified, <command>ANALYZE</command> uses the value from
+      <xref linkend="guc-vacuum-buffer-usage-limit"/>.  Higher settings can
+      allow <command>ANALYZE</command> to run more quickly, but having too
+      large a setting may cause too many other useful pages to be evicted from
+      shared buffers.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><replaceable class="parameter">boolean</replaceable></term>
     <listitem>
index b6d30b576486771fe2796074fa06ce9ab339fc8e..dd0fbb8cb7529634b7dd2de9f264128aa76315a3 100644 (file)
@@ -39,6 +39,7 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet
     PARALLEL <replaceable class="parameter">integer</replaceable>
     SKIP_DATABASE_STATS [ <replaceable class="parameter">boolean</replaceable> ]
     ONLY_DATABASE_STATS [ <replaceable class="parameter">boolean</replaceable> ]
+    BUFFER_USAGE_LIMIT [ <replaceable class="parameter">string</replaceable> ]
 
 <phrase>and <replaceable class="parameter">table_and_columns</replaceable> is:</phrase>
 
@@ -345,6 +346,29 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>BUFFER_USAGE_LIMIT</literal></term>
+    <listitem>
+     <para>
+      Specifies the
+      <glossterm linkend="glossary-buffer-access-strategy">Buffer Access Strategy</glossterm>
+      ring buffer size for <command>VACUUM</command>.  This size is used to
+      calculate the number of shared buffers which will be reused as part of
+      this strategy.  <literal>0</literal> disables use of a
+      <literal>Buffer Access Strategy</literal>.  If <option>ANALYZE</option>
+      is also specified, the <option>BUFFER_USAGE_LIMIT</option> value is used
+      for both the vacuum and analyze stages.  This option can't be used with
+      the <option>FULL</option> option except if <option>ANALYZE</option> is
+      also specified.  When this option is not specified,
+      <command>VACUUM</command> uses the value from
+      <xref linkend="guc-vacuum-buffer-usage-limit"/>.  Higher settings can
+      allow <command>VACUUM</command> to run more quickly, but having too
+      large a setting may cause too many other useful pages to be evicted from
+      shared buffers.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><replaceable class="parameter">boolean</replaceable></term>
     <listitem>
index 9386c08a55672756d6a2197571907d1ed6bf4cf8..1980e7664bc879379821c22fbe717c23e64fc6ce 100644 (file)
@@ -57,6 +57,7 @@
 #include "utils/acl.h"
 #include "utils/fmgroids.h"
 #include "utils/guc.h"
+#include "utils/guc_hooks.h"
 #include "utils/memutils.h"
 #include "utils/pg_rusage.h"
 #include "utils/snapmgr.h"
@@ -120,6 +121,26 @@ static VacOptValue get_vacoptval_from_boolean(DefElem *def);
 static bool vac_tid_reaped(ItemPointer itemptr, void *state);
 static int     vac_cmp_itemptr(const void *left, const void *right);
 
+/*
+ * GUC check function to ensure GUC value specified is within the allowable
+ * range.
+ */
+bool
+check_vacuum_buffer_usage_limit(int *newval, void **extra,
+                                                               GucSource source)
+{
+       /* Value upper and lower hard limits are inclusive */
+       if (*newval == 0 || (*newval >= MIN_BAS_VAC_RING_SIZE_KB &&
+                                                *newval <= MAX_BAS_VAC_RING_SIZE_KB))
+               return true;
+
+       /* Value does not fall within any allowable range */
+       GUC_check_errdetail("\"vacuum_buffer_usage_limit\" must be 0 or between %d KB and %d KB",
+                                               MIN_BAS_VAC_RING_SIZE_KB, MAX_BAS_VAC_RING_SIZE_KB);
+
+       return false;
+}
+
 /*
  * Primary entry point for manual VACUUM and ANALYZE commands
  *
@@ -139,6 +160,7 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
        bool            disable_page_skipping = false;
        bool            process_main = true;
        bool            process_toast = true;
+       int                     ring_size;
        bool            skip_database_stats = false;
        bool            only_database_stats = false;
        MemoryContext vac_context;
@@ -151,6 +173,12 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
        /* By default parallel vacuum is enabled */
        params.nworkers = 0;
 
+       /*
+        * Set this to an invalid value so it is clear whether or not a
+        * BUFFER_USAGE_LIMIT was specified when making the access strategy.
+        */
+       ring_size = -1;
+
        /* Parse options list */
        foreach(lc, vacstmt->options)
        {
@@ -161,6 +189,48 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
                        verbose = defGetBoolean(opt);
                else if (strcmp(opt->defname, "skip_locked") == 0)
                        skip_locked = defGetBoolean(opt);
+               else if (strcmp(opt->defname, "buffer_usage_limit") == 0)
+               {
+                       const char *hintmsg;
+                       int                     result;
+                       char       *vac_buffer_size;
+
+                       if (opt->arg == NULL)
+                       {
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                                errmsg("buffer_usage_limit option requires a valid value"),
+                                                parser_errposition(pstate, opt->location)));
+                       }
+
+                       vac_buffer_size = defGetString(opt);
+
+                       if (!parse_int(vac_buffer_size, &result, GUC_UNIT_KB, &hintmsg))
+                       {
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                errmsg("value: \"%s\": is invalid for buffer_usage_limit",
+                                                               vac_buffer_size),
+                                                hintmsg ? errhint("%s", _(hintmsg)) : 0));
+                       }
+
+                       /*
+                        * Check that the specified size falls within the hard upper and
+                        * lower limits if it is not 0.  We explicitly disallow -1 since
+                        * that behavior can be obtained by not specifying
+                        * BUFFER_USAGE_LIMIT.
+                        */
+                       if (result != 0 &&
+                               (result < MIN_BAS_VAC_RING_SIZE_KB || result > MAX_BAS_VAC_RING_SIZE_KB))
+                       {
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                errmsg("buffer_usage_limit option must be 0 or between %d KB and %d KB",
+                                                               MIN_BAS_VAC_RING_SIZE_KB, MAX_BAS_VAC_RING_SIZE_KB)));
+                       }
+
+                       ring_size = result;
+               }
                else if (!vacstmt->is_vacuumcmd)
                        ereport(ERROR,
                                        (errcode(ERRCODE_SYNTAX_ERROR),
@@ -265,6 +335,17 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("VACUUM FULL cannot be performed in parallel")));
 
+       /*
+        * BUFFER_USAGE_LIMIT does nothing for VACUUM (FULL) so just raise an
+        * ERROR for that case.  VACUUM (FULL, ANALYZE) does make use of it, so
+        * we'll permit that.
+        */
+       if (ring_size != -1 && (params.options & VACOPT_FULL) &&
+               !(params.options & VACOPT_ANALYZE))
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("BUFFER_USAGE_LIMIT cannot be specified for VACUUM FULL")));
+
        /*
         * Make sure VACOPT_ANALYZE is specified if any column lists are present.
         */
@@ -366,7 +447,19 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 
                MemoryContext old_context = MemoryContextSwitchTo(vac_context);
 
-               bstrategy = GetAccessStrategy(BAS_VACUUM);
+               Assert(ring_size >= -1);
+
+               /*
+                * If BUFFER_USAGE_LIMIT was specified by the VACUUM or ANALYZE
+                * command, it overrides the value of VacuumBufferUsageLimit.  Either
+                * value may be 0, in which case GetAccessStrategyWithSize() will
+                * return NULL, effectively allowing full use of shared buffers.
+                */
+               if (ring_size == -1)
+                       ring_size = VacuumBufferUsageLimit;
+
+               bstrategy = GetAccessStrategyWithSize(BAS_VACUUM, ring_size);
+
                MemoryContextSwitchTo(old_context);
        }
 
index e200d5caf826ec2a4bbb7268e0652dbf857c75f9..87ea5c524264ca27908c6032b43d51313b14c32f 100644 (file)
@@ -87,6 +87,12 @@ typedef struct PVShared
         */
        int                     maintenance_work_mem_worker;
 
+       /*
+        * The number of buffers each worker's Buffer Access Strategy ring should
+        * contain.
+        */
+       int                     ring_nbuffers;
+
        /*
         * Shared vacuum cost balance.  During parallel vacuum,
         * VacuumSharedCostBalance points to this value and it accumulates the
@@ -365,6 +371,9 @@ parallel_vacuum_init(Relation rel, Relation *indrels, int nindexes,
                maintenance_work_mem / Min(parallel_workers, nindexes_mwm) :
                maintenance_work_mem;
 
+       /* Use the same buffer size for all workers */
+       shared->ring_nbuffers = GetAccessStrategyBufferCount(bstrategy);
+
        pg_atomic_init_u32(&(shared->cost_balance), 0);
        pg_atomic_init_u32(&(shared->active_nworkers), 0);
        pg_atomic_init_u32(&(shared->idx), 0);
@@ -1018,8 +1027,9 @@ parallel_vacuum_main(dsm_segment *seg, shm_toc *toc)
        pvs.indname = NULL;
        pvs.status = PARALLEL_INDVAC_STATUS_INITIAL;
 
-       /* Each parallel VACUUM worker gets its own access strategy */
-       pvs.bstrategy = GetAccessStrategy(BAS_VACUUM);
+       /* Each parallel VACUUM worker gets its own access strategy. */
+       pvs.bstrategy = GetAccessStrategyWithSize(BAS_VACUUM,
+                                                                                         shared->ring_nbuffers * (BLCKSZ / 1024));
 
        /* Setup error traceback support for ereport() */
        errcallback.callback = parallel_vacuum_error_callback;
index 90cfc18f100e281f05ec2db35d3e026ba7aaa28b..53c8f8d79cba63b5c1c1e42bfe91c1ab0b972c68 100644 (file)
@@ -2349,11 +2349,21 @@ do_autovacuum(void)
        }
 
        /*
-        * Create a buffer access strategy object for VACUUM to use.  We want to
-        * use the same one across all the vacuum operations we perform, since the
-        * point is for VACUUM not to blow out the shared cache.
+        * Optionally, create a buffer access strategy object for VACUUM to use.
+        * We use the same BufferAccessStrategy object for all tables VACUUMed by
+        * this worker to prevent autovacuum from blowing out shared buffers.
+        *
+        * VacuumBufferUsageLimit being set to 0 results in
+        * GetAccessStrategyWithSize returning NULL, effectively meaning we can
+        * use up to all of shared buffers.
+        *
+        * If we later enter failsafe mode on any of the tables being vacuumed, we
+        * will cease use of the BufferAccessStrategy only for that table.
+        *
+        * XXX should we consider adding code to adjust the size of this if
+        * VacuumBufferUsageLimit changes?
         */
-       bstrategy = GetAccessStrategy(BAS_VACUUM);
+       bstrategy = GetAccessStrategyWithSize(BAS_VACUUM, VacuumBufferUsageLimit);
 
        /*
         * create a memory context to act as fake PortalContext, so that the
index a775276ff2ce1f08ded462dfabf5e87d02501da3..011af7aff3e30fd0d9dd781aa9fbabf213518915 100644 (file)
@@ -229,12 +229,12 @@ update hint bits).  In a scan that modifies every page in the scan, like a
 bulk UPDATE or DELETE, the buffers in the ring will always be dirtied and
 the ring strategy effectively degrades to the normal strategy.
 
-VACUUM uses a 256KB ring like sequential scans, but dirty pages are not
-removed from the ring.  Instead, WAL is flushed if needed to allow reuse of
-the buffers.  Before introducing the buffer ring strategy in 8.3, VACUUM's
-buffers were sent to the freelist, which was effectively a buffer ring of 1
-buffer, resulting in excessive WAL flushing.  Allowing VACUUM to update
-256KB between WAL flushes should be more efficient.
+VACUUM uses a ring like sequential scans, however, the size of this ring is
+controlled by the vacuum_buffer_usage_limit GUC.  Dirty pages are not removed
+from the ring.  Instead, WAL is flushed if needed to allow reuse of the
+buffers.  Before introducing the buffer ring strategy in 8.3, VACUUM's buffers
+were sent to the freelist, which was effectively a buffer ring of 1 buffer,
+resulting in excessive WAL flushing.
 
 Bulk writes work similarly to VACUUM.  Currently this applies only to
 COPY IN and CREATE TABLE AS SELECT.  (Might it be interesting to make
index f122709fbe2b43169a2d33d1f84bbe7f0b75560b..1c804fd2f5973b437f21e2ea0fe4c4a26673abf7 100644 (file)
@@ -540,8 +540,7 @@ StrategyInitialize(bool init)
 BufferAccessStrategy
 GetAccessStrategy(BufferAccessStrategyType btype)
 {
-       BufferAccessStrategy strategy;
-       int                     nbuffers;
+       int                     ring_size_kb;
 
        /*
         * Select ring size to use.  See buffer/README for rationales.
@@ -556,13 +555,13 @@ GetAccessStrategy(BufferAccessStrategyType btype)
                        return NULL;
 
                case BAS_BULKREAD:
-                       nbuffers = 256 * 1024 / BLCKSZ;
+                       ring_size_kb = 256;
                        break;
                case BAS_BULKWRITE:
-                       nbuffers = 16 * 1024 * 1024 / BLCKSZ;
+                       ring_size_kb = 16 * 1024;
                        break;
                case BAS_VACUUM:
-                       nbuffers = 256 * 1024 / BLCKSZ;
+                       ring_size_kb = 256;
                        break;
 
                default:
@@ -571,21 +570,65 @@ GetAccessStrategy(BufferAccessStrategyType btype)
                        return NULL;            /* keep compiler quiet */
        }
 
-       /* Make sure ring isn't an undue fraction of shared buffers */
-       nbuffers = Min(NBuffers / 8, nbuffers);
+       return GetAccessStrategyWithSize(btype, ring_size_kb);
+}
+
+/*
+ * GetAccessStrategyWithSize -- create a BufferAccessStrategy object with a
+ *             number of buffers equivalent to the passed in size.
+ *
+ * If the given ring size is 0, no BufferAccessStrategy will be created and
+ * the function will return NULL.  ring_size_kb must not be negative.
+ */
+BufferAccessStrategy
+GetAccessStrategyWithSize(BufferAccessStrategyType btype, int ring_size_kb)
+{
+       int                     ring_buffers;
+       BufferAccessStrategy strategy;
+
+       Assert(ring_size_kb >= 0);
+
+       /* Figure out how many buffers ring_size_kb is */
+       ring_buffers = ring_size_kb / (BLCKSZ / 1024);
+
+       /* 0 means unlimited, so no BufferAccessStrategy required */
+       if (ring_buffers == 0)
+               return NULL;
+
+       /* Cap to 1/8th of shared_buffers */
+       ring_buffers = Min(NBuffers / 8, ring_buffers);
+
+       /* NBuffers should never be less than 16, so this shouldn't happen */
+       Assert(ring_buffers > 0);
 
        /* Allocate the object and initialize all elements to zeroes */
        strategy = (BufferAccessStrategy)
                palloc0(offsetof(BufferAccessStrategyData, buffers) +
-                               nbuffers * sizeof(Buffer));
+                               ring_buffers * sizeof(Buffer));
 
        /* Set fields that don't start out zero */
        strategy->btype = btype;
-       strategy->nbuffers = nbuffers;
+       strategy->nbuffers = ring_buffers;
 
        return strategy;
 }
 
+/*
+ * GetAccessStrategyBufferCount -- an accessor for the number of buffers in
+ *             the ring
+ *
+ * Returns 0 on NULL input to match behavior of GetAccessStrategyWithSize()
+ * returning NULL with 0 size.
+ */
+int
+GetAccessStrategyBufferCount(BufferAccessStrategy strategy)
+{
+       if (strategy == NULL)
+               return 0;
+
+       return strategy->nbuffers;
+}
+
 /*
  * FreeAccessStrategy -- release a BufferAccessStrategy object
  *
index 1b1d8142548aaf1234fcf1fc92753b9a8b8b3537..011ec18015a2c1c148229483e5b0b2c1c7c89c77 100644 (file)
@@ -139,7 +139,10 @@ int                        max_worker_processes = 8;
 int                    max_parallel_workers = 8;
 int                    MaxBackends = 0;
 
-int                    VacuumCostPageHit = 1;  /* GUC parameters for vacuum */
+/* GUC parameters for vacuum */
+int                    VacuumBufferUsageLimit = 256;
+
+int                    VacuumCostPageHit = 1;
 int                    VacuumCostPageMiss = 2;
 int                    VacuumCostPageDirty = 20;
 int                    VacuumCostLimit = 200;
index 8062589efd5201948e3a0b07a9a9f5d5092eadb5..e8e8245e91ff95e11c0469909d7508df08fc8acd 100644 (file)
@@ -2224,6 +2224,17 @@ struct config_int ConfigureNamesInt[] =
                NULL, NULL, NULL
        },
 
+       {
+               {"vacuum_buffer_usage_limit", PGC_USERSET, RESOURCES_MEM,
+                       gettext_noop("Sets the buffer pool size for VACUUM, ANALYZE, and autovacuum."),
+                       NULL,
+                       GUC_UNIT_KB
+               },
+               &VacuumBufferUsageLimit,
+               256, 0, MAX_BAS_VAC_RING_SIZE_KB,
+               check_vacuum_buffer_usage_limit, NULL, NULL
+       },
+
        {
                {"shared_memory_size", PGC_INTERNAL, PRESET_OPTIONS,
                        gettext_noop("Shows the size of the server's main shared memory area (rounded up to the nearest MB)."),
index ee49ca39370977d896cc75e68e3b7391a8f31ee1..e715aff3b8116abdf8a8b651fecd05b6d9b4c760 100644 (file)
                                        #   mmap
                                        # (change requires restart)
 #min_dynamic_shared_memory = 0MB       # (change requires restart)
+#vacuum_buffer_usage_limit = 256 # size of vacuum and analyze buffer access strategy ring.
+                                # 0 to disable vacuum buffer access strategy
+                                # range 128kB to 16GB
 
 # - Disk -
 
index e38a49e8bd7032ed36b5fdb4114b2c022a0456bf..6614fd2e6284d1a741abaa54cf7a464f0ea2fbec 100644 (file)
@@ -2662,7 +2662,7 @@ psql_completion(const char *text, int start, int end)
                 * one word, so the above test is correct.
                 */
                if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
-                       COMPLETE_WITH("VERBOSE", "SKIP_LOCKED");
+                       COMPLETE_WITH("VERBOSE", "SKIP_LOCKED", "BUFFER_USAGE_LIMIT");
                else if (TailMatches("VERBOSE|SKIP_LOCKED"))
                        COMPLETE_WITH("ON", "OFF");
        }
@@ -4620,7 +4620,7 @@ psql_completion(const char *text, int start, int end)
                                                  "DISABLE_PAGE_SKIPPING", "SKIP_LOCKED",
                                                  "INDEX_CLEANUP", "PROCESS_MAIN", "PROCESS_TOAST",
                                                  "TRUNCATE", "PARALLEL", "SKIP_DATABASE_STATS",
-                                                 "ONLY_DATABASE_STATS");
+                                                 "ONLY_DATABASE_STATS", "BUFFER_USAGE_LIMIT");
                else if (TailMatches("FULL|FREEZE|ANALYZE|VERBOSE|DISABLE_PAGE_SKIPPING|SKIP_LOCKED|PROCESS_MAIN|PROCESS_TOAST|TRUNCATE|SKIP_DATABASE_STATS|ONLY_DATABASE_STATS"))
                        COMPLETE_WITH("ON", "OFF");
                else if (TailMatches("INDEX_CLEANUP"))
index 06a86f9ac1f503ca7be58853f83f5daae8678627..14bd574fc24ef2e64ce75ed8432942d9108dce12 100644 (file)
@@ -263,6 +263,15 @@ extern PGDLLIMPORT double hash_mem_multiplier;
 extern PGDLLIMPORT int maintenance_work_mem;
 extern PGDLLIMPORT int max_parallel_maintenance_workers;
 
+/*
+ * Upper and lower hard limits for the buffer access strategy ring size
+ * specified by the VacuumBufferUsageLimit GUC and BUFFER_USAGE_LIMIT option
+ * to VACUUM and ANALYZE.
+ */
+#define MIN_BAS_VAC_RING_SIZE_KB 128
+#define MAX_BAS_VAC_RING_SIZE_KB (16 * 1024 * 1024)
+
+extern PGDLLIMPORT int VacuumBufferUsageLimit;
 extern PGDLLIMPORT int VacuumCostPageHit;
 extern PGDLLIMPORT int VacuumCostPageMiss;
 extern PGDLLIMPORT int VacuumCostPageDirty;
index 788aa279ba03dfae18f7a55f9aba47654d1c7d12..6ab00daa2ea47528bb1a2f459c551383fdc968be 100644 (file)
@@ -260,7 +260,12 @@ extern Size BufferShmemSize(void);
 extern void AtProcExit_LocalBuffers(void);
 
 /* in freelist.c */
+
 extern BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype);
+extern BufferAccessStrategy GetAccessStrategyWithSize(BufferAccessStrategyType btype,
+                                                                                                         int ring_size_kb);
+extern int     GetAccessStrategyBufferCount(BufferAccessStrategy strategy);
+
 extern void FreeAccessStrategy(BufferAccessStrategy strategy);
 
 
index aeb3663071fe170042cc7b99b9d81102dea93000..f722fb250aa0cd897c60aabf5372afd41d4dd3c0 100644 (file)
@@ -33,6 +33,8 @@ extern bool check_autovacuum_max_workers(int *newval, void **extra,
                                                                                 GucSource source);
 extern bool check_autovacuum_work_mem(int *newval, void **extra,
                                                                          GucSource source);
+extern bool check_vacuum_buffer_usage_limit(int *newval, void **extra,
+                                                                                       GucSource source);
 extern bool check_backtrace_functions(char **newval, void **extra,
                                                                          GucSource source);
 extern void assign_backtrace_functions(const char *newval, void *extra);
index e5a312182eba080b2d9a256111acdc3fc83d2011..eae274378836b4513477a9ec03fd8a07f0d17e3d 100644 (file)
@@ -350,6 +350,25 @@ SELECT t.relfilenode = :toast_filenode AS is_same_toast_filenode
  f
 (1 row)
 
+-- BUFFER_USAGE_LIMIT option
+VACUUM (BUFFER_USAGE_LIMIT '512 kB') vac_option_tab;
+ANALYZE (BUFFER_USAGE_LIMIT '512 kB') vac_option_tab;
+-- try disabling the buffer usage limit
+VACUUM (BUFFER_USAGE_LIMIT 0) vac_option_tab;
+ANALYZE (BUFFER_USAGE_LIMIT 0) vac_option_tab;
+-- value exceeds max size error
+VACUUM (BUFFER_USAGE_LIMIT 16777220) vac_option_tab;
+ERROR:  buffer_usage_limit option must be 0 or between 128 KB and 16777216 KB
+-- value is less than min size error
+VACUUM (BUFFER_USAGE_LIMIT 120) vac_option_tab;
+ERROR:  buffer_usage_limit option must be 0 or between 128 KB and 16777216 KB
+-- integer overflow error
+VACUUM (BUFFER_USAGE_LIMIT 10000000000) vac_option_tab;
+ERROR:  value: "10000000000": is invalid for buffer_usage_limit
+HINT:  Value exceeds integer range.
+-- incompatible with VACUUM FULL error
+VACUUM (BUFFER_USAGE_LIMIT '512 kB', FULL) vac_option_tab;
+ERROR:  BUFFER_USAGE_LIMIT cannot be specified for VACUUM FULL
 -- SKIP_DATABASE_STATS option
 VACUUM (SKIP_DATABASE_STATS) vactst;
 -- ONLY_DATABASE_STATS option
index a1fad43657cc721ac2c7b53f5355c9a5b86f5394..51d7b1fecc71044fd66a27a19f99ebfe8a9125df 100644 (file)
@@ -272,6 +272,21 @@ SELECT t.relfilenode = :toast_filenode AS is_same_toast_filenode
   FROM pg_class c, pg_class t
   WHERE c.reltoastrelid = t.oid AND c.relname = 'vac_option_tab';
 
+-- BUFFER_USAGE_LIMIT option
+VACUUM (BUFFER_USAGE_LIMIT '512 kB') vac_option_tab;
+ANALYZE (BUFFER_USAGE_LIMIT '512 kB') vac_option_tab;
+-- try disabling the buffer usage limit
+VACUUM (BUFFER_USAGE_LIMIT 0) vac_option_tab;
+ANALYZE (BUFFER_USAGE_LIMIT 0) vac_option_tab;
+-- value exceeds max size error
+VACUUM (BUFFER_USAGE_LIMIT 16777220) vac_option_tab;
+-- value is less than min size error
+VACUUM (BUFFER_USAGE_LIMIT 120) vac_option_tab;
+-- integer overflow error
+VACUUM (BUFFER_USAGE_LIMIT 10000000000) vac_option_tab;
+-- incompatible with VACUUM FULL error
+VACUUM (BUFFER_USAGE_LIMIT '512 kB', FULL) vac_option_tab;
+
 -- SKIP_DATABASE_STATS option
 VACUUM (SKIP_DATABASE_STATS) vactst;