Hello While working on bugfixes for FK problems in partitioned tables, I came across some behavior that appears to stem from our inclusion of foreign keys in relcache, without sufficient care for invalidating the relcache entries when the foreign key set for the table changes. (Namely, a partition retains its relcache entry with no FKs when an FK is added to the parent table, leading a DELETE to skip running action triggers).
At https://postgr.es/m/201901182216.nr5clsxrn624@alvherre.pgsql I posted a simplistic for the specific problem I found by calling CacheInvalidateRelcache in the problem spot. But I'm wondering if the correct fix isn't to have CacheInvalidateHeapTuple deal with FK pg_constraint tuples instead, per the attached patch. Why does this not lead to stale cache problems elsewhere? FKs were added to relcache entries by commit 100340e2dcd0 ("Restore foreign-key-aware estimation of join relation sizes"), so CCing Tom and Tomas. -- Álvaro Herrera PostgreSQL Expert, https://www.2ndQuadrant.com/
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c index 80d7a76e24..b9f698ef2c 100644 --- a/src/backend/utils/cache/inval.c +++ b/src/backend/utils/cache/inval.c @@ -52,10 +52,11 @@ * catcaches may need invalidation for a given tuple. * * Also, whenever we see an operation on a pg_class, pg_attribute, or * pg_index tuple, we register a relcache flush operation for the relation * described by that tuple (as specified in CacheInvalidateHeapTuple()). + * Likewise for pg_constraint tuples for foreign keys on relations. * * We keep the relcache flush requests in lists separate from the catcache * tuple flush requests. This allows us to issue all the pending catcache * flushes before we issue relcache flushes, which saves us from loading * a catcache tuple during relcache load only to flush it again right away. @@ -98,10 +99,11 @@ #include <limits.h> #include "access/htup_details.h" #include "access/xact.h" #include "catalog/catalog.h" +#include "catalog/pg_constraint.h" #include "miscadmin.h" #include "storage/sinval.h" #include "storage/smgr.h" #include "utils/catcache.h" #include "utils/inval.h" @@ -1201,10 +1203,27 @@ CacheInvalidateHeapTuple(Relation relation, * shared catalogs can't have such updates. */ relationId = indextup->indexrelid; databaseId = MyDatabaseId; } + else if (tupleRelId == ConstraintRelationId) + { + Form_pg_constraint constrtup = (Form_pg_constraint) GETSTRUCT(tuple); + + /* + * Foreign keys are part of relcache entries, too, so send out an + * inval for the table that the FK applies to. + */ + if (constrtup->contype == CONSTRAINT_FOREIGN && + OidIsValid(constrtup->conrelid)) + { + relationId = constrtup->conrelid; + databaseId = MyDatabaseId; + } + else + return; + } else return; /* * Yes. We need to register a relcache invalidation event.