Re: Publication DDL can race with a concurrent UPDATE - Mailing list pgsql-hackers

From vignesh C
Subject Re: Publication DDL can race with a concurrent UPDATE
Date
Msg-id CALDaNm0Aonwse0wjBkK2qrCi=OOR61UhqaEocFfPkTn_7SdfsA@mail.gmail.com
Whole thread
List pgsql-hackers
On Tue, 8 Sept 2026 at 10:54, Zhijie Hou (Fujitsu)
<houzj.fnst@fujitsu.com> wrote:
>
>
> On Monday, September 7, 2026 8:56 PM vignesh C <vignesh21@gmail.com> wrote:
> >
> > The underlying issue appears to be that publication DDL naming a table takes
> > ShareUpdateExclusiveLock, which does not conflict with the
> > RowExclusiveLock held by a concurrent UPDATE. This allows the publication
> > definition to change while the UPDATE is in progress: the UPDATE uses the old
> > definition when checking the operation and generating WAL, while logical
> > decoding uses the new definition.
>
> Thanks for reporting the issue.
>
> >
> > There may be other variations of this race ex: adding column list, but the
> > underlying problem is the same: an UPDATE can proceed based on stale
> > publication information and produce a logical replication change that is
> > rejected only later on the subscriber.
> >
> > I fixed the issue by changing the lock taken by publication DDL from
> > ShareUpdateExclusiveLock to ShareRowExclusiveLock. This makes the DDL
> > conflict with the RowExclusiveLock held by concurrent data-modifying
> > statements, preventing the publication definition from changing while the
> > UPDATE is in progress.
>
> I think this fix is not sufficient, as it does not address the ALTER PUBLICATION
> SET (options) cases, where the publication action can also be altered
> concurrently with DMLs, IIUC. The publication data in the relcache is also
> affected by pubaction changes, so those should be blocked as well.
>
> Addressing the above should be sufficient for the row filter and column list
> cases. However, for the replica identity check on UPDATE and DELETE operations,
> further analysis may be needed - especially for the TABLES IN SCHEMA and ALL
> TABLES cases, where tables are not explicitly published.

One approach could be:
For "TABLES IN SCHEMA", lock the schema's namespace OID.
LockSchemaList() already takes a lock on the schema to prevent DROP
SCHEMA, so upgrade it from AccessShareLock to ShareRowExclusiveLock.
This keeps the existing protection and also prevents concurrent
writers from racing with CREATE PUBLICATION ... FOR TABLES IN SCHEMA
and ALTER PUBLICATION ... ADD/SET TABLES IN SCHEMA. For "FOR ALL
TABLES", lock the pg_publication relation with ShareRowExclusiveLock.
Take this lock in CreatePublication() when enabling FOR ALL TABLES,
and in AlterPublicationAllFlags() when changing puballtables from
false to true. On the DML side(UPDATE and DELETE),
CheckCmdReplicaIdentity() takes a matching RowExclusiveLock on the
table's namespace and pg_publication before using the publication
descriptor. This is needed only for tables without a local replica
identity; tables with a replica identity or REPLICA IDENTITY FULL are
not affected by this race.
RowExclusiveLock is self-compatible, so normal concurrent DML does not
block other DML. It conflicts with the ShareRowExclusiveLock taken by
the publication DDL, ensuring that the DDL and DML cannot race.
The attached POC demonstrates the changes for the same.

Does this approach look reasonable, or is there a better way to handle
this synchronization?

Regards,
Vignesh

Attachment

pgsql-hackers by date:

Previous
From: Peter Eisentraut
Date:
Subject: Re: Silence -fsanitize=function where we cast function pointers on purpose
Next
From: Daria Lepikhova
Date:
Subject: Re: Incremental backups report progress as if they were full backups