From: Tom Lane Date: Tue, 1 Jul 2008 02:09:34 +0000 (+0000) Subject: Teach autovacuum how to determine whether a temp table belongs to a crashed X-Git-Tag: REL8_4_BETA1~1221 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5b965bf08bfb4aa8928bafaed20e42b89de02a5c;p=postgresql Teach autovacuum how to determine whether a temp table belongs to a crashed backend. If so, send a LOG message to the postmaster log, and if the table is beyond the vacuum-for-wraparound horizon, forcibly drop it. Per recent discussions. Perhaps we ought to back-patch this, but it probably needs to age a bit in HEAD first. --- diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 5bacb412a9..dfaea41cfe 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -13,7 +13,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.106 2008/06/19 00:46:04 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.107 2008/07/01 02:09:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2209,6 +2209,32 @@ isOtherTempNamespace(Oid namespaceId) return isAnyTempNamespace(namespaceId); } +/* + * GetTempNamespaceBackendId - if the given namespace is a temporary-table + * namespace (either my own, or another backend's), return the BackendId + * that owns it. Temporary-toast-table namespaces are included, too. + * If it isn't a temp namespace, return -1. + */ +int +GetTempNamespaceBackendId(Oid namespaceId) +{ + int result; + char *nspname; + + /* See if the namespace name starts with "pg_temp_" or "pg_toast_temp_" */ + nspname = get_namespace_name(namespaceId); + if (!nspname) + return -1; /* no such namespace? */ + if (strncmp(nspname, "pg_temp_", 8) == 0) + result = atoi(nspname + 8); + else if (strncmp(nspname, "pg_toast_temp_", 14) == 0) + result = atoi(nspname + 14); + else + result = -1; + pfree(nspname); + return result; +} + /* * GetTempToastNamespace - get the OID of my temporary-toast-table namespace, * which must already be assigned. (This is only used when creating a toast diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 7ac0bae1c5..9ed34eed6f 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -55,7 +55,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.79 2008/06/05 15:47:32 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.80 2008/07/01 02:09:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -71,6 +71,7 @@ #include "access/heapam.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" @@ -90,7 +91,7 @@ #include "storage/pmsignal.h" #include "storage/proc.h" #include "storage/procarray.h" -#include "storage/sinval.h" +#include "storage/sinvaladt.h" #include "tcop/tcopprot.h" #include "utils/flatfiles.h" #include "utils/fmgroids.h" @@ -275,10 +276,6 @@ static void autovac_balance_cost(void); static void do_autovacuum(void); static void FreeWorkerInfo(int code, Datum arg); -static void relation_check_autovac(Oid relid, Form_pg_class classForm, - Form_pg_autovacuum avForm, PgStat_StatTabEntry *tabentry, - List **table_oids, List **table_toast_list, - List **toast_oids); static autovac_table *table_recheck_autovac(Oid relid); static void relation_needs_vacanalyze(Oid relid, Form_pg_autovacuum avForm, Form_pg_class classForm, @@ -1912,19 +1909,16 @@ do_autovacuum(void) PgStat_StatTabEntry *tabentry; HeapTuple avTup; Oid relid; + bool dovacuum; + bool doanalyze; + bool wraparound; + int backendID; /* Consider only regular and toast tables. */ if (classForm->relkind != RELKIND_RELATION && classForm->relkind != RELKIND_TOASTVALUE) continue; - /* - * Skip temp tables (i.e. those in temp namespaces). We cannot safely - * process other backends' temp tables. - */ - if (isAnyTempNamespace(classForm->relnamespace)) - continue; - relid = HeapTupleGetOid(tuple); /* Fetch the pg_autovacuum tuple for the relation, if any */ @@ -1936,8 +1930,76 @@ do_autovacuum(void) tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared, shared, dbentry); - relation_check_autovac(relid, classForm, avForm, tabentry, - &table_oids, &table_toast_list, &toast_oids); + /* Check if it needs vacuum or analyze */ + relation_needs_vacanalyze(relid, avForm, classForm, tabentry, + &dovacuum, &doanalyze, &wraparound); + + /* + * Check if it is a temp table (presumably, of some other backend's). + * We cannot safely process other backends' temp tables. + */ + backendID = GetTempNamespaceBackendId(classForm->relnamespace); + + if (backendID > 0) + { + /* We just ignore it if the owning backend is still active */ + if (backendID == MyBackendId || !BackendIdIsActive(backendID)) + { + /* + * We found an orphan temp table (which was probably left + * behind by a crashed backend). If it's so old as to need + * vacuum for wraparound, forcibly drop it. Otherwise just + * log a complaint. + */ + if (wraparound && classForm->relkind == RELKIND_RELATION) + { + ObjectAddress object; + + ereport(LOG, + (errmsg("autovacuum: dropping orphan temp table \"%s\".\"%s\" in database \"%s\"", + get_namespace_name(classForm->relnamespace), + NameStr(classForm->relname), + get_database_name(MyDatabaseId)))); + object.classId = RelationRelationId; + object.objectId = relid; + object.objectSubId = 0; + performDeletion(&object, DROP_CASCADE); + } + else + { + ereport(LOG, + (errmsg("autovacuum: found orphan temp table \"%s\".\"%s\" in database \"%s\"", + get_namespace_name(classForm->relnamespace), + NameStr(classForm->relname), + get_database_name(MyDatabaseId)))); + } + } + } + else if (classForm->relkind == RELKIND_RELATION) + { + /* Plain relations that need work are added to table_oids */ + if (dovacuum || doanalyze) + table_oids = lappend_oid(table_oids, relid); + else if (OidIsValid(classForm->reltoastrelid)) + { + /* + * If it doesn't appear to need vacuuming, but it has a toast + * table, remember the association to revisit below. + */ + av_relation *rel = palloc(sizeof(av_relation)); + + rel->ar_relid = relid; + rel->ar_toastrelid = classForm->reltoastrelid; + + table_toast_list = lappend(table_toast_list, rel); + } + } + else + { + /* TOAST relations that need vacuum are added to toast_oids */ + if (dovacuum) + toast_oids = lappend_oid(toast_oids, relid); + } if (HeapTupleIsValid(avTup)) heap_freetuple(avTup); @@ -2231,56 +2293,6 @@ get_pgstat_tabentry_relid(Oid relid, bool isshared, PgStat_StatDBEntry *shared, return tabentry; } -/* - * relation_check_autovac - * - * For a given relation (either a plain table or TOAST table), check whether it - * needs vacuum or analyze. - * - * Plain tables that need either are added to the table_list. TOAST tables - * that need vacuum are added to toast_list. Plain tables that don't need - * either but which have a TOAST table are added, as a struct, to - * table_toast_list. The latter is to allow appending the OIDs of the plain - * tables whose TOAST table needs vacuuming into the plain tables list, which - * allows us to substantially reduce the number of "rechecks" that we need to - * do later on. - */ -static void -relation_check_autovac(Oid relid, Form_pg_class classForm, - Form_pg_autovacuum avForm, PgStat_StatTabEntry *tabentry, - List **table_oids, List **table_toast_list, - List **toast_oids) -{ - bool dovacuum; - bool doanalyze; - bool dummy; - - relation_needs_vacanalyze(relid, avForm, classForm, tabentry, - &dovacuum, &doanalyze, &dummy); - - if (classForm->relkind == RELKIND_TOASTVALUE) - { - if (dovacuum) - *toast_oids = lappend_oid(*toast_oids, relid); - } - else - { - Assert(classForm->relkind == RELKIND_RELATION); - - if (dovacuum || doanalyze) - *table_oids = lappend_oid(*table_oids, relid); - else if (OidIsValid(classForm->reltoastrelid)) - { - av_relation *rel = palloc(sizeof(av_relation)); - - rel->ar_relid = relid; - rel->ar_toastrelid = classForm->reltoastrelid; - - *table_toast_list = lappend(*table_toast_list, rel); - } - } -} - /* * table_recheck_autovac * diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c index e414e4a507..8aa4ec836b 100644 --- a/src/backend/storage/ipc/sinvaladt.c +++ b/src/backend/storage/ipc/sinvaladt.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.72 2008/06/20 00:24:53 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.73 2008/07/01 02:09:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -357,6 +357,33 @@ CleanupInvalidationState(int status, Datum arg) LWLockRelease(SInvalWriteLock); } +/* + * BackendIdIsActive + * Test if the given backend ID is currently assigned to a process. + */ +bool +BackendIdIsActive(int backendID) +{ + bool result; + SISeg *segP = shmInvalBuffer; + + /* Need to lock out additions/removals of backends */ + LWLockAcquire(SInvalWriteLock, LW_SHARED); + + if (backendID > 0 && backendID <= segP->lastBackend) + { + ProcState *stateP = &segP->procState[backendID - 1]; + + result = (stateP->procPid != 0); + } + else + result = false; + + LWLockRelease(SInvalWriteLock); + + return result; +} + /* * SIInsertDataEntries * Add new invalidation message(s) to the buffer. diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h index 7c9ad7c735..b161b7108b 100644 --- a/src/include/catalog/namespace.h +++ b/src/include/catalog/namespace.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.53 2008/01/01 19:45:56 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.54 2008/07/01 02:09:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -95,6 +95,7 @@ extern bool isTempToastNamespace(Oid namespaceId); extern bool isTempOrToastNamespace(Oid namespaceId); extern bool isAnyTempNamespace(Oid namespaceId); extern bool isOtherTempNamespace(Oid namespaceId); +extern int GetTempNamespaceBackendId(Oid namespaceId); extern Oid GetTempToastNamespace(void); extern void ResetTempTableNamespace(void); diff --git a/src/include/storage/sinvaladt.h b/src/include/storage/sinvaladt.h index 1748f8821b..abf243601c 100644 --- a/src/include/storage/sinvaladt.h +++ b/src/include/storage/sinvaladt.h @@ -15,7 +15,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/storage/sinvaladt.h,v 1.48 2008/06/19 21:32:56 tgl Exp $ + * $PostgreSQL: pgsql/src/include/storage/sinvaladt.h,v 1.49 2008/07/01 02:09:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -30,6 +30,7 @@ extern Size SInvalShmemSize(void); extern void CreateSharedInvalidationState(void); extern void SharedInvalBackendInit(void); +extern bool BackendIdIsActive(int backendID); extern void SIInsertDataEntries(const SharedInvalidationMessage *data, int n); extern int SIGetDataEntries(SharedInvalidationMessage *data, int datasize);