RE: Per-table resync for logical replication subscriptions

From: "Hayato Kuroda (Fujitsu)" <kuroda(dot)hayato(at)fujitsu(dot)com>
To: 'Cagri Biroglu' <cagri(dot)biroglu(at)adyen(dot)com>
Cc: Ajin Cherian <itsajin(at)gmail(dot)com>, "smithpb2250(at)gmail(dot)com" <smithpb2250(at)gmail(dot)com>, "pgsql-hackers(at)lists(dot)postgresql(dot)org" <pgsql-hackers(at)lists(dot)postgresql(dot)org>, Masahiko Sawada <sawada(dot)mshk(at)gmail(dot)com>
Subject: RE: Per-table resync for logical replication subscriptions
Date: 2026-09-24 08:43:41
Message-ID: TY5PR01MB183149575EEA744B1CFA8BA6DF5812@TY5PR01MB18314.jpnprd01.prod.outlook.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Dear Cagri,

Thanks for updating the patch! Few comments:

01.
There seems to be a corner issue for partition handling. Existing code ensures to
lock the parittion root first then lock the children. However, if both the parent
and its child is in the pg_subscription_rel and DBA runs the command like below,
the ordering of the lock can be opposite. This situation can happen if the
schema definition is difference between the publisher and subscriber side.

ALTER SUBSCRIPTION sub REFRESH TABLE table_child, table_root;

I showed a reproducer in [1], but I think it seldom happen. We may have to
re-order relids to be the partition root first if we want to fix.

02:

```
+ /*
+ * The local copy is discarded below, so require the same privilege
+ * TRUNCATE itself would. This runs here, rather than with the other
+ * two truncate checks further down, so that the command is rejected
+ * before it escalates any lock. As in ExecuteTruncate(), the
+ * privilege is required of the relations named in the command, not of
+ * the partitions that come with them.
+ */
+ userrel = table_open(relid, NoLock);
+ truncate_check_perms(relid, userrel->rd_rel);
+ table_close(userrel, NoLock);
```
According to a comment atop objectNamesToOids(), REVOKE command only acquires the
AccessShareLock. So I think the concurrent DDL can revoke a TRUNCATE privilege
after the truncate_check_perms(). IIUC the check should be done after acquiring
the AccessExclusive.

[1]:
Setup
=====
Publisher definition:
CREATE TABLE test_p (id int);
CREATE TABLE test_c (id int);
CREATE PUBLICATION pub_p FOR TABLE test_p;
CREATE PUBLICATION pub_c FOR TABLE test_c;

Subscriber definition:
CREATE TABLE test_p (id int) PARTITION BY RANGE (id);
CREATE TABLE test_c PARTITION OF test_p FOR VALUES FROM (-100) TO (0);
CREATE SUBSCRIPTION sub CONNECTION '{pub_connstr}' PUBLICATION pub_p, pub_c;

With above setup test_p and test_c can be in the pg_subscription_rel.

```
subscriber=# \d
List of relations
Schema | Name | Type | Owner
--------+--------+-------------------+----------
public | test_c | table | postgres
public | test_p | partitioned table | postgres
(2 rows)

subscriber=# SELECT * FROM pg_subscription_rel ;
srsubid | srrelid | srsubstate | srsublsn
---------+---------+------------+------------
16390 | 16384 | r | 0/0177F038
16390 | 16387 | r | 0/0177F038
(2 rows)
```

Workload
========
0. Disable a subscription
1. Establish a connection to the subscriber
2. Attach the backend via gdb, and add a break on AlterSubscription_refresh_table
3. Run `ALTER SUBSCRIPTION sub REFRESH test_c, test_p`
4. Proceed steps on gdb and stop just after acquiring an AccessExclusiveLock for test_c.
5. Establish another connection to the subscriber
6. Run `INSERT INTO test_p VALUES (-1);`. It would wait acquiring a lock
7. detach from the first backend process.
8. Deadlock could happen between two backends.

Best regards,
Hayato Kuroda
FUJITSU LIMITED

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message vignesh C 2026-09-24 09:18:15 Re: Include schema-qualified names in publication error messages.
Previous Message Antonin Houska 2026-09-24 08:41:27 Re: REPACK (CONCURRENTLY) can silently lose updates when the toast table is rewritten