RE: Test publication.sql racing with subscription.sql might fail due to cache lookup error

From: "Hayato Kuroda (Fujitsu)" <kuroda(dot)hayato(at)fujitsu(dot)com>
To: 'vignesh C' <vignesh21(at)gmail(dot)com>, Zhijie Hou <houzhijie22(at)gmail(dot)com>
Cc: Alexander Lakhin <exclusion(at)gmail(dot)com>, pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: RE: Test publication.sql racing with subscription.sql might fail due to cache lookup error
Date: 2026-10-14 07:30:12
Message-ID: OS7PR01MB183173B9EEBBE4188BF54C550F59D2@OS7PR01MB18317.jpnprd01.prod.outlook.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Dear Vignesh,

> The attached v2 version patch has the changes for the same. This also
> addresses hou-san comment of not requiring a new function from [1].

Thanks for updating the patch. I've been thinking that publication_add_relation()
can really cause the cache lookup failure. The caller PublicationAddTables()
acquires the ShareUpdateExclusiveLock for the expressly specified relations via
OpenTableList(), the issue cannot happen for these targets. So I feel it might be
OK not to raise an ERROR in this case.

FYI, I did several experiments, and found that cache lookup failure can happen
with the below schema definition.

```
root // ADD TABLE
├── mid1
│ └── victim // to be dropped
└── mid2
└── trigger
└── other
```

When the parent table is specified by the ALTER PUB command, all {grand}children
are gathered via find_all_inheritors(NoLock). The cache miss cannot happen if the
the direct child tables are dropped, because the DROP command will acquire the
AccessExclusive lock. It will conflict with the ALTER PUB command, so these commands
can be serialized.

For the grandchild tables, the DROP does not take care the root thus the concurrent
operation can happen. For reproducing it, find_all_inheritors() must gather the
OID which is concurrently dropped then invalidate its relcache. Above schema
definition is reachable with the concurrent DROP TABLE victim command. It's too
complex to provide a reproducer by the injection point though.

Below briefly shows loops in find_all_inheritors(), and the outer loop can seek
all relations. If drop is done after the OID is appended to the list and next
iteration checks the pg_inherit in find_inheritance_children(), the relcache for
victim can be invalidated.

```
foreach(l, rels_list)
{
Oid currentrel = lfirst_oid(l);
List *currentchildren;
ListCell *lc;

/* Get the direct children of this rel */
currentchildren = find_inheritance_children(currentrel, lockmode); // (1)
...
foreach(lc, currentchildren)
{
...
rels_list = lappend_oid(rels_list, child_oid); // (2)
}

// do DROP TABLE victim; after currentrel points the victim here.
// Must be done after the victim is appended at (2).
// Upcoming (1) acquires a lock for pg_inherits, and a relcache for victim
// can be invalidated.
}
```

Above meant the cache mis rarely happens. Do we need a guard for the case?

Best regards,
Hayato Kuroda
FUJITSU LIMITED

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Takeshi Ideriha 2026-10-14 07:32:31 Re: Incorrect CONTEXT reported for errors from parallel apply worker in logical replication
Previous Message David Rowley 2026-10-14 07:27:53 Re: Reducing relcache memory usage: deduping index shapes