v1_autovacuum_on_partitioned_table.patch
application/octet-stream
Filename: v1_autovacuum_on_partitioned_table.patch
Type: application/octet-stream
Part: 0
Message:
Autovacuum on partitioned table
Patch
Format: unified
Series: patch v1
| File | + | − |
|---|---|---|
| src/backend/postmaster/autovacuum.c | 148 | 38 |
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index c1dd8168ca..d4ce99a9f5 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -75,6 +75,7 @@
#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_inherits.h"
#include "commands/dbcommands.h"
#include "commands/vacuum.h"
#include "lib/ilist.h"
@@ -2036,11 +2037,11 @@ do_autovacuum(void)
* Scan pg_class to determine which tables to vacuum.
*
* We do this in two passes: on the first one we collect the list of plain
- * relations and materialized views, 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_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.
+ * relations, materialized views and partitioned tables, 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_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
@@ -2063,7 +2064,12 @@ do_autovacuum(void)
bool wraparound;
if (classForm->relkind != RELKIND_RELATION &&
- classForm->relkind != RELKIND_MATVIEW)
+ classForm->relkind != RELKIND_MATVIEW &&
+ classForm->relkind != RELKIND_PARTITIONED_TABLE)
+ continue;
+
+ /* Collect partitioned tables, not partitions. So skip them. */
+ if (classForm->relispartition)
continue;
relid = classForm->oid;
@@ -2092,19 +2098,76 @@ do_autovacuum(void)
continue;
}
- /* 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);
+ if (classForm->relkind == RELKIND_RELATION ||
+ classForm->relkind == RELKIND_MATVIEW)
+ {
+ /* 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, relopts, classForm, tabentry,
+ effective_multixact_freeze_max_age,
+ &dovacuum, &doanalyze, &wraparound);
+
+ /* Relations that need work are added to table_oids */
+ if (dovacuum || doanalyze)
+ table_oids = lappend_oid(table_oids, relid);
+ }
+ else
+ {
+ /*
+ * If the relation is a partitioned table, we check if its children
+ * need vacuum or analyze. All children excluding foreign partitions
+ * need to do that are added to the table_oids list. At least one
+ * child is added the list, the partitioned table become an object
+ * for autovacuum.
+ */
+ List *tableOIDs;
+ ListCell *lc;
+ List *child_oids = NIL;
- /* Check if it needs vacuum or analyze */
- relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
- effective_multixact_freeze_max_age,
- &dovacuum, &doanalyze, &wraparound);
+ /* Find all members of inheritance set taking AccessShareLock */
+ tableOIDs = find_all_inheritors(relid, AccessShareLock, NULL);
- /* Relations that need work are added to table_oids */
- if (dovacuum || doanalyze)
- table_oids = lappend_oid(table_oids, relid);
+ foreach(lc, tableOIDs)
+ {
+ Oid childOID = lfirst_oid(lc);
+ HeapTuple childtuple;
+ Form_pg_class childclassForm;
+
+ /* Ignore the parent table */
+ if (childOID == relid)
+ continue;
+
+ childtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(childOID));
+ childclassForm = (Form_pg_class) GETSTRUCT(childtuple);
+
+ /* Skip foreign partitions */
+ if (childclassForm->relkind == RELKIND_FOREIGN_TABLE)
+ continue;
+
+ /* Fetch reloptions and the pgstat entry for this table */
+ relopts = extract_autovac_opts(childtuple, pg_class_desc);
+ tabentry = get_pgstat_tabentry_relid(childOID,
+ childclassForm->relisshared,
+ shared, dbentry);
+
+ relation_needs_vacanalyze(childOID, relopts, childclassForm, tabentry,
+ effective_multixact_freeze_max_age,
+ &dovacuum, &doanalyze, &wraparound);
+
+ if (dovacuum || doanalyze)
+ child_oids = lappend_oid(child_oids, childOID);
+ }
+
+ if (child_oids)
+ {
+ table_oids = lappend_oid(table_oids, relid);
+ table_oids = list_concat(table_oids, child_oids);
+ }
+ }
/*
* Remember TOAST associations for the second pass. Note: we must do
@@ -2725,6 +2788,7 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
Assert(((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_RELATION ||
((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW ||
+ ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_PARTITIONED_TABLE ||
((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE);
relopts = extractRelOptions(tup, pg_class_desc, NULL);
@@ -2798,33 +2862,79 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
return NULL;
classForm = (Form_pg_class) GETSTRUCT(classTup);
- /*
- * 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.
- */
- avopts = extract_autovac_opts(classTup, pg_class_desc);
- if (classForm->relkind == RELKIND_TOASTVALUE &&
- avopts == NULL && table_toast_map != NULL)
+ if (classForm->relkind != RELKIND_PARTITIONED_TABLE)
{
- av_relation *hentry;
- bool found;
+ /*
+ * 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.
+ */
+ avopts = extract_autovac_opts(classTup, pg_class_desc);
+ if (classForm->relkind == RELKIND_TOASTVALUE &&
+ avopts == NULL && table_toast_map != NULL)
+ {
+ av_relation *hentry;
+ bool found;
+
+ 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, avopts, classForm, tabentry,
+ effective_multixact_freeze_max_age,
+ &dovacuum, &doanalyze, &wraparound);
- hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found);
- if (found && hentry->ar_hasrelopts)
- avopts = &hentry->ar_reloptions;
+ /* ignore ANALYZE for toast tables */
+ if (classForm->relkind == RELKIND_TOASTVALUE)
+ doanalyze = false;
}
+ else
+ {
+ /*
+ * If the relation is partitioned and doesn't have any foreign tables
+ * we check its children again.
+ */
+ List *tableOIDs;
+ ListCell *lc;
+
+ /* Find all members of inheritance set taking AccessShareLock */
+ tableOIDs = find_all_inheritors(relid, AccessShareLock, NULL);
+
+ foreach(lc, tableOIDs)
+ {
+ Oid childOID = lfirst_oid(lc);
+ HeapTuple childtuple;
+ Form_pg_class childclassForm;
+
+ /* Ignore the parent table */
+ if (childOID == relid)
+ continue;
+
+ childtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(childOID));
+ childclassForm = (Form_pg_class) GETSTRUCT(childtuple);
+
+ /* Skip foreign partitions */
+ if (childclassForm->relkind == RELKIND_FOREIGN_TABLE)
+ continue;
- /* fetch the pgstat table entry */
- tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
- shared, dbentry);
+ /* Fetch reloptions and the pgstat entry */
+ avopts = extract_autovac_opts(childtuple, pg_class_desc);
+ tabentry = get_pgstat_tabentry_relid(childOID, childclassForm->relisshared,
+ shared, dbentry);
- relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
- effective_multixact_freeze_max_age,
- &dovacuum, &doanalyze, &wraparound);
+ relation_needs_vacanalyze(childOID, avopts, childclassForm, tabentry,
+ effective_multixact_freeze_max_age,
+ &dovacuum, &doanalyze, &wraparound);
- /* ignore ANALYZE for toast tables */
- if (classForm->relkind == RELKIND_TOASTVALUE)
- doanalyze = false;
+ /* Its parents need vacuum or analyze */
+ if (dovacuum || doanalyze)
+ break;
+ }
+ }
/* OK, it needs something done */
if (doanalyze || dovacuum)