Skip to content

Commit cd3064f

Browse files
committed
Fix per-relation memory leakage in autovacuum.
PgStat_StatTabEntry and AutoVacOpts structs were leaked until the end of the autovacuum worker's run, which is bad news if there are a lot of relations in the database. Note: pfree'ing the PgStat_StatTabEntry structs here seems a bit risky, because pgstat_fetch_stat_tabentry_ext does not guarantee anything about whether its result is long-lived. It appears okay so long as autovacuum forces PGSTAT_FETCH_CONSISTENCY_NONE, but I think that API could use a re-think. Also ensure that the VacuumRelation structure passed to vacuum() is in recoverable storage. Back-patch to v15 where we started to manage table statistics this way. (The AutoVacOpts leakage is probably older, but I'm not excited enough to worry about just that part.) Author: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Andres Freund <andres@anarazel.de> Discussion: https://postgr.es/m/285483.1746756246@sss.pgh.pa.us Backpatch-through: 15
1 parent ac3afd1 commit cd3064f

File tree

1 file changed

+40
-10
lines changed

1 file changed

+40
-10
lines changed

src/backend/postmaster/autovacuum.c

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,6 +2059,12 @@ do_autovacuum(void)
20592059
}
20602060
}
20612061
}
2062+
2063+
/* Release stuff to avoid per-relation leakage */
2064+
if (relopts)
2065+
pfree(relopts);
2066+
if (tabentry)
2067+
pfree(tabentry);
20622068
}
20632069

20642070
table_endscan(relScan);
@@ -2075,7 +2081,8 @@ do_autovacuum(void)
20752081
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
20762082
PgStat_StatTabEntry *tabentry;
20772083
Oid relid;
2078-
AutoVacOpts *relopts = NULL;
2084+
AutoVacOpts *relopts;
2085+
bool free_relopts = false;
20792086
bool dovacuum;
20802087
bool doanalyze;
20812088
bool wraparound;
@@ -2093,7 +2100,9 @@ do_autovacuum(void)
20932100
* main rel
20942101
*/
20952102
relopts = extract_autovac_opts(tuple, pg_class_desc);
2096-
if (relopts == NULL)
2103+
if (relopts)
2104+
free_relopts = true;
2105+
else
20972106
{
20982107
av_relation *hentry;
20992108
bool found;
@@ -2114,6 +2123,12 @@ do_autovacuum(void)
21142123
/* ignore analyze for toast tables */
21152124
if (dovacuum)
21162125
table_oids = lappend_oid(table_oids, relid);
2126+
2127+
/* Release stuff to avoid leakage */
2128+
if (free_relopts)
2129+
pfree(relopts);
2130+
if (tabentry)
2131+
pfree(tabentry);
21172132
}
21182133

21192134
table_endscan(relScan);
@@ -2485,6 +2500,8 @@ do_autovacuum(void)
24852500
pg_atomic_test_set_flag(&MyWorkerInfo->wi_dobalance);
24862501
}
24872502

2503+
list_free(table_oids);
2504+
24882505
/*
24892506
* Perform additional work items, as requested by backends.
24902507
*/
@@ -2666,8 +2683,8 @@ perform_work_item(AutoVacuumWorkItem *workitem)
26662683
/*
26672684
* extract_autovac_opts
26682685
*
2669-
* Given a relation's pg_class tuple, return the AutoVacOpts portion of
2670-
* reloptions, if set; otherwise, return NULL.
2686+
* Given a relation's pg_class tuple, return a palloc'd copy of the
2687+
* AutoVacOpts portion of reloptions, if set; otherwise, return NULL.
26712688
*
26722689
* Note: callers do not have a relation lock on the table at this point,
26732690
* so the table could have been dropped, and its catalog rows gone, after
@@ -2716,6 +2733,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
27162733
autovac_table *tab = NULL;
27172734
bool wraparound;
27182735
AutoVacOpts *avopts;
2736+
bool free_avopts = false;
27192737

27202738
/* fetch the relation's relcache entry */
27212739
classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
@@ -2728,8 +2746,10 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
27282746
* main table reloptions if the toast table itself doesn't have.
27292747
*/
27302748
avopts = extract_autovac_opts(classTup, pg_class_desc);
2731-
if (classForm->relkind == RELKIND_TOASTVALUE &&
2732-
avopts == NULL && table_toast_map != NULL)
2749+
if (avopts)
2750+
free_avopts = true;
2751+
else if (classForm->relkind == RELKIND_TOASTVALUE &&
2752+
table_toast_map != NULL)
27332753
{
27342754
av_relation *hentry;
27352755
bool found;
@@ -2832,6 +2852,8 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
28322852
avopts->vacuum_cost_delay >= 0));
28332853
}
28342854

2855+
if (free_avopts)
2856+
pfree(avopts);
28352857
heap_freetuple(classTup);
28362858
return tab;
28372859
}
@@ -2863,6 +2885,10 @@ recheck_relation_needs_vacanalyze(Oid relid,
28632885
effective_multixact_freeze_max_age,
28642886
dovacuum, doanalyze, wraparound);
28652887

2888+
/* Release tabentry to avoid leakage */
2889+
if (tabentry)
2890+
pfree(tabentry);
2891+
28662892
/* ignore ANALYZE for toast tables */
28672893
if (classForm->relkind == RELKIND_TOASTVALUE)
28682894
*doanalyze = false;
@@ -3088,18 +3114,22 @@ autovacuum_do_vac_analyze(autovac_table *tab, BufferAccessStrategy bstrategy)
30883114
VacuumRelation *rel;
30893115
List *rel_list;
30903116
MemoryContext vac_context;
3117+
MemoryContext old_context;
30913118

30923119
/* Let pgstat know what we're doing */
30933120
autovac_report_activity(tab);
30943121

3122+
/* Create a context that vacuum() can use as cross-transaction storage */
3123+
vac_context = AllocSetContextCreate(CurrentMemoryContext,
3124+
"Vacuum",
3125+
ALLOCSET_DEFAULT_SIZES);
3126+
30953127
/* Set up one VacuumRelation target, identified by OID, for vacuum() */
3128+
old_context = MemoryContextSwitchTo(vac_context);
30963129
rangevar = makeRangeVar(tab->at_nspname, tab->at_relname, -1);
30973130
rel = makeVacuumRelation(rangevar, tab->at_relid, NIL);
30983131
rel_list = list_make1(rel);
3099-
3100-
vac_context = AllocSetContextCreate(CurrentMemoryContext,
3101-
"Vacuum",
3102-
ALLOCSET_DEFAULT_SIZES);
3132+
MemoryContextSwitchTo(old_context);
31033133

31043134
vacuum(rel_list, &tab->at_params, bstrategy, vac_context, true);
31053135

0 commit comments

Comments
 (0)