From b0a7e8cbd7be1d9e22a0d389f53c0ff9c683ec68 Mon Sep 17 00:00:00 2001 From: Vignesh C Date: Fri, 30 Oct 2026 18:25:52 +0530 Subject: [PATCH v3] Avoid cache lookup failure for concurrently dropped relations InvalidatePublicationRels() is given relation OIDs that its callers collect without holding a lock on the relations: DROP PUBLICATION (both the FOR TABLE and the FOR TABLES IN SCHEMA cases), ALTER PUBLICATION ... SET (...), ALTER PUBLICATION ... ADD TABLES IN SCHEMA, and ALTER PUBLICATION ... ADD TABLE of a partitioned table. A partition or schema table can be dropped after the list was built, leaving its OID in the list. Invalidating the relcache for such a relation then fails with "cache lookup failed for relation". The last case looks safe, because ADD TABLE locks the table, but it locks only the table: OpenTableList() does not lock the partitions of a partitioned table, and the partitions are collected afterwards with find_all_inheritors(NoLock). Dropping a partition takes AccessExclusiveLock on the partition and its direct parent, so a direct child of the published table conflicts with the command through the lock on the table, but a grandchild does not, and can be dropped while the list is being built. Skip relations that no longer exist in InvalidatePublicationRels(), for all callers. Also continue with the remaining relations, instead of stopping, so that they are still invalidated. --- src/backend/commands/publicationcmds.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c index 301050104dd..a1882b987cb 100644 --- a/src/backend/commands/publicationcmds.c +++ b/src/backend/commands/publicationcmds.c @@ -1207,6 +1207,13 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt, /* * Invalidate the relations. + * + * A relation that no longer exists is ignored. The callers build the list + * without holding a lock that prevents its relations from being dropped, so + * one of them can be dropped concurrently after the list was built. That + * includes ALTER PUBLICATION ... ADD TABLE of a partitioned table: it locks + * the table, but not its partitions, and dropping a partition that is not a + * direct child of the table takes no lock on the table. */ void InvalidatePublicationRels(List *relids) @@ -1217,10 +1224,18 @@ InvalidatePublicationRels(List *relids) */ if (list_length(relids) < MAX_RELCACHE_INVAL_MSGS) { - ListCell *lc; + foreach_oid(relid, relids) + { + HeapTuple tup; - foreach(lc, relids) - CacheInvalidateRelcacheByRelid(lfirst_oid(lc)); + tup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); + /* dropped concurrently */ + if (!HeapTupleIsValid(tup)) + continue; + + CacheInvalidateRelcacheByTuple(tup); + ReleaseSysCache(tup); + } } else CacheInvalidateRelcacheAll(); -- 2.55.0