Skip to content

Commit b9b125b

Browse files
committed
Move various prechecks from vacuum() into ExecVacuum()
vacuum() is used for both the VACUUM command and for autovacuum. There were many prechecks being done inside vacuum() that were just not relevant to autovacuum. Let's move the bulk of these into ExecVacuum() so that they're only executed when running the VACUUM command. This removes a small amount of overhead when autovacuum vacuums a table. While we are at it, allocate VACUUM's BufferAccessStrategy in ExecVacuum() and pass it into vacuum() instead of expecting vacuum() to make it if it's not already made by the calling function. To make this work, we need to create the vacuum memory context slightly earlier, so we now need to pass that down to vacuum() so that it's available for use in other memory allocations. Author: Melanie Plageman Reviewed-by: David Rowley Discussion: https://postgr.es/m/20230405211534.4skgskbilnxqrmxg@awork3.anarazel.de
1 parent acab1b0 commit b9b125b

File tree

3 files changed

+91
-78
lines changed

3 files changed

+91
-78
lines changed

src/backend/commands/vacuum.c

Lines changed: 78 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ void
105105
ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
106106
{
107107
VacuumParams params;
108+
BufferAccessStrategy bstrategy = NULL;
108109
bool verbose = false;
109110
bool skip_locked = false;
110111
bool analyze = false;
@@ -115,6 +116,7 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
115116
bool process_toast = true;
116117
bool skip_database_stats = false;
117118
bool only_database_stats = false;
119+
MemoryContext vac_context;
118120
ListCell *lc;
119121

120122
/* index_cleanup and truncate values unspecified for now */
@@ -254,6 +256,42 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
254256
}
255257
}
256258

259+
260+
/*
261+
* Sanity check DISABLE_PAGE_SKIPPING option.
262+
*/
263+
if ((params.options & VACOPT_FULL) != 0 &&
264+
(params.options & VACOPT_DISABLE_PAGE_SKIPPING) != 0)
265+
ereport(ERROR,
266+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
267+
errmsg("VACUUM option DISABLE_PAGE_SKIPPING cannot be used with FULL")));
268+
269+
/* sanity check for PROCESS_TOAST */
270+
if ((params.options & VACOPT_FULL) != 0 &&
271+
(params.options & VACOPT_PROCESS_TOAST) == 0)
272+
ereport(ERROR,
273+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
274+
errmsg("PROCESS_TOAST required with VACUUM FULL")));
275+
276+
/* sanity check for ONLY_DATABASE_STATS */
277+
if (params.options & VACOPT_ONLY_DATABASE_STATS)
278+
{
279+
Assert(params.options & VACOPT_VACUUM);
280+
if (vacstmt->rels != NIL)
281+
ereport(ERROR,
282+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
283+
errmsg("ONLY_DATABASE_STATS cannot be specified with a list of tables")));
284+
/* don't require people to turn off PROCESS_TOAST/MAIN explicitly */
285+
if (params.options & ~(VACOPT_VACUUM |
286+
VACOPT_VERBOSE |
287+
VACOPT_PROCESS_MAIN |
288+
VACOPT_PROCESS_TOAST |
289+
VACOPT_ONLY_DATABASE_STATS))
290+
ereport(ERROR,
291+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
292+
errmsg("ONLY_DATABASE_STATS cannot be specified with other VACUUM options")));
293+
}
294+
257295
/*
258296
* All freeze ages are zero if the FREEZE option is given; otherwise pass
259297
* them as -1 which means to use the default values.
@@ -279,12 +317,43 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
279317
/* user-invoked vacuum uses VACOPT_VERBOSE instead of log_min_duration */
280318
params.log_min_duration = -1;
281319

320+
/*
321+
* Create special memory context for cross-transaction storage.
322+
*
323+
* Since it is a child of PortalContext, it will go away eventually even
324+
* if we suffer an error; there's no need for special abort cleanup logic.
325+
*/
326+
vac_context = AllocSetContextCreate(PortalContext,
327+
"Vacuum",
328+
ALLOCSET_DEFAULT_SIZES);
329+
330+
/*
331+
* Make a buffer strategy object in the cross-transaction memory context.
332+
* We needn't bother making this for VACUUM (FULL) or VACUUM
333+
* (ONLY_DATABASE_STATS) as they'll not make use of it. VACUUM (FULL,
334+
* ANALYZE) is possible, so we'd better ensure that we make a strategy
335+
* when we see ANALYZE.
336+
*/
337+
if ((params.options & (VACOPT_ONLY_DATABASE_STATS |
338+
VACOPT_FULL)) == 0 ||
339+
(params.options & VACOPT_ANALYZE) != 0)
340+
{
341+
342+
MemoryContext old_context = MemoryContextSwitchTo(vac_context);
343+
344+
bstrategy = GetAccessStrategy(BAS_VACUUM);
345+
MemoryContextSwitchTo(old_context);
346+
}
347+
282348
/* Now go through the common routine */
283-
vacuum(vacstmt->rels, &params, NULL, isTopLevel);
349+
vacuum(vacstmt->rels, &params, bstrategy, vac_context, isTopLevel);
350+
351+
/* Finally, clean up the vacuum memory context */
352+
MemoryContextDelete(vac_context);
284353
}
285354

286355
/*
287-
* Internal entry point for VACUUM and ANALYZE commands.
356+
* Internal entry point for autovacuum and the VACUUM / ANALYZE commands.
288357
*
289358
* relations, if not NIL, is a list of VacuumRelation to process; otherwise,
290359
* we process all relevant tables in the database. For each VacuumRelation,
@@ -294,21 +363,23 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
294363
* params contains a set of parameters that can be used to customize the
295364
* behavior.
296365
*
297-
* bstrategy is normally given as NULL, but in autovacuum it can be passed
298-
* in to use the same buffer strategy object across multiple vacuum() calls.
366+
* bstrategy may be passed in as NULL when the caller does not want to
367+
* restrict the number of shared_buffers that VACUUM / ANALYZE can use,
368+
* otherwise, the caller must build a BufferAccessStrategy with the number of
369+
* shared_buffers that VACUUM / ANALYZE should try to limit themselves to
370+
* using.
299371
*
300372
* isTopLevel should be passed down from ProcessUtility.
301373
*
302374
* It is the caller's responsibility that all parameters are allocated in a
303375
* memory context that will not disappear at transaction commit.
304376
*/
305377
void
306-
vacuum(List *relations, VacuumParams *params,
307-
BufferAccessStrategy bstrategy, bool isTopLevel)
378+
vacuum(List *relations, VacuumParams *params, BufferAccessStrategy bstrategy,
379+
MemoryContext vac_context, bool isTopLevel)
308380
{
309381
static bool in_vacuum = false;
310382

311-
MemoryContext vac_context;
312383
const char *stmttype;
313384
volatile bool in_outer_xact,
314385
use_own_xacts;
@@ -344,69 +415,6 @@ vacuum(List *relations, VacuumParams *params,
344415
errmsg("%s cannot be executed from VACUUM or ANALYZE",
345416
stmttype)));
346417

347-
/*
348-
* Sanity check DISABLE_PAGE_SKIPPING option.
349-
*/
350-
if ((params->options & VACOPT_FULL) != 0 &&
351-
(params->options & VACOPT_DISABLE_PAGE_SKIPPING) != 0)
352-
ereport(ERROR,
353-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
354-
errmsg("VACUUM option DISABLE_PAGE_SKIPPING cannot be used with FULL")));
355-
356-
/* sanity check for PROCESS_TOAST */
357-
if ((params->options & VACOPT_FULL) != 0 &&
358-
(params->options & VACOPT_PROCESS_TOAST) == 0)
359-
ereport(ERROR,
360-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
361-
errmsg("PROCESS_TOAST required with VACUUM FULL")));
362-
363-
/* sanity check for ONLY_DATABASE_STATS */
364-
if (params->options & VACOPT_ONLY_DATABASE_STATS)
365-
{
366-
Assert(params->options & VACOPT_VACUUM);
367-
if (relations != NIL)
368-
ereport(ERROR,
369-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
370-
errmsg("ONLY_DATABASE_STATS cannot be specified with a list of tables")));
371-
/* don't require people to turn off PROCESS_TOAST/MAIN explicitly */
372-
if (params->options & ~(VACOPT_VACUUM |
373-
VACOPT_VERBOSE |
374-
VACOPT_PROCESS_MAIN |
375-
VACOPT_PROCESS_TOAST |
376-
VACOPT_ONLY_DATABASE_STATS))
377-
ereport(ERROR,
378-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
379-
errmsg("ONLY_DATABASE_STATS cannot be specified with other VACUUM options")));
380-
}
381-
382-
/*
383-
* Create special memory context for cross-transaction storage.
384-
*
385-
* Since it is a child of PortalContext, it will go away eventually even
386-
* if we suffer an error; there's no need for special abort cleanup logic.
387-
*/
388-
vac_context = AllocSetContextCreate(PortalContext,
389-
"Vacuum",
390-
ALLOCSET_DEFAULT_SIZES);
391-
392-
/*
393-
* If caller didn't give us a buffer strategy object, make one in the
394-
* cross-transaction memory context. We needn't bother making this for
395-
* VACUUM (FULL) or VACUUM (ONLY_DATABASE_STATS) as they'll not make use
396-
* of it. VACUUM (FULL, ANALYZE) is possible, so we'd better ensure that
397-
* we make a strategy when we see ANALYZE.
398-
*/
399-
if (bstrategy == NULL &&
400-
((params->options & (VACOPT_ONLY_DATABASE_STATS |
401-
VACOPT_FULL)) == 0 ||
402-
(params->options & VACOPT_ANALYZE) != 0))
403-
{
404-
MemoryContext old_context = MemoryContextSwitchTo(vac_context);
405-
406-
bstrategy = GetAccessStrategy(BAS_VACUUM);
407-
MemoryContextSwitchTo(old_context);
408-
}
409-
410418
/*
411419
* Build list of relation(s) to process, putting any new data in
412420
* vac_context for safekeeping.
@@ -578,12 +586,6 @@ vacuum(List *relations, VacuumParams *params,
578586
vac_update_datfrozenxid();
579587
}
580588

581-
/*
582-
* Clean up working storage --- note we must do this after
583-
* StartTransactionCommand, else we might be trying to delete the active
584-
* context!
585-
*/
586-
MemoryContextDelete(vac_context);
587589
}
588590

589591
/*

src/backend/postmaster/autovacuum.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3140,13 +3140,17 @@ relation_needs_vacanalyze(Oid relid,
31403140
/*
31413141
* autovacuum_do_vac_analyze
31423142
* Vacuum and/or analyze the specified table
3143+
*
3144+
* We expect the caller to have switched into a memory context that won't
3145+
* disappear at transaction commit.
31433146
*/
31443147
static void
31453148
autovacuum_do_vac_analyze(autovac_table *tab, BufferAccessStrategy bstrategy)
31463149
{
31473150
RangeVar *rangevar;
31483151
VacuumRelation *rel;
31493152
List *rel_list;
3153+
MemoryContext vac_context;
31503154

31513155
/* Let pgstat know what we're doing */
31523156
autovac_report_activity(tab);
@@ -3156,7 +3160,13 @@ autovacuum_do_vac_analyze(autovac_table *tab, BufferAccessStrategy bstrategy)
31563160
rel = makeVacuumRelation(rangevar, tab->at_relid, NIL);
31573161
rel_list = list_make1(rel);
31583162

3159-
vacuum(rel_list, &tab->at_params, bstrategy, true);
3163+
vac_context = AllocSetContextCreate(CurrentMemoryContext,
3164+
"Vacuum",
3165+
ALLOCSET_DEFAULT_SIZES);
3166+
3167+
vacuum(rel_list, &tab->at_params, bstrategy, vac_context, true);
3168+
3169+
MemoryContextDelete(vac_context);
31603170
}
31613171

31623172
/*

src/include/commands/vacuum.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,8 @@ extern PGDLLIMPORT int VacuumCostBalanceLocal;
310310
/* in commands/vacuum.c */
311311
extern void ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel);
312312
extern void vacuum(List *relations, VacuumParams *params,
313-
BufferAccessStrategy bstrategy, bool isTopLevel);
313+
BufferAccessStrategy bstrategy, MemoryContext vac_context,
314+
bool isTopLevel);
314315
extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
315316
int *nindexes, Relation **Irel);
316317
extern void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode);

0 commit comments

Comments
 (0)