On Wed, Mar 15, 2023 at 9:03 PM Melanie Plageman <melanieplage...@gmail.com> wrote: > On Sat, Mar 11, 2023 at 2:16 PM Justin Pryzby <pry...@telsasoft.com> wrote: > > On Sat, Mar 11, 2023 at 09:55:33AM -0500, Melanie Plageman wrote: > > This patch should add support in vacuumdb.c. And maybe a comment about > > adding support there, since it's annoying when it the release notes one > > year say "support VACUUM (FOO)" and then one year later say "support > > vacuumdb --foo". > > So, v4 adds support for buffer-usage-limit to vacuumdb. There are a few > issues. The main one is that no other vacuumdb option takes a size as a > parameter. I couldn't actually find any other client with a parameter > specified as a size. > > My VACUUM option code is using the GUC size parsing code from > parse_int() -- including the unit flag GUC_UNIT_KB. Now that vacuumdb > also needs to parse sizes, I think we'll need to lift the parse_int() > code and the unit_conversion struct and > unit_conversion_memory_unit_conversion_table out of guc.c and put it > somewhere that it can be accessed for more than guc parsing (e.g. option > parsing). > > For vacuumdb in this version, I just specified buffer-usage-limit is > only in kB and thus can only be specified as an int. > > If we had something like pg_parse_size() in common, would this make > sense? It would be a little bit of work to figure out what to do about > the flags, etc. > > Another issue is the server-side guc > #define MAX_BAS_RING_SIZE_KB (16 * 1024 * 1024) > I just redefined it in vacuumdb code. I'm not sure what the preferred > method for dealing with this is. > > I know this validation would get done server-side if I just passed the > user-specified option through, but all of the other vacuumdb options > appear to be doing min/max boundary validation on the client side.
So, after discussing vacuumdb client-side validation off-list with Jelte, I realized that I was trying to do too much there. Attached v5 passes the contents of the buffer-usage-limit option to vacuumdb unvalidated into the VACUUM command string which vacuumdb builds. This solves most of the problems. I also improved the error messages coming from VACUUM (buffer_usage_limit) handling. - Melanie
From 5ca6eed0c126ed050277960d0a74979d70031239 Mon Sep 17 00:00:00 2001 From: Melanie Plageman <melanieplage...@gmail.com> Date: Wed, 22 Feb 2023 12:26:01 -0500 Subject: [PATCH v5 2/3] use shared buffers when failsafe active --- src/backend/access/heap/vacuumlazy.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index 8f14cf85f3..3de7976cf6 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -2622,6 +2622,13 @@ lazy_check_wraparound_failsafe(LVRelState *vacrel) if (unlikely(vacuum_xid_failsafe_check(&vacrel->cutoffs))) { vacrel->failsafe_active = true; + /* + * Abandon use of a buffer access strategy when entering failsafe mode, + * as completing the ongoing VACUUM is our top priority. + * Assume the caller who allocated the memory for the + * BufferAccessStrategy object will free it. + */ + vacrel->bstrategy = NULL; /* Disable index vacuuming, index cleanup, and heap rel truncation */ vacrel->do_index_vacuuming = false; -- 2.37.2
From a3bf94e4044f14dcd7b5c062b8417a3e0b9fe686 Mon Sep 17 00:00:00 2001 From: Melanie Plageman <melanieplage...@gmail.com> Date: Wed, 22 Feb 2023 12:06:41 -0500 Subject: [PATCH v5 1/3] remove global variable vac_strategy --- src/backend/commands/vacuum.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index c54360a6a0..a6aac30529 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -75,7 +75,6 @@ int vacuum_multixact_failsafe_age; /* A few variables that don't seem worth passing around as parameters */ static MemoryContext vac_context = NULL; -static BufferAccessStrategy vac_strategy; /* @@ -94,7 +93,7 @@ static void vac_truncate_clog(TransactionId frozenXID, TransactionId lastSaneFrozenXid, MultiXactId lastSaneMinMulti); static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, - bool skip_privs); + bool skip_privs, BufferAccessStrategy bstrategy); static double compute_parallel_delay(void); static VacOptValue get_vacoptval_from_boolean(DefElem *def); static bool vac_tid_reaped(ItemPointer itemptr, void *state); @@ -338,7 +337,7 @@ vacuum(List *relations, VacuumParams *params, in_outer_xact = IsInTransactionBlock(isTopLevel); /* - * Due to static variables vac_context, anl_context and vac_strategy, + * Due to static variable vac_context * vacuum() is not reentrant. This matters when VACUUM FULL or ANALYZE * calls a hostile index expression that itself calls ANALYZE. */ @@ -404,7 +403,6 @@ vacuum(List *relations, VacuumParams *params, bstrategy = GetAccessStrategy(BAS_VACUUM); MemoryContextSwitchTo(old_context); } - vac_strategy = bstrategy; /* * Build list of relation(s) to process, putting any new data in @@ -509,7 +507,7 @@ vacuum(List *relations, VacuumParams *params, if (params->options & VACOPT_VACUUM) { - if (!vacuum_rel(vrel->oid, vrel->relation, params, false)) + if (!vacuum_rel(vrel->oid, vrel->relation, params, false, bstrategy)) continue; } @@ -527,7 +525,7 @@ vacuum(List *relations, VacuumParams *params, } analyze_rel(vrel->oid, vrel->relation, params, - vrel->va_cols, in_outer_xact, vac_strategy); + vrel->va_cols, in_outer_xact, bstrategy); if (use_own_xacts) { @@ -1838,7 +1836,7 @@ vac_truncate_clog(TransactionId frozenXID, * At entry and exit, we are not inside a transaction. */ static bool -vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, bool skip_privs) +vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, bool skip_privs, BufferAccessStrategy bstrategy) { LOCKMODE lmode; Relation rel; @@ -2084,7 +2082,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, bool skip_privs) cluster_rel(relid, InvalidOid, &cluster_params); } else - table_relation_vacuum(rel, params, vac_strategy); + table_relation_vacuum(rel, params, bstrategy); } /* Roll back any GUC changes executed by index functions */ @@ -2118,7 +2116,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, bool skip_privs) memcpy(&toast_vacuum_params, params, sizeof(VacuumParams)); toast_vacuum_params.options |= VACOPT_PROCESS_MAIN; - vacuum_rel(toast_relid, NULL, &toast_vacuum_params, true); + vacuum_rel(toast_relid, NULL, &toast_vacuum_params, true, bstrategy); } /* -- 2.37.2
From a38fb38daf476cc4a572757d2634713bc2bd4f8a Mon Sep 17 00:00:00 2001 From: Melanie Plageman <melanieplage...@gmail.com> Date: Thu, 16 Mar 2023 20:27:51 -0400 Subject: [PATCH v5 3/3] Add vacuum[db] buffer usage limit option and guc --- doc/src/sgml/config.sgml | 25 +++++++++ doc/src/sgml/ref/vacuum.sgml | 22 ++++++++ doc/src/sgml/ref/vacuumdb.sgml | 20 ++++++++ src/backend/commands/vacuum.c | 51 ++++++++++++++++++- src/backend/commands/vacuumparallel.c | 6 ++- src/backend/postmaster/autovacuum.c | 15 +++++- src/backend/storage/buffer/README | 2 + src/backend/storage/buffer/freelist.c | 48 +++++++++++++++++ src/backend/utils/init/globals.c | 2 + src/backend/utils/misc/guc_tables.c | 11 ++++ src/backend/utils/misc/postgresql.conf.sample | 4 ++ src/bin/psql/tab-complete.c | 2 +- src/bin/scripts/vacuumdb.c | 21 ++++++++ src/include/commands/vacuum.h | 4 ++ src/include/miscadmin.h | 1 + src/include/storage/bufmgr.h | 5 ++ 16 files changed, 235 insertions(+), 4 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index e5c41cc6c6..241aafddd9 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -1981,6 +1981,31 @@ 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 ring buffer size to be used for a given invocation of + <command>VACUUM</command> or instance of autovacuum. This size is + converted to a number of shared buffers which will be reused as part of + a <literal>Buffer Access Strategy</literal>. <literal>0</literal> will + disable use of a <literal>Buffer Access Strategy</literal>. + <literal>-1</literal> will set the size to a default of <literal>256 + kB</literal>. The maximum ring buffer size is <literal>16 GB</literal>. + Though you may set <varname>vacuum_buffer_usage_limit</varname> below + <literal>128 kB</literal>, it will be clamped to <literal>128 + kB</literal> at runtime. The default value is <literal>-1</literal>. + Without units, the size is taken as <literal>kB</literal>. This + parameter can be set at any time. + </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> diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index b6d30b5764..216c09c3d7 100644 --- a/doc/src/sgml/ref/vacuum.sgml +++ b/doc/src/sgml/ref/vacuum.sgml @@ -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,27 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet </listitem> </varlistentry> + <varlistentry> + <term><literal>BUFFER_USAGE_LIMIT</literal></term> + <listitem> + <para> + Specifies the ring buffer size for <command>VACUUM</command>. This size + is used to calculate a number of shared buffers which will be reused as + part of a <literal>Buffer Access Strategy</literal>. <literal>0</literal> + disables use of a <literal>Buffer Access Strategy</literal>. + <literal>-1</literal> indicates that <command>VACUUM</command> should + fall back to the value specified by <xref + linkend="guc-vacuum-buffer-usage-limit"/>. The maximum value is + <literal>16 GB</literal>. Though you may specify a size smaller than + <literal>128</literal>, the value will be clamped to <literal>128 + kB</literal> at runtime. Without units, the size is taken as + <literal>kB</literal>. This size applies to a single invocation of + <command>VACUUM</command>. The analyze stage and parallel vacuum workers + do not use this size. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><replaceable class="parameter">boolean</replaceable></term> <listitem> diff --git a/doc/src/sgml/ref/vacuumdb.sgml b/doc/src/sgml/ref/vacuumdb.sgml index 74bac2d4ba..5e8a6c167d 100644 --- a/doc/src/sgml/ref/vacuumdb.sgml +++ b/doc/src/sgml/ref/vacuumdb.sgml @@ -278,6 +278,26 @@ PostgreSQL documentation </listitem> </varlistentry> + <varlistentry> + <term><option>--buffer-usage-limit <replaceable class="parameter">buffer_usage_limit</replaceable></option></term> + <listitem> + <para> + The size of the ring buffer used for vacuuming. This size is used to + calculate a number of shared buffers which will be reused as part of a + <literal>Buffer Access Strategy</literal>. <literal>0</literal> + disables use of a <literal>Buffer Access Strategy</literal>. + <literal>-1</literal> indicates that the vacuum should fall back to the + value specified by <xref linkend="guc-vacuum-buffer-usage-limit"/>. The + maximum value is <literal>16 GB</literal>. Though you may specify a + size smaller than <literal>128 kB</literal>, the value will be clamped + to <literal>128 kB</literal> at runtime. Without units, the value is + taken as <literal>kB</literal>. This size applies to a single + invocation of <command>vacuumdb</command>. The analyze stage and + parallel vacuum workers do not use this size. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><option>-n <replaceable class="parameter">schema</replaceable></option></term> <term><option>--schema=<replaceable class="parameter">schema</replaceable></option></term> diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a6aac30529..f434f07dd1 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -128,6 +128,9 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel) /* By default parallel vacuum is enabled */ params.nworkers = 0; + /* by default use buffer access strategy with default size */ + params.ring_size = -1; + /* Parse options list */ foreach(lc, vacstmt->options) { @@ -211,6 +214,43 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel) skip_database_stats = defGetBoolean(opt); else if (strcmp(opt->defname, "only_database_stats") == 0) only_database_stats = defGetBoolean(opt); + else if (strcmp(opt->defname, "buffer_usage_limit") == 0) + { + char *vac_buffer_size; + int result; + const char *hintmsg; + + 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 for out-of-bounds */ + if (result < -1 || result > MAX_BAS_RING_SIZE_KB) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("buffer_usage_limit for a vacuum must be between -1 and %d", + MAX_BAS_RING_SIZE_KB))); + } + + params.ring_size = result; + + } else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), @@ -400,7 +440,16 @@ vacuum(List *relations, VacuumParams *params, { MemoryContext old_context = MemoryContextSwitchTo(vac_context); - bstrategy = GetAccessStrategy(BAS_VACUUM); + if (params->ring_size == -1) + { + if (vacuum_buffer_usage_limit == -1) + bstrategy = GetAccessStrategy(BAS_VACUUM); + else + bstrategy = GetAccessStrategyWithSize(BAS_VACUUM, vacuum_buffer_usage_limit); + } + else + bstrategy = GetAccessStrategyWithSize(BAS_VACUUM, params->ring_size); + MemoryContextSwitchTo(old_context); } diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c index bcd40c80a1..16742f6612 100644 --- a/src/backend/commands/vacuumparallel.c +++ b/src/backend/commands/vacuumparallel.c @@ -1012,7 +1012,11 @@ 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 */ + /* + * Each parallel VACUUM worker gets its own access strategy + * For now, use the default buffer access strategy ring size. + * TODO: should this work differently even though they are only for indexes + */ pvs.bstrategy = GetAccessStrategy(BAS_VACUUM); /* Setup error traceback support for ereport() */ diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index c0e2e00a7e..7dbd3b8935 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -2291,8 +2291,14 @@ 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. + * If we later enter failsafe mode, we will cease use of the + * BufferAccessStrategy. Either way, we clean up the BufferAccessStrategy + * object at the end of this function. */ - bstrategy = GetAccessStrategy(BAS_VACUUM); + if (vacuum_buffer_usage_limit == -1) + bstrategy = GetAccessStrategy(BAS_VACUUM); + else + bstrategy = GetAccessStrategyWithSize(BAS_VACUUM, vacuum_buffer_usage_limit); /* * create a memory context to act as fake PortalContext, so that the @@ -2881,6 +2887,13 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, tab->at_params.multixact_freeze_table_age = multixact_freeze_table_age; tab->at_params.is_wraparound = wraparound; tab->at_params.log_min_duration = log_min_duration; + + // TODO: should this be 0 so that we are sure that vacuum() never + // allocates a new bstrategy for us, even if we pass in NULL for that + // parameter? maybe could change how failsafe NULLs out bstrategy if + // so? + tab->at_params.ring_size = vacuum_buffer_usage_limit; + tab->at_vacuum_cost_limit = vac_cost_limit; tab->at_vacuum_cost_delay = vac_cost_delay; tab->at_relname = NULL; diff --git a/src/backend/storage/buffer/README b/src/backend/storage/buffer/README index a775276ff2..bf166bbdf1 100644 --- a/src/backend/storage/buffer/README +++ b/src/backend/storage/buffer/README @@ -245,6 +245,8 @@ for WAL flushes. While it's okay for a background vacuum to be slowed by doing its own WAL flushing, we'd prefer that COPY not be subject to that, so we let it use up a bit more of the buffer arena. +TODO: update with info about new option to control the size + Background Writer's Processing ------------------------------ diff --git a/src/backend/storage/buffer/freelist.c b/src/backend/storage/buffer/freelist.c index c690d5f15f..97ca3f344f 100644 --- a/src/backend/storage/buffer/freelist.c +++ b/src/backend/storage/buffer/freelist.c @@ -586,6 +586,54 @@ GetAccessStrategy(BufferAccessStrategyType btype) return strategy; } +static inline int +bufsize_limit_to_nbuffers(int bufsize_limit_kb) +{ + int blcksz_kb = BLCKSZ / 1024; + + Assert(blcksz_kb > 0); + + return bufsize_limit_kb / blcksz_kb; +} + +BufferAccessStrategy +GetAccessStrategyWithSize(BufferAccessStrategyType btype, int ring_size) +{ + BufferAccessStrategy strategy; + int nbuffers; + + /* Default nbuffers should have resulted in calling GetAccessStrategy() */ + // TODO: this being called ring_size and also nbuffers being called + // ring_size in GetAccessStrategy is confusing. Rename the member of + // BufferAccessStrategy + Assert(ring_size != -1); + + if (ring_size == 0) + return NULL; + + Assert(ring_size <= MAX_BAS_RING_SIZE_KB); + + // TODO: warn about clamping? + if (ring_size < MIN_BAS_RING_SIZE_KB) + nbuffers = bufsize_limit_to_nbuffers(MIN_BAS_RING_SIZE_KB); + else + nbuffers = bufsize_limit_to_nbuffers(ring_size); + + // TODO: make this smaller? + nbuffers = Min(NBuffers / 8, nbuffers); + + /* Allocate the object and initialize all elements to zeroes */ + strategy = (BufferAccessStrategy) + palloc0(offsetof(BufferAccessStrategyData, buffers) + + nbuffers * sizeof(Buffer)); + + /* Set fields that don't start out zero */ + strategy->btype = btype; + strategy->ring_size = nbuffers; + + return strategy; +} + /* * FreeAccessStrategy -- release a BufferAccessStrategy object * diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c index 1b1d814254..6eca3371bd 100644 --- a/src/backend/utils/init/globals.c +++ b/src/backend/utils/init/globals.c @@ -139,6 +139,8 @@ int max_worker_processes = 8; int max_parallel_workers = 8; int MaxBackends = 0; +int vacuum_buffer_usage_limit = -1; + int VacuumCostPageHit = 1; /* GUC parameters for vacuum */ int VacuumCostPageMiss = 2; int VacuumCostPageDirty = 20; diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 1c0583fe26..883ee29d14 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -2206,6 +2206,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 and autovacuum."), + NULL, + GUC_UNIT_KB + }, + &vacuum_buffer_usage_limit, + -1, -1, MAX_BAS_RING_SIZE_KB, + NULL, 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)."), diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index d06074b86f..36273aa47d 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -156,6 +156,10 @@ # mmap # (change requires restart) #min_dynamic_shared_memory = 0MB # (change requires restart) +#vacuum_buffer_usage_limit = -1 # size of vacuum buffer access strategy ring. + # -1 to use default, + # 0 to disable vacuum buffer access strategy and use shared buffers + # > 0 to specify size # - Disk - diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 42e87b9e49..6fd80dd3c3 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -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")) diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c index 39be265b5b..5b2a5d2858 100644 --- a/src/bin/scripts/vacuumdb.c +++ b/src/bin/scripts/vacuumdb.c @@ -46,6 +46,7 @@ typedef struct vacuumingOptions bool process_main; bool process_toast; bool skip_database_stats; + char *buffer_usage_limit; } vacuumingOptions; /* object filter options */ @@ -123,6 +124,7 @@ main(int argc, char *argv[]) {"no-truncate", no_argument, NULL, 10}, {"no-process-toast", no_argument, NULL, 11}, {"no-process-main", no_argument, NULL, 12}, + {"buffer-usage-limit", required_argument, NULL, 13}, {NULL, 0, NULL, 0} }; @@ -147,6 +149,7 @@ main(int argc, char *argv[]) /* initialize options */ memset(&vacopts, 0, sizeof(vacopts)); vacopts.parallel_workers = -1; + vacopts.buffer_usage_limit = NULL; vacopts.no_index_cleanup = false; vacopts.force_index_cleanup = false; vacopts.do_truncate = true; @@ -266,6 +269,9 @@ main(int argc, char *argv[]) case 12: vacopts.process_main = false; break; + case 13: + vacopts.buffer_usage_limit = pg_strdup(optarg); + break; default: /* getopt_long already emitted a complaint */ pg_log_error_hint("Try \"%s --help\" for more information.", progname); @@ -324,6 +330,9 @@ main(int argc, char *argv[]) if (!vacopts.process_toast) pg_fatal("cannot use the \"%s\" option when performing only analyze", "no-process-toast"); + if (vacopts.buffer_usage_limit) + pg_fatal("cannot use the \"%s\" option when performing only analyze", + "buffer-usage-limit"); /* allow 'and_analyze' with 'analyze_only' */ } @@ -550,6 +559,10 @@ vacuum_one_database(ConnParams *cparams, pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s", "--parallel", "13"); + if (vacopts->buffer_usage_limit && PQserverVersion(conn) < 160000) + pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s", + "--buffer-usage-limit", "16"); + /* skip_database_stats is used automatically if server supports it */ vacopts->skip_database_stats = (PQserverVersion(conn) >= 160000); @@ -1048,6 +1061,13 @@ prepare_vacuum_command(PQExpBuffer sql, int serverVersion, vacopts->parallel_workers); sep = comma; } + if (vacopts->buffer_usage_limit) + { + Assert(serverVersion >= 160000); + appendPQExpBuffer(sql, "%sBUFFER_USAGE_LIMIT '%s'", sep, + vacopts->buffer_usage_limit); + sep = comma; + } if (sep != paren) appendPQExpBufferChar(sql, ')'); } @@ -1111,6 +1131,7 @@ help(const char *progname) printf(_(" --force-index-cleanup always remove index entries that point to dead tuples\n")); printf(_(" -j, --jobs=NUM use this many concurrent connections to vacuum\n")); printf(_(" --min-mxid-age=MXID_AGE minimum multixact ID age of tables to vacuum\n")); + printf(_(" --buffer-usage-limit=BUFSIZE size of ring buffer used for vacuum\n")); printf(_(" --min-xid-age=XID_AGE minimum transaction ID age of tables to vacuum\n")); printf(_(" --no-index-cleanup don't remove index entries that point to dead tuples\n")); printf(_(" --no-process-main skip the main relation\n")); diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index bdfd96cfec..d8fba51d82 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -213,6 +213,9 @@ typedef enum VacOptValue * * Note that at least one of VACOPT_VACUUM and VACOPT_ANALYZE must be set * in options. + * + * When adding a new VacuumParam member, consider adding it to vacuumdb as + * well. */ typedef struct VacuumParams { @@ -236,6 +239,7 @@ typedef struct VacuumParams * disabled. */ int nworkers; + int ring_size; } VacuumParams; /* diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 06a86f9ac1..b572dfcc6c 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -262,6 +262,7 @@ extern PGDLLIMPORT int work_mem; extern PGDLLIMPORT double hash_mem_multiplier; extern PGDLLIMPORT int maintenance_work_mem; extern PGDLLIMPORT int max_parallel_maintenance_workers; +extern PGDLLIMPORT int vacuum_buffer_usage_limit; extern PGDLLIMPORT int VacuumCostPageHit; extern PGDLLIMPORT int VacuumCostPageMiss; diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h index b8a18b8081..338de38568 100644 --- a/src/include/storage/bufmgr.h +++ b/src/include/storage/bufmgr.h @@ -101,6 +101,9 @@ extern PGDLLIMPORT int32 *LocalRefCount; /* upper limit for effective_io_concurrency */ #define MAX_IO_CONCURRENCY 1000 +#define MAX_BAS_RING_SIZE_KB (16 * 1024 * 1024) +#define MIN_BAS_RING_SIZE_KB 128 + /* special block number for ReadBuffer() */ #define P_NEW InvalidBlockNumber /* grow the file to get a new page */ @@ -196,6 +199,8 @@ extern void AtProcExit_LocalBuffers(void); /* in freelist.c */ extern BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype); + +extern BufferAccessStrategy GetAccessStrategyWithSize(BufferAccessStrategyType btype, int nbuffers); extern void FreeAccessStrategy(BufferAccessStrategy strategy); -- 2.37.2