mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Update autovacuum to use reloptions instead of a system catalog, for
per-table overrides of parameters. This removes a whole class of problems related to misusing the catalog, and perhaps more importantly, gives us pg_dump support for the parameters. Based on a patch by Euler Taveira de Oliveira, heavily reworked by me.
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.20 2009/02/02 19:31:38 alvherre Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.21 2009/02/09 20:57:59 alvherre Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -48,6 +48,14 @@
|
||||
|
||||
static relopt_bool boolRelOpts[] =
|
||||
{
|
||||
{
|
||||
{
|
||||
"autovacuum_enabled",
|
||||
"Enables autovacuum in this relation",
|
||||
RELOPT_KIND_HEAP
|
||||
},
|
||||
true
|
||||
},
|
||||
/* list terminator */
|
||||
{ { NULL } }
|
||||
};
|
||||
@ -86,12 +94,83 @@ static relopt_int intRelOpts[] =
|
||||
},
|
||||
GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
|
||||
},
|
||||
{
|
||||
{
|
||||
"autovacuum_vacuum_threshold",
|
||||
"Minimum number of tuple updates or deletes prior to vacuum",
|
||||
RELOPT_KIND_HEAP
|
||||
},
|
||||
50, 0, INT_MAX
|
||||
},
|
||||
{
|
||||
{
|
||||
"autovacuum_analyze_threshold",
|
||||
"Minimum number of tuple inserts, updates or deletes prior to analyze",
|
||||
RELOPT_KIND_HEAP
|
||||
},
|
||||
50, 0, INT_MAX
|
||||
},
|
||||
{
|
||||
{
|
||||
"autovacuum_vacuum_cost_delay",
|
||||
"Vacuum cost delay in milliseconds, for autovacuum",
|
||||
RELOPT_KIND_HEAP
|
||||
},
|
||||
20, 0, 1000
|
||||
},
|
||||
{
|
||||
{
|
||||
"autovacuum_vacuum_cost_limit",
|
||||
"Vacuum cost amount available before napping, for autovacuum",
|
||||
RELOPT_KIND_HEAP
|
||||
},
|
||||
200, 1, 10000
|
||||
},
|
||||
{
|
||||
{
|
||||
"autovacuum_freeze_min_age",
|
||||
"Minimum age at which VACUUM should freeze a table row, for autovacuum",
|
||||
RELOPT_KIND_HEAP
|
||||
},
|
||||
100000000, 0, 1000000000
|
||||
},
|
||||
{
|
||||
{
|
||||
"autovacuum_freeze_max_age",
|
||||
"Age at which to autovacuum a table to prevent transaction ID wraparound",
|
||||
RELOPT_KIND_HEAP
|
||||
},
|
||||
200000000, 100000000, 2000000000
|
||||
},
|
||||
{
|
||||
{
|
||||
"autovacuum_freeze_table_age",
|
||||
"Age at which VACUUM should perform a full table sweep to replace old Xid values with FrozenXID",
|
||||
RELOPT_KIND_HEAP
|
||||
}, 150000000, 0, 2000000000
|
||||
},
|
||||
/* list terminator */
|
||||
{ { NULL } }
|
||||
};
|
||||
|
||||
static relopt_real realRelOpts[] =
|
||||
{
|
||||
{
|
||||
{
|
||||
"autovacuum_vacuum_scale_factor",
|
||||
"Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
|
||||
RELOPT_KIND_HEAP
|
||||
},
|
||||
0.2, 0.0, 100.0
|
||||
},
|
||||
{
|
||||
{
|
||||
"autovacuum_analyze_scale_factor",
|
||||
"Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
|
||||
RELOPT_KIND_HEAP
|
||||
},
|
||||
0.1, 0.0, 100.0
|
||||
},
|
||||
/* list terminator */
|
||||
{ { NULL } }
|
||||
};
|
||||
@ -973,7 +1052,8 @@ fillRelOptions(void *rdopts, Size basesize, relopt_value *options,
|
||||
|
||||
|
||||
/*
|
||||
* Option parser for anything that uses StdRdOptions (i.e. fillfactor only)
|
||||
* Option parser for anything that uses StdRdOptions (i.e. fillfactor and
|
||||
* autovacuum)
|
||||
*/
|
||||
bytea *
|
||||
default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
|
||||
@ -982,7 +1062,27 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
|
||||
StdRdOptions *rdopts;
|
||||
int numoptions;
|
||||
relopt_parse_elt tab[] = {
|
||||
{"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)}
|
||||
{"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
|
||||
{"autovacuum_enabled", RELOPT_TYPE_BOOL,
|
||||
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
|
||||
{"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
|
||||
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)},
|
||||
{"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
|
||||
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
|
||||
{"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT,
|
||||
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)},
|
||||
{"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
|
||||
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
|
||||
{"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
|
||||
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
|
||||
{"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
|
||||
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
|
||||
{"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
|
||||
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
|
||||
{"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
|
||||
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
|
||||
{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
|
||||
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)}
|
||||
};
|
||||
|
||||
options = parseRelOptions(reloptions, validate, kind, &numoptions);
|
||||
|
@ -2,7 +2,7 @@
|
||||
#
|
||||
# Makefile for backend/catalog
|
||||
#
|
||||
# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.68 2008/12/19 16:25:16 petere Exp $
|
||||
# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.69 2009/02/09 20:57:59 alvherre Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@ -26,7 +26,7 @@ all: $(BKIFILES)
|
||||
# indexing.h had better be last, and toasting.h just before it.
|
||||
|
||||
POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
|
||||
pg_proc.h pg_type.h pg_attribute.h pg_class.h pg_autovacuum.h \
|
||||
pg_proc.h pg_type.h pg_attribute.h pg_class.h \
|
||||
pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
|
||||
pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
|
||||
pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \
|
||||
|
@ -55,7 +55,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.92 2009/01/16 13:27:24 heikki Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.93 2009/02/09 20:57:59 alvherre Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -69,12 +69,12 @@
|
||||
|
||||
#include "access/genam.h"
|
||||
#include "access/heapam.h"
|
||||
#include "access/reloptions.h"
|
||||
#include "access/transam.h"
|
||||
#include "access/xact.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/indexing.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_autovacuum.h"
|
||||
#include "catalog/pg_database.h"
|
||||
#include "commands/dbcommands.h"
|
||||
#include "commands/vacuum.h"
|
||||
@ -165,13 +165,15 @@ typedef struct av_relation
|
||||
{
|
||||
Oid ar_toastrelid; /* hash key - must be first */
|
||||
Oid ar_relid;
|
||||
bool ar_hasrelopts;
|
||||
AutoVacOpts ar_reloptions; /* copy of AutoVacOpts from the main table's
|
||||
reloptions, or NULL if none */
|
||||
} av_relation;
|
||||
|
||||
/* struct to keep track of tables to vacuum and/or analyze, after rechecking */
|
||||
typedef struct autovac_table
|
||||
{
|
||||
Oid at_relid;
|
||||
Oid at_toastrelid;
|
||||
bool at_dovacuum;
|
||||
bool at_doanalyze;
|
||||
int at_freeze_min_age;
|
||||
@ -282,16 +284,17 @@ static void autovac_balance_cost(void);
|
||||
static void do_autovacuum(void);
|
||||
static void FreeWorkerInfo(int code, Datum arg);
|
||||
|
||||
static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map);
|
||||
static void relation_needs_vacanalyze(Oid relid, Form_pg_autovacuum avForm,
|
||||
static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map,
|
||||
TupleDesc pg_class_desc);
|
||||
static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
|
||||
Form_pg_class classForm,
|
||||
PgStat_StatTabEntry *tabentry, bool *dovacuum,
|
||||
bool *doanalyze, bool *wraparound);
|
||||
PgStat_StatTabEntry *tabentry,
|
||||
bool *dovacuum, bool *doanalyze, bool *wraparound);
|
||||
|
||||
static void autovacuum_do_vac_analyze(autovac_table *tab,
|
||||
BufferAccessStrategy bstrategy);
|
||||
static HeapTuple get_pg_autovacuum_tuple_relid(Relation avRel, Oid relid,
|
||||
HTAB *table_toast_map);
|
||||
static AutoVacOpts *extract_autovac_opts(HeapTuple tup,
|
||||
TupleDesc pg_class_desc);
|
||||
static PgStat_StatTabEntry *get_pgstat_tabentry_relid(Oid relid, bool isshared,
|
||||
PgStat_StatDBEntry *shared,
|
||||
PgStat_StatDBEntry *dbentry);
|
||||
@ -1816,8 +1819,7 @@ get_database_list(void)
|
||||
static void
|
||||
do_autovacuum(void)
|
||||
{
|
||||
Relation classRel,
|
||||
avRel;
|
||||
Relation classRel;
|
||||
HeapTuple tuple;
|
||||
HeapScanDesc relScan;
|
||||
Form_pg_database dbForm;
|
||||
@ -1829,6 +1831,7 @@ do_autovacuum(void)
|
||||
PgStat_StatDBEntry *dbentry;
|
||||
BufferAccessStrategy bstrategy;
|
||||
ScanKeyData key;
|
||||
TupleDesc pg_class_desc;
|
||||
|
||||
/*
|
||||
* StartTransactionCommand and CommitTransactionCommand will automatically
|
||||
@ -1890,12 +1893,14 @@ do_autovacuum(void)
|
||||
shared = pgstat_fetch_stat_dbentry(InvalidOid);
|
||||
|
||||
classRel = heap_open(RelationRelationId, AccessShareLock);
|
||||
avRel = heap_open(AutovacuumRelationId, AccessShareLock);
|
||||
|
||||
/* create a copy so we can use it after closing pg_class */
|
||||
pg_class_desc = CreateTupleDescCopy(RelationGetDescr(classRel));
|
||||
|
||||
/* create hash table for toast <-> main relid mapping */
|
||||
MemSet(&ctl, 0, sizeof(ctl));
|
||||
ctl.keysize = sizeof(Oid);
|
||||
ctl.entrysize = sizeof(Oid) * 2;
|
||||
ctl.entrysize = sizeof(av_relation);
|
||||
ctl.hash = oid_hash;
|
||||
|
||||
table_toast_map = hash_create("TOAST to main relid map",
|
||||
@ -1909,9 +1914,9 @@ do_autovacuum(void)
|
||||
* We do this in two passes: on the first one we collect the list of
|
||||
* plain relations, and on the second one we collect TOAST tables.
|
||||
* The reason for doing the second pass is that during it we want to use
|
||||
* the main relation's pg_autovacuum entry if the TOAST table does not have
|
||||
* any, and we cannot obtain it unless we know beforehand what's the main
|
||||
* table OID.
|
||||
* the main relation's pg_class.reloptions entry if the TOAST table does
|
||||
* not have any, and we cannot obtain it unless we know beforehand what's
|
||||
* the main table OID.
|
||||
*
|
||||
* We need to check TOAST tables separately because in cases with short,
|
||||
* wide tables there might be proportionally much more activity in the
|
||||
@ -1931,9 +1936,8 @@ do_autovacuum(void)
|
||||
while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
|
||||
{
|
||||
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
|
||||
Form_pg_autovacuum avForm = NULL;
|
||||
PgStat_StatTabEntry *tabentry;
|
||||
HeapTuple avTup;
|
||||
AutoVacOpts *relopts;
|
||||
Oid relid;
|
||||
bool dovacuum;
|
||||
bool doanalyze;
|
||||
@ -1942,17 +1946,13 @@ do_autovacuum(void)
|
||||
|
||||
relid = HeapTupleGetOid(tuple);
|
||||
|
||||
/* Fetch the pg_autovacuum tuple for the relation, if any */
|
||||
avTup = get_pg_autovacuum_tuple_relid(avRel, relid, NULL);
|
||||
if (HeapTupleIsValid(avTup))
|
||||
avForm = (Form_pg_autovacuum) GETSTRUCT(avTup);
|
||||
|
||||
/* Fetch the pgstat entry for this table */
|
||||
/* Fetch reloptions and the pgstat entry for this table */
|
||||
relopts = extract_autovac_opts(tuple, pg_class_desc);
|
||||
tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
|
||||
shared, dbentry);
|
||||
|
||||
/* Check if it needs vacuum or analyze */
|
||||
relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
|
||||
relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
|
||||
&dovacuum, &doanalyze, &wraparound);
|
||||
|
||||
/*
|
||||
@ -1998,7 +1998,7 @@ do_autovacuum(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Plain relations that need work are added to table_oids */
|
||||
/* relations that need work are added to table_oids */
|
||||
if (dovacuum || doanalyze)
|
||||
table_oids = lappend_oid(table_oids, relid);
|
||||
|
||||
@ -2020,12 +2020,16 @@ do_autovacuum(void)
|
||||
{
|
||||
/* hash_search already filled in the key */
|
||||
hentry->ar_relid = relid;
|
||||
hentry->ar_hasrelopts = false;
|
||||
if (relopts != NULL)
|
||||
{
|
||||
hentry->ar_hasrelopts = true;
|
||||
memcpy(&hentry->ar_reloptions, relopts,
|
||||
sizeof(AutoVacOpts));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (HeapTupleIsValid(avTup))
|
||||
heap_freetuple(avTup);
|
||||
}
|
||||
|
||||
heap_endscan(relScan);
|
||||
@ -2040,10 +2044,9 @@ do_autovacuum(void)
|
||||
while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
|
||||
{
|
||||
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
|
||||
Form_pg_autovacuum avForm = NULL;
|
||||
PgStat_StatTabEntry *tabentry;
|
||||
HeapTuple avTup;
|
||||
Oid relid;
|
||||
AutoVacOpts *relopts = NULL;
|
||||
bool dovacuum;
|
||||
bool doanalyze;
|
||||
bool wraparound;
|
||||
@ -2057,17 +2060,26 @@ do_autovacuum(void)
|
||||
|
||||
relid = HeapTupleGetOid(tuple);
|
||||
|
||||
/* Fetch the pg_autovacuum tuple for this rel */
|
||||
avTup = get_pg_autovacuum_tuple_relid(avRel, relid, table_toast_map);
|
||||
/*
|
||||
* fetch reloptions -- if this toast table does not have them,
|
||||
* try the main rel
|
||||
*/
|
||||
relopts = extract_autovac_opts(tuple, pg_class_desc);
|
||||
if (relopts == NULL)
|
||||
{
|
||||
av_relation *hentry;
|
||||
bool found;
|
||||
|
||||
if (HeapTupleIsValid(avTup))
|
||||
avForm = (Form_pg_autovacuum) GETSTRUCT(avTup);
|
||||
hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found);
|
||||
if (found && hentry->ar_hasrelopts)
|
||||
relopts = &hentry->ar_reloptions;
|
||||
}
|
||||
|
||||
/* Fetch the pgstat entry for this table */
|
||||
tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
|
||||
shared, dbentry);
|
||||
|
||||
relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
|
||||
relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
|
||||
&dovacuum, &doanalyze, &wraparound);
|
||||
|
||||
/* ignore analyze for toast tables */
|
||||
@ -2076,7 +2088,6 @@ do_autovacuum(void)
|
||||
}
|
||||
|
||||
heap_endscan(relScan);
|
||||
heap_close(avRel, AccessShareLock);
|
||||
heap_close(classRel, AccessShareLock);
|
||||
|
||||
/*
|
||||
@ -2163,10 +2174,10 @@ do_autovacuum(void)
|
||||
* condition is not closed but it is very small.
|
||||
*/
|
||||
MemoryContextSwitchTo(AutovacMemCxt);
|
||||
tab = table_recheck_autovac(relid, table_toast_map);
|
||||
tab = table_recheck_autovac(relid, table_toast_map, pg_class_desc);
|
||||
if (tab == NULL)
|
||||
{
|
||||
/* someone else vacuumed the table */
|
||||
/* someone else vacuumed the table, or it went away */
|
||||
LWLockRelease(AutovacuumScheduleLock);
|
||||
continue;
|
||||
}
|
||||
@ -2292,49 +2303,29 @@ deleted:
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a copy of the pg_autovacuum tuple for the given relid, or NULL if
|
||||
* there isn't any. avRel is pg_autovacuum, already open and suitably locked.
|
||||
* extract_autovac_opts
|
||||
*
|
||||
* If table_toast_map is not null, use it to find an alternative OID with which
|
||||
* to search a pg_autovacuum entry, if the passed relid does not yield one
|
||||
* directly.
|
||||
* Given a relation's pg_class tuple, return the AutoVacOpts portion of
|
||||
* reloptions, if set; otherwise, return NULL.
|
||||
*/
|
||||
static HeapTuple
|
||||
get_pg_autovacuum_tuple_relid(Relation avRel, Oid relid,
|
||||
HTAB *table_toast_map)
|
||||
AutoVacOpts *
|
||||
extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
|
||||
{
|
||||
ScanKeyData entry[1];
|
||||
SysScanDesc avScan;
|
||||
HeapTuple avTup;
|
||||
bytea *relopts;
|
||||
AutoVacOpts *av;
|
||||
|
||||
ScanKeyInit(&entry[0],
|
||||
Anum_pg_autovacuum_vacrelid,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(relid));
|
||||
Assert(((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_RELATION ||
|
||||
((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE);
|
||||
|
||||
avScan = systable_beginscan(avRel, AutovacuumRelidIndexId, true,
|
||||
SnapshotNow, 1, entry);
|
||||
relopts = extractRelOptions(tup, pg_class_desc, InvalidOid);
|
||||
if (relopts == NULL)
|
||||
return NULL;
|
||||
|
||||
av = palloc(sizeof(AutoVacOpts));
|
||||
memcpy(av, &(((StdRdOptions *) relopts)->autovacuum), sizeof(AutoVacOpts));
|
||||
pfree(relopts);
|
||||
|
||||
avTup = systable_getnext(avScan);
|
||||
|
||||
if (HeapTupleIsValid(avTup))
|
||||
avTup = heap_copytuple(avTup);
|
||||
|
||||
systable_endscan(avScan);
|
||||
|
||||
if (!HeapTupleIsValid(avTup) && table_toast_map != NULL)
|
||||
{
|
||||
av_relation *hentry;
|
||||
bool found;
|
||||
|
||||
hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found);
|
||||
if (found)
|
||||
/* avoid second recursion */
|
||||
avTup = get_pg_autovacuum_tuple_relid(avRel, hentry->ar_relid,
|
||||
NULL);
|
||||
}
|
||||
|
||||
return avTup;
|
||||
return av;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2370,13 +2361,11 @@ get_pgstat_tabentry_relid(Oid relid, bool isshared, PgStat_StatDBEntry *shared,
|
||||
* Note that the returned autovac_table does not have the name fields set.
|
||||
*/
|
||||
static autovac_table *
|
||||
table_recheck_autovac(Oid relid, HTAB *table_toast_map)
|
||||
table_recheck_autovac(Oid relid, HTAB *table_toast_map,
|
||||
TupleDesc pg_class_desc)
|
||||
{
|
||||
Form_pg_autovacuum avForm = NULL;
|
||||
Form_pg_class classForm;
|
||||
HeapTuple classTup;
|
||||
HeapTuple avTup;
|
||||
Relation avRel;
|
||||
bool dovacuum;
|
||||
bool doanalyze;
|
||||
autovac_table *tab = NULL;
|
||||
@ -2384,6 +2373,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map)
|
||||
PgStat_StatDBEntry *shared;
|
||||
PgStat_StatDBEntry *dbentry;
|
||||
bool wraparound;
|
||||
AutoVacOpts *avopts;
|
||||
|
||||
/* use fresh stats */
|
||||
autovac_refresh_stats();
|
||||
@ -2399,23 +2389,27 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map)
|
||||
return NULL;
|
||||
classForm = (Form_pg_class) GETSTRUCT(classTup);
|
||||
|
||||
/*
|
||||
* Fetch the pg_autovacuum entry, if any. For a toast table, also try the
|
||||
* main rel's pg_autovacuum entry if there isn't one for the TOAST table
|
||||
* itself.
|
||||
/*
|
||||
* Get the applicable reloptions. If it is a TOAST table, try to get the
|
||||
* main table reloptions if the toast table itself doesn't have.
|
||||
*/
|
||||
avRel = heap_open(AutovacuumRelationId, AccessShareLock);
|
||||
avTup = get_pg_autovacuum_tuple_relid(avRel, relid,
|
||||
classForm->relkind == RELKIND_TOASTVALUE ? table_toast_map : NULL);
|
||||
avopts = extract_autovac_opts(classTup, pg_class_desc);
|
||||
if (classForm->relkind == RELKIND_TOASTVALUE &&
|
||||
avopts == NULL && table_toast_map != NULL)
|
||||
{
|
||||
av_relation *hentry;
|
||||
bool found;
|
||||
|
||||
if (HeapTupleIsValid(avTup))
|
||||
avForm = (Form_pg_autovacuum) GETSTRUCT(avTup);
|
||||
hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found);
|
||||
if (found && hentry->ar_hasrelopts)
|
||||
avopts = &hentry->ar_reloptions;
|
||||
}
|
||||
|
||||
/* fetch the pgstat table entry */
|
||||
tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
|
||||
shared, dbentry);
|
||||
|
||||
relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
|
||||
relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
|
||||
&dovacuum, &doanalyze, &wraparound);
|
||||
|
||||
/* ignore ANALYZE for toast tables */
|
||||
@ -2431,41 +2425,28 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map)
|
||||
int vac_cost_delay;
|
||||
|
||||
/*
|
||||
* Calculate the vacuum cost parameters and the minimum freeze age. If
|
||||
* there is a tuple in pg_autovacuum, use it; else, use the GUC
|
||||
* defaults. Note that the fields may contain "-1" (or indeed any
|
||||
* negative value), which means use the GUC defaults for each setting.
|
||||
* In cost_limit, the value 0 also means to use the value from
|
||||
* elsewhere.
|
||||
* Calculate the vacuum cost parameters and the freeze ages. If there
|
||||
* are options set in pg_class.reloptions, use them; in the case of a
|
||||
* toast table, try the main table too. Otherwise use the GUC
|
||||
* defaults, autovacuum's own first and plain vacuum second.
|
||||
*/
|
||||
if (avForm != NULL)
|
||||
if (avopts)
|
||||
{
|
||||
vac_cost_limit = (avForm->vac_cost_limit > 0) ?
|
||||
avForm->vac_cost_limit :
|
||||
((autovacuum_vac_cost_limit > 0) ?
|
||||
autovacuum_vac_cost_limit : VacuumCostLimit);
|
||||
|
||||
vac_cost_delay = (avForm->vac_cost_delay >= 0) ?
|
||||
avForm->vac_cost_delay :
|
||||
((autovacuum_vac_cost_delay >= 0) ?
|
||||
autovacuum_vac_cost_delay : VacuumCostDelay);
|
||||
|
||||
freeze_min_age = (avForm->freeze_min_age >= 0) ?
|
||||
avForm->freeze_min_age : default_freeze_min_age;
|
||||
|
||||
freeze_table_age = (avForm->freeze_table_age >= 0) ?
|
||||
avForm->freeze_table_age : default_freeze_table_age;
|
||||
vac_cost_delay = avopts->vacuum_cost_delay;
|
||||
vac_cost_limit = avopts->vacuum_cost_limit;
|
||||
freeze_min_age = avopts->freeze_min_age;
|
||||
freeze_table_age = avopts->freeze_table_age;
|
||||
}
|
||||
else
|
||||
{
|
||||
vac_cost_limit = (autovacuum_vac_cost_limit > 0) ?
|
||||
autovacuum_vac_cost_limit : VacuumCostLimit;
|
||||
|
||||
vac_cost_delay = (autovacuum_vac_cost_delay >= 0) ?
|
||||
/* -1 in autovac setting means use plain vacuum_cost_delay */
|
||||
vac_cost_delay = autovacuum_vac_cost_delay >= 0 ?
|
||||
autovacuum_vac_cost_delay : VacuumCostDelay;
|
||||
|
||||
/* 0 or -1 in autovac setting means use plain vacuum_cost_limit */
|
||||
vac_cost_limit = autovacuum_vac_cost_limit > 0 ?
|
||||
autovacuum_vac_cost_limit : VacuumCostLimit;
|
||||
/* these do not have autovacuum-specific settings */
|
||||
freeze_min_age = default_freeze_min_age;
|
||||
|
||||
freeze_table_age = default_freeze_table_age;
|
||||
}
|
||||
|
||||
@ -2483,9 +2464,6 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map)
|
||||
tab->at_datname = NULL;
|
||||
}
|
||||
|
||||
heap_close(avRel, AccessShareLock);
|
||||
if (HeapTupleIsValid(avTup))
|
||||
heap_freetuple(avTup);
|
||||
heap_freetuple(classTup);
|
||||
|
||||
return tab;
|
||||
@ -2496,8 +2474,12 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map)
|
||||
*
|
||||
* Check whether a relation needs to be vacuumed or analyzed; return each into
|
||||
* "dovacuum" and "doanalyze", respectively. Also return whether the vacuum is
|
||||
* being forced because of Xid wraparound. avForm and tabentry can be NULL,
|
||||
* classForm shouldn't.
|
||||
* being forced because of Xid wraparound.
|
||||
*
|
||||
* relopts is a pointer to the AutoVacOpts options (either for itself in the
|
||||
* case of a plain table, or for either itself or its parent table in the case
|
||||
* of a TOAST table), NULL if none; tabentry is the pgstats entry, which can be
|
||||
* NULL.
|
||||
*
|
||||
* A table needs to be vacuumed if the number of dead tuples exceeds a
|
||||
* threshold. This threshold is calculated as
|
||||
@ -2513,19 +2495,19 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map)
|
||||
* We also force vacuum if the table's relfrozenxid is more than freeze_max_age
|
||||
* transactions back.
|
||||
*
|
||||
* A table whose pg_autovacuum.enabled value is false, is automatically
|
||||
* skipped (unless we have to vacuum it due to freeze_max_age). Thus
|
||||
* autovacuum can be disabled for specific tables. Also, when the stats
|
||||
* A table whose autovacuum_enabled option is false is
|
||||
* automatically skipped (unless we have to vacuum it due to freeze_max_age).
|
||||
* Thus autovacuum can be disabled for specific tables. Also, when the stats
|
||||
* collector does not have data about a table, it will be skipped.
|
||||
*
|
||||
* A table whose vac_base_thresh value is <0 takes the base value from the
|
||||
* A table whose vac_base_thresh value is < 0 takes the base value from the
|
||||
* autovacuum_vacuum_threshold GUC variable. Similarly, a vac_scale_factor
|
||||
* value <0 is substituted with the value of
|
||||
* value < 0 is substituted with the value of
|
||||
* autovacuum_vacuum_scale_factor GUC variable. Ditto for analyze.
|
||||
*/
|
||||
static void
|
||||
relation_needs_vacanalyze(Oid relid,
|
||||
Form_pg_autovacuum avForm,
|
||||
AutoVacOpts *relopts,
|
||||
Form_pg_class classForm,
|
||||
PgStat_StatTabEntry *tabentry,
|
||||
/* output params below */
|
||||
@ -2534,9 +2516,10 @@ relation_needs_vacanalyze(Oid relid,
|
||||
bool *wraparound)
|
||||
{
|
||||
bool force_vacuum;
|
||||
bool av_enabled;
|
||||
float4 reltuples; /* pg_class.reltuples */
|
||||
|
||||
/* constants from pg_autovacuum or GUC variables */
|
||||
/* constants from reloptions or GUC variables */
|
||||
int vac_base_thresh,
|
||||
anl_base_thresh;
|
||||
float4 vac_scale_factor,
|
||||
@ -2558,36 +2541,28 @@ relation_needs_vacanalyze(Oid relid,
|
||||
AssertArg(OidIsValid(relid));
|
||||
|
||||
/*
|
||||
* Determine vacuum/analyze equation parameters. If there is a tuple in
|
||||
* pg_autovacuum, use it; else, use the GUC defaults. Note that the
|
||||
* fields may contain "-1" (or indeed any negative value), which means use
|
||||
* the GUC defaults for each setting.
|
||||
* Determine vacuum/analyze equation parameters. We have two possible
|
||||
* sources: the passed reloptions (which could be a main table or a toast
|
||||
* table), or the autovacuum GUC variables.
|
||||
*/
|
||||
if (avForm != NULL)
|
||||
if (relopts)
|
||||
{
|
||||
vac_scale_factor = (avForm->vac_scale_factor >= 0) ?
|
||||
avForm->vac_scale_factor : autovacuum_vac_scale;
|
||||
vac_base_thresh = (avForm->vac_base_thresh >= 0) ?
|
||||
avForm->vac_base_thresh : autovacuum_vac_thresh;
|
||||
|
||||
anl_scale_factor = (avForm->anl_scale_factor >= 0) ?
|
||||
avForm->anl_scale_factor : autovacuum_anl_scale;
|
||||
anl_base_thresh = (avForm->anl_base_thresh >= 0) ?
|
||||
avForm->anl_base_thresh : autovacuum_anl_thresh;
|
||||
|
||||
freeze_max_age = (avForm->freeze_max_age >= 0) ?
|
||||
Min(avForm->freeze_max_age, autovacuum_freeze_max_age) :
|
||||
autovacuum_freeze_max_age;
|
||||
vac_scale_factor = relopts->vacuum_scale_factor;
|
||||
vac_base_thresh = relopts->vacuum_threshold;
|
||||
anl_scale_factor = relopts->analyze_scale_factor;
|
||||
anl_base_thresh = relopts->analyze_threshold;
|
||||
freeze_max_age = Min(relopts->freeze_max_age,
|
||||
autovacuum_freeze_max_age);
|
||||
av_enabled = relopts->enabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
vac_scale_factor = autovacuum_vac_scale;
|
||||
vac_base_thresh = autovacuum_vac_thresh;
|
||||
|
||||
anl_scale_factor = autovacuum_anl_scale;
|
||||
anl_base_thresh = autovacuum_anl_thresh;
|
||||
|
||||
freeze_max_age = autovacuum_freeze_max_age;
|
||||
av_enabled = true;
|
||||
}
|
||||
|
||||
/* Force vacuum if table is at risk of wraparound */
|
||||
@ -2599,8 +2574,8 @@ relation_needs_vacanalyze(Oid relid,
|
||||
xidForceLimit));
|
||||
*wraparound = force_vacuum;
|
||||
|
||||
/* User disabled it in pg_autovacuum? (But ignore if at risk) */
|
||||
if (avForm && !avForm->enabled && !force_vacuum)
|
||||
/* User disabled it in pg_class.reloptions? (But ignore if at risk) */
|
||||
if (!force_vacuum && !av_enabled)
|
||||
{
|
||||
*doanalyze = false;
|
||||
*dovacuum = false;
|
||||
|
@ -37,7 +37,7 @@
|
||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.521 2009/02/06 21:15:11 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.522 2009/02/09 20:57:59 alvherre Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 200902061
|
||||
#define CATALOG_VERSION_NO 200902091
|
||||
|
||||
#endif
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.106 2009/01/22 20:16:08 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.107 2009/02/09 20:57:59 alvherre Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -97,9 +97,6 @@ DECLARE_UNIQUE_INDEX(pg_auth_members_role_member_index, 2694, on pg_auth_members
|
||||
DECLARE_UNIQUE_INDEX(pg_auth_members_member_role_index, 2695, on pg_auth_members using btree(member oid_ops, roleid oid_ops));
|
||||
#define AuthMemMemRoleIndexId 2695
|
||||
|
||||
DECLARE_UNIQUE_INDEX(pg_autovacuum_vacrelid_index, 1250, on pg_autovacuum using btree(vacrelid oid_ops));
|
||||
#define AutovacuumRelidIndexId 1250
|
||||
|
||||
DECLARE_UNIQUE_INDEX(pg_cast_oid_index, 2660, on pg_cast using btree(oid oid_ops));
|
||||
#define CastOidIndexId 2660
|
||||
DECLARE_UNIQUE_INDEX(pg_cast_source_target_index, 2661, on pg_cast using btree(castsource oid_ops, casttarget oid_ops));
|
||||
|
@ -1,66 +0,0 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* pg_autovacuum.h
|
||||
* definition of the system "autovacuum" relation (pg_autovacuum)
|
||||
*
|
||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/catalog/pg_autovacuum.h,v 1.11 2009/01/16 13:27:24 heikki Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef PG_AUTOVACUUM_H
|
||||
#define PG_AUTOVACUUM_H
|
||||
|
||||
#include "catalog/genbki.h"
|
||||
|
||||
/* ----------------
|
||||
* pg_autovacuum definition. cpp turns this into
|
||||
* typedef struct FormData_pg_autovacuum
|
||||
* ----------------
|
||||
*/
|
||||
#define AutovacuumRelationId 1248
|
||||
|
||||
CATALOG(pg_autovacuum,1248) BKI_WITHOUT_OIDS
|
||||
{
|
||||
Oid vacrelid; /* OID of table */
|
||||
bool enabled; /* enabled for this table? */
|
||||
int4 vac_base_thresh; /* base threshold value */
|
||||
float4 vac_scale_factor; /* reltuples scaling factor */
|
||||
int4 anl_base_thresh; /* base threshold value */
|
||||
float4 anl_scale_factor; /* reltuples scaling factor */
|
||||
int4 vac_cost_delay; /* vacuum cost-based delay */
|
||||
int4 vac_cost_limit; /* vacuum cost limit */
|
||||
int4 freeze_min_age; /* vacuum min freeze age */
|
||||
int4 freeze_max_age; /* max age before forcing vacuum */
|
||||
int4 freeze_table_age; /* age at which vacuum scans whole table */
|
||||
} FormData_pg_autovacuum;
|
||||
|
||||
/* ----------------
|
||||
* Form_pg_autovacuum corresponds to a pointer to a tuple with
|
||||
* the format of pg_autovacuum relation.
|
||||
* ----------------
|
||||
*/
|
||||
typedef FormData_pg_autovacuum *Form_pg_autovacuum;
|
||||
|
||||
/* ----------------
|
||||
* compiler constants for pg_autovacuum
|
||||
* ----------------
|
||||
*/
|
||||
#define Natts_pg_autovacuum 10
|
||||
#define Anum_pg_autovacuum_vacrelid 1
|
||||
#define Anum_pg_autovacuum_enabled 2
|
||||
#define Anum_pg_autovacuum_vac_base_thresh 3
|
||||
#define Anum_pg_autovacuum_vac_scale_factor 4
|
||||
#define Anum_pg_autovacuum_anl_base_thresh 5
|
||||
#define Anum_pg_autovacuum_anl_scale_factor 6
|
||||
#define Anum_pg_autovacuum_vac_cost_delay 7
|
||||
#define Anum_pg_autovacuum_vac_cost_limit 8
|
||||
#define Anum_pg_autovacuum_freeze_min_age 9
|
||||
#define Anum_pg_autovacuum_freeze_max_age 10
|
||||
#define Anum_pg_autovacuum_freeze_table_age 11
|
||||
|
||||
/* There are no preloaded tuples in pg_autovacuum.h */
|
||||
|
||||
#endif /* PG_AUTOVACUUM_H */
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.111 2009/01/01 17:24:02 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.112 2009/02/09 20:57:59 alvherre Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -214,10 +214,26 @@ typedef struct RelationData
|
||||
* be applied to relations that use this format or a superset for
|
||||
* private options data.
|
||||
*/
|
||||
/* autovacuum-related reloptions. */
|
||||
typedef struct AutoVacOpts
|
||||
{
|
||||
bool enabled;
|
||||
int vacuum_threshold;
|
||||
int analyze_threshold;
|
||||
int vacuum_cost_delay;
|
||||
int vacuum_cost_limit;
|
||||
int freeze_min_age;
|
||||
int freeze_max_age;
|
||||
int freeze_table_age;
|
||||
float8 vacuum_scale_factor;
|
||||
float8 analyze_scale_factor;
|
||||
} AutoVacOpts;
|
||||
|
||||
typedef struct StdRdOptions
|
||||
{
|
||||
int32 vl_len_; /* varlena header (do not touch directly!) */
|
||||
int fillfactor; /* page fill factor in percent (0..100) */
|
||||
AutoVacOpts autovacuum; /* autovacuum-related options */
|
||||
} StdRdOptions;
|
||||
|
||||
#define HEAP_MIN_FILLFACTOR 10
|
||||
|
@ -90,7 +90,6 @@ SELECT relname, relhasindex
|
||||
pg_attribute | t
|
||||
pg_auth_members | t
|
||||
pg_authid | t
|
||||
pg_autovacuum | t
|
||||
pg_cast | t
|
||||
pg_class | t
|
||||
pg_constraint | t
|
||||
@ -152,7 +151,7 @@ SELECT relname, relhasindex
|
||||
timetz_tbl | f
|
||||
tinterval_tbl | f
|
||||
varchar_tbl | f
|
||||
(141 rows)
|
||||
(140 rows)
|
||||
|
||||
--
|
||||
-- another sanity check: every system catalog that has OIDs should have
|
||||
|
Reference in New Issue
Block a user