autovac-partitioned-via-hash.diff
text/x-diff
Filename: autovac-partitioned-via-hash.diff
Type: text/x-diff
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/commands/analyze.c | 16 | 18 |
| src/backend/postmaster/autovacuum.c | 158 | 84 |
| src/backend/postmaster/pgstat.c | 8 | 6 |
commit ec796bd8ee2970e2eae3b3839e1bb96696393dc7
Author: Andres Freund <andres@anarazel.de>
Date: 2021-07-30 17:20:21 -0700
tmp
Author:
Reviewed-By:
Discussion: https://postgr.es/m/
Backpatch:
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 0c9591415e4..df021215281 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -320,12 +320,12 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
PgStat_Counter startwritetime = 0;
if (inh)
- ereport(elevel,
+ ereport(LOG,
(errmsg("analyzing \"%s.%s\" inheritance tree",
get_namespace_name(RelationGetNamespace(onerel)),
RelationGetRelationName(onerel))));
else
- ereport(elevel,
+ ereport(LOG,
(errmsg("analyzing \"%s.%s\"",
get_namespace_name(RelationGetNamespace(onerel)),
RelationGetRelationName(onerel))));
@@ -682,6 +682,18 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
in_outer_xact);
}
+ /*
+ * Let the collector know about the ancestor tables of this partition.
+ */
+ if (!inh &&
+ (va_cols == NIL) &&
+ onerel->rd_rel->relispartition &&
+ onerel->rd_rel->relkind == RELKIND_RELATION &&
+ onerel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)
+ {
+ pgstat_report_anl_ancestors(RelationGetRelid(onerel));
+ }
+
/*
* Now report ANALYZE to the stats collector. For regular tables, we do
* it only if not doing inherited stats. For partitioned tables, we only
@@ -695,22 +707,6 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
pgstat_report_analyze(onerel, totalrows, totaldeadrows,
(va_cols == NIL));
- /*
- * If this is a manual analyze of all columns of a permanent leaf
- * partition, and not doing inherited stats, also let the collector know
- * about the ancestor tables of this partition. Autovacuum does the
- * equivalent of this at the start of its run, so there's no reason to do
- * it there.
- */
- if (!inh && !IsAutoVacuumWorkerProcess() &&
- (va_cols == NIL) &&
- onerel->rd_rel->relispartition &&
- onerel->rd_rel->relkind == RELKIND_RELATION &&
- onerel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)
- {
- pgstat_report_anl_ancestors(RelationGetRelid(onerel));
- }
-
/*
* If this isn't part of VACUUM ANALYZE, let index AMs do cleanup.
*
@@ -1183,6 +1179,8 @@ acquire_sample_rows(Relation onerel, int elevel,
BlockSamplerData prefetch_bs;
#endif
+ elog(LOG, "acquiring %d sample rows for %s", targrows,
+ NameStr(onerel->rd_rel->relname));
Assert(targrows > 0);
totalblocks = RelationGetNumberOfBlocks(onerel);
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 912ef9cb54c..73a872371d1 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -74,6 +74,7 @@
#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/namespace.h"
+#include "catalog/partition.h"
#include "catalog/pg_database.h"
#include "catalog/pg_inherits.h"
#include "commands/dbcommands.h"
@@ -327,16 +328,21 @@ static void do_autovacuum(void);
static void FreeWorkerInfo(int code, Datum arg);
static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map,
+ HTAB *table_partition_info_map,
TupleDesc pg_class_desc,
- int effective_multixact_freeze_max_age);
-static void recheck_relation_needs_vacanalyze(Oid relid, AutoVacOpts *avopts,
+ int effective_multixact_freeze_max_age,
+ PgStat_Counter additional_analyze_rows);
+static void recheck_relation_needs_vacanalyze(Oid relid,
+ AutoVacOpts *avopts,
Form_pg_class classForm,
int effective_multixact_freeze_max_age,
+ PgStat_Counter additional_analyze_rows,
bool *dovacuum, bool *doanalyze, bool *wraparound);
static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
Form_pg_class classForm,
PgStat_StatTabEntry *tabentry,
int effective_multixact_freeze_max_age,
+ PgStat_Counter additional_analyze_rows,
bool *dovacuum, bool *doanalyze, bool *wraparound);
static void autovacuum_do_vac_analyze(autovac_table *tab,
@@ -1944,6 +1950,12 @@ get_database_list(void)
return dblist;
}
+typedef struct AutovacPartitionInfo
+{
+ Oid partitioned_table_oid;
+ PgStat_Counter changes_since_analyze;
+} AutovacPartitionInfo;
+
/*
* Process a database table-by-table
*
@@ -1961,16 +1973,15 @@ do_autovacuum(void)
List *orphan_oids = NIL;
HASHCTL ctl;
HTAB *table_toast_map;
+ HTAB *partition_info_map;
ListCell *volatile cell;
PgStat_StatDBEntry *shared;
PgStat_StatDBEntry *dbentry;
BufferAccessStrategy bstrategy;
- ScanKeyData key;
TupleDesc pg_class_desc;
int effective_multixact_freeze_max_age;
bool did_vacuum = false;
bool found_concurrent_worker = false;
- bool updated = false;
int i;
/*
@@ -2053,19 +2064,26 @@ do_autovacuum(void)
&ctl,
HASH_ELEM | HASH_BLOBS);
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(AutovacPartitionInfo);
+
+ partition_info_map = hash_create("Autovacuum Partitioned Table Rowcount Hash",
+ 100,
+ &ctl,
+ HASH_ELEM | HASH_BLOBS);
+
+
/*
* Scan pg_class to determine which tables to vacuum.
*
- * We do this in three passes: First we let pgstat collector know about
- * the partitioned table ancestors of all partitions that have recently
- * acquired rows for analyze. This informs the second pass about the
- * total number of tuple count in partitioning hierarchies.
+ * We do this in two passes:
*
- * On the second pass, we collect the list of plain relations,
- * materialized views and partitioned tables. On the third one we collect
- * TOAST tables.
+ * On the first pass, we collect the list of plain relations, materialized
+ * views and partitioned tables. For partitions we sum up the sum of the
+ * changes in partitioned tables. On the second pass one we collect TOAST
+ * and partitioned tables tables.
*
- * The reason for doing the third pass is that during it we want to use
+ * The reason for doing the second pass is that during it we want to use
* 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.
@@ -2077,44 +2095,7 @@ do_autovacuum(void)
relScan = table_beginscan_catalog(classRel, 0, NULL);
/*
- * First pass: before collecting the list of tables to vacuum, let stat
- * collector know about partitioned-table ancestors of each partition.
- */
- while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
- {
- Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
- Oid relid = classForm->oid;
- PgStat_StatTabEntry *tabentry;
-
- /* Only consider permanent leaf partitions */
- if (!classForm->relispartition ||
- classForm->relkind == RELKIND_PARTITIONED_TABLE ||
- classForm->relpersistence == RELPERSISTENCE_TEMP)
- continue;
-
- /*
- * No need to do this for partitions that haven't acquired any rows.
- */
- tabentry = pgstat_fetch_stat_tabentry(relid);
- if (tabentry &&
- tabentry->changes_since_analyze -
- tabentry->changes_since_analyze_reported > 0)
- {
- pgstat_report_anl_ancestors(relid);
- updated = true;
- }
- }
-
- /* Acquire fresh stats for the next passes, if needed */
- if (updated)
- {
- autovac_refresh_stats();
- dbentry = pgstat_fetch_stat_dbentry(MyDatabaseId);
- shared = pgstat_fetch_stat_dbentry(InvalidOid);
- }
-
- /*
- * On the second pass, we collect main tables to vacuum, and also the main
+ * On the first pass, we collect main tables to vacuum, and also the main
* table relid to TOAST relid mapping.
*/
while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
@@ -2128,8 +2109,7 @@ do_autovacuum(void)
bool wraparound;
if (classForm->relkind != RELKIND_RELATION &&
- classForm->relkind != RELKIND_MATVIEW &&
- classForm->relkind != RELKIND_PARTITIONED_TABLE)
+ classForm->relkind != RELKIND_MATVIEW)
continue;
relid = classForm->oid;
@@ -2167,6 +2147,7 @@ do_autovacuum(void)
/* Check if it needs vacuum or analyze */
relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
effective_multixact_freeze_max_age,
+ 0,
&dovacuum, &doanalyze, &wraparound);
/* Relations that need work are added to table_oids */
@@ -2200,17 +2181,40 @@ do_autovacuum(void)
}
}
}
+
+ /* Sum up pending changes to leaf partitions in parent partition */
+ if (classForm->relispartition &&
+ classForm->relpersistence != RELPERSISTENCE_TEMP &&
+ (tabentry && tabentry->changes_since_analyze > 0))
+ {
+ List *ancestors = get_partition_ancestors(classForm->oid);
+ ListCell *lc;
+
+ foreach(lc, ancestors)
+ {
+ Oid ancestor_oid = lfirst_oid(lc);
+ AutovacPartitionInfo *ancestor;
+ bool found;
+
+ ancestor = hash_search(partition_info_map,
+ &ancestor_oid,
+ HASH_ENTER, &found);
+ if (!found)
+ ancestor->changes_since_analyze = 0;
+ ancestor->changes_since_analyze += tabentry->changes_since_analyze;
+
+ elog(LOG, "reporting up %lu changes from %u to ancestor %u",
+ tabentry->changes_since_analyze, relid, ancestor_oid);
+ }
+
+ continue;
+ }
}
table_endscan(relScan);
- /* third pass: check TOAST tables */
- ScanKeyInit(&key,
- Anum_pg_class_relkind,
- BTEqualStrategyNumber, F_CHAREQ,
- CharGetDatum(RELKIND_TOASTVALUE));
-
- relScan = table_beginscan_catalog(classRel, 1, &key);
+ /* second pass: check TOAST and partitioned tables */
+ relScan = table_beginscan_catalog(classRel, 0, NULL);
while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
@@ -2221,6 +2225,10 @@ do_autovacuum(void)
bool doanalyze;
bool wraparound;
+ if (classForm->relkind != RELKIND_TOASTVALUE &&
+ classForm->relkind != RELKIND_PARTITIONED_TABLE)
+ continue;
+
/*
* We cannot safely process other backends' temp tables, so skip 'em.
*/
@@ -2228,33 +2236,63 @@ do_autovacuum(void)
continue;
relid = classForm->oid;
-
- /*
- * 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)
+
+ if (classForm->relkind == RELKIND_TOASTVALUE)
{
- av_relation *hentry;
- bool found;
+ /*
+ * If this toast table does not have a reloption, try the main rel
+ */
+ if (relopts == NULL)
+ {
+ av_relation *hentry;
+ bool found;
- hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found);
- if (found && hentry->ar_hasrelopts)
- relopts = &hentry->ar_reloptions;
+ 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, relopts, classForm, tabentry,
+ effective_multixact_freeze_max_age,
+ 0,
+ &dovacuum, &doanalyze, &wraparound);
+
+ /* ignore analyze for toast tables */
+ if (dovacuum)
+ table_oids = lappend_oid(table_oids, relid);
}
+ else if (classForm->relkind == RELKIND_PARTITIONED_TABLE)
+ {
+ AutovacPartitionInfo *partition_info;
+ PgStat_Counter additional_analyze_rows = 0;
+ bool found;
- /* Fetch the pgstat entry for this table */
- tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
- shared, dbentry);
+ tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
+ shared, dbentry);
- relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
- effective_multixact_freeze_max_age,
- &dovacuum, &doanalyze, &wraparound);
+ partition_info = hash_search(partition_info_map, &relid, HASH_FIND, &found);
+ if (found)
+ {
+ elog(LOG, "additional changes to %u: %llu",
+ relid, (long long unsigned) partition_info->changes_since_analyze);
+ additional_analyze_rows = partition_info->changes_since_analyze;
+ }
- /* ignore analyze for toast tables */
- if (dovacuum)
- table_oids = lappend_oid(table_oids, relid);
+ relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
+ effective_multixact_freeze_max_age,
+ additional_analyze_rows,
+ &dovacuum, &doanalyze, &wraparound);
+
+ Assert(!dovacuum && !wraparound);
+
+ if (doanalyze)
+ table_oids = lappend_oid(table_oids, relid);
+ }
}
table_endscan(relScan);
@@ -2369,12 +2407,14 @@ do_autovacuum(void)
{
Oid relid = lfirst_oid(cell);
HeapTuple classTup;
+ Form_pg_class classForm;
autovac_table *tab;
bool isshared;
bool skipit;
double stdVacuumCostDelay;
int stdVacuumCostLimit;
dlist_iter iter;
+ PgStat_Counter additional_analyze_rows = 0;
CHECK_FOR_INTERRUPTS();
@@ -2405,7 +2445,8 @@ do_autovacuum(void)
classTup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(classTup))
continue; /* somebody deleted the rel, forget it */
- isshared = ((Form_pg_class) GETSTRUCT(classTup))->relisshared;
+ classForm = (Form_pg_class) GETSTRUCT(classTup);
+ isshared = classForm->relisshared;
ReleaseSysCache(classTup);
/*
@@ -2467,9 +2508,27 @@ do_autovacuum(void)
* that somebody just finished vacuuming this table. The window to
* the race condition is not closed but it is very small.
*/
+
+ if (classForm->relkind == RELKIND_PARTITIONED_TABLE)
+ {
+ AutovacPartitionInfo *partition_info;
+ bool found;
+
+ partition_info = hash_search(partition_info_map, &relid, HASH_FIND, &found);
+ if (found)
+ {
+ elog(LOG, "additional changes to %u: %llu",
+ relid, (long long unsigned) partition_info->changes_since_analyze);
+ additional_analyze_rows = partition_info->changes_since_analyze;
+ }
+ }
+
MemoryContextSwitchTo(AutovacMemCxt);
- tab = table_recheck_autovac(relid, table_toast_map, pg_class_desc,
- effective_multixact_freeze_max_age);
+ tab = table_recheck_autovac(relid,
+ table_toast_map, partition_info_map,
+ pg_class_desc,
+ effective_multixact_freeze_max_age,
+ additional_analyze_rows);
if (tab == NULL)
{
/* someone else vacuumed the table, or it went away */
@@ -2845,8 +2904,10 @@ get_pgstat_tabentry_relid(Oid relid, bool isshared, PgStat_StatDBEntry *shared,
*/
static autovac_table *
table_recheck_autovac(Oid relid, HTAB *table_toast_map,
+ HTAB *partition_info_map,
TupleDesc pg_class_desc,
- int effective_multixact_freeze_max_age)
+ int effective_multixact_freeze_max_age,
+ PgStat_Counter additional_analyze_rows)
{
Form_pg_class classForm;
HeapTuple classTup;
@@ -2895,6 +2956,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
{
recheck_relation_needs_vacanalyze(relid, avopts, classForm,
effective_multixact_freeze_max_age,
+ additional_analyze_rows,
&dovacuum, &doanalyze, &wraparound);
/* Quick exit if a relation doesn't need to be vacuumed or analyzed */
@@ -2910,6 +2972,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
recheck_relation_needs_vacanalyze(relid, avopts, classForm,
effective_multixact_freeze_max_age,
+ additional_analyze_rows,
&dovacuum, &doanalyze, &wraparound);
/* OK, it needs something done */
@@ -3039,6 +3102,7 @@ recheck_relation_needs_vacanalyze(Oid relid,
AutoVacOpts *avopts,
Form_pg_class classForm,
int effective_multixact_freeze_max_age,
+ PgStat_Counter additional_analyze_rows,
bool *dovacuum,
bool *doanalyze,
bool *wraparound)
@@ -3058,6 +3122,7 @@ recheck_relation_needs_vacanalyze(Oid relid,
relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
effective_multixact_freeze_max_age,
+ additional_analyze_rows,
dovacuum, doanalyze, wraparound);
/* ignore ANALYZE for toast tables */
@@ -3108,6 +3173,7 @@ relation_needs_vacanalyze(Oid relid,
Form_pg_class classForm,
PgStat_StatTabEntry *tabentry,
int effective_multixact_freeze_max_age,
+ PgStat_Counter additional_analyze_rows,
/* output params below */
bool *dovacuum,
bool *doanalyze,
@@ -3223,7 +3289,7 @@ relation_needs_vacanalyze(Oid relid,
reltuples = classForm->reltuples;
vactuples = tabentry->n_dead_tuples;
instuples = tabentry->inserts_since_vacuum;
- anltuples = tabentry->changes_since_analyze;
+ anltuples = tabentry->changes_since_analyze + additional_analyze_rows;
/* If the table hasn't yet been vacuumed, take reltuples as zero */
if (reltuples < 0)
@@ -3252,6 +3318,14 @@ relation_needs_vacanalyze(Oid relid,
(vac_ins_base_thresh >= 0 && instuples > vacinsthresh);
*doanalyze = (anltuples > anlthresh);
}
+ else if (additional_analyze_rows && AutoVacuumingActive())
+ {
+ anlthresh = (float4) anl_base_thresh;
+ anltuples = additional_analyze_rows;
+ *dovacuum = false;
+ *wraparound = false;
+ *doanalyze = (anltuples > anlthresh);
+ }
else
{
/*
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 11702f2a804..d1e9fd3f75c 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -4870,6 +4870,7 @@ pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
tabentry->n_live_tuples = tabmsg->t_counts.t_delta_live_tuples;
tabentry->n_dead_tuples = tabmsg->t_counts.t_delta_dead_tuples;
tabentry->changes_since_analyze = tabmsg->t_counts.t_changed_tuples;
+ //tabentry->changes_since_analyze_reported = tabmsg->t_counts.t_changed_tuples;
tabentry->changes_since_analyze_reported = 0;
tabentry->inserts_since_vacuum = tabmsg->t_counts.t_tuples_inserted;
tabentry->blocks_fetched = tabmsg->t_counts.t_blocks_fetched;
@@ -5269,8 +5270,11 @@ pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len)
*/
if (msg->m_resetcounter)
{
+ elog(LOG, "resetting change_since_analyze of %u, was %lu/%lu",
+ msg->m_tableoid, tabentry->changes_since_analyze,
+ tabentry->changes_since_analyze_reported);
+ tabentry->changes_since_analyze_reported = tabentry->changes_since_analyze;
tabentry->changes_since_analyze = 0;
- tabentry->changes_since_analyze_reported = 0;
}
if (msg->m_autovacuum)
@@ -5301,12 +5305,10 @@ pgstat_recv_anl_ancestors(PgStat_MsgAnlAncestors *msg, int len)
PgStat_StatTabEntry *ancestor;
ancestor = pgstat_get_tab_entry(dbentry, ancestor_relid, true);
- ancestor->changes_since_analyze +=
- tabentry->changes_since_analyze - tabentry->changes_since_analyze_reported;
+ elog(LOG, "anl increase to %u of %lu",
+ ancestor_relid, tabentry->changes_since_analyze);
+ ancestor->changes_since_analyze += tabentry->changes_since_analyze;
}
-
- tabentry->changes_since_analyze_reported = tabentry->changes_since_analyze;
-
}
/* ----------