pgsql: Avoid resetting subscription two_phase state to pending when alr

From: Amit Kapila <akapila(at)postgresql(dot)org>
To: pgsql-committers(at)lists(dot)postgresql(dot)org
Subject: pgsql: Avoid resetting subscription two_phase state to pending when alr
Date: 2026-11-05 04:25:46
Message-ID: E1xOp2p-00000000G71-0ren@gemulon.postgresql.org
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-committers

Avoid resetting subscription two_phase state to pending when already enabled.

ALTER SUBSCRIPTION ... SET (two_phase = true) always moved the
subscription's two_phase state to pending, even if two_phase was already
enabled. The publisher's slot still has two_phase enabled in that case,
and prepared transactions may already exist on the subscriber, so the
reset broke checks that rely on the enabled state:

1. A later SET (two_phase = false) no longer checked for existing prepared
transactions and disabled two_phase anyway. When those transactions were
committed on the publisher, their changes were re-sent as regular
transactions instead of COMMIT PREPARED, leading to self-deadlocks,
duplicate data, or orphaned prepared transactions.

2. REFRESH PUBLICATION (and SET/ADD PUBLICATION) with copy_data = true was
allowed even though the publisher's slot still had two_phase enabled. This
could stream prepared transactions during the initial sync of newly added
tables and apply them as empty prepares.

Fix this by leaving the state unchanged when two_phase is already enabled.

This problem was introduced by commit 1462aad2e44, which allowed altering
the two_phase option of a subscription.

This changes behavior when slot_name is altered together with SET
(two_phase = true) on an ENABLED subscription. Previously the state was
reset to PENDING and the apply worker enabled two_phase on the replacement
slot. Now the state stays ENABLED and the apply worker does not touch the
replacement slot's two_phase property. This is intentional: resetting the
state would reintroduce the problems described above, since prepared
transactions from the old slot may still exist on the subscriber. The
documentation already requires the replacement slot's two_phase property
to match the subscription option.

Author: Zhijie Hou <houzhijie22(at)gmail(dot)com>
Reviewed-by: shveta malik <shveta(dot)malik(at)gmail(dot)com>
Reviewed-by: Hayato Kuroda <kuroda(dot)hayato(at)fujitsu(dot)com>
Reviewed-by: Bertrand Drouvot <bertranddrouvot(dot)pg(at)gmail(dot)com>
Reviewed-by: vignesh C <vignesh21(at)gmail(dot)com>
Reviewed-by: Peter Smith <smithpb2250(at)gmail(dot)com>
Reviewed-by: Amit Kapila <amit(dot)kapila16(at)gmail(dot)com>
Discussion: https://postgr.es/m/8e85abdf-738b-4578-ac42-4dc8eaa061dd@gmail.com
Backpatch-through: 18

Branch
------
REL_19_STABLE

Details
-------
https://git.postgresql.org/pg/commitdiff/1e1a4821a9efefe4a813932d1510c6704938f713

Modified Files
--------------
src/backend/commands/subscriptioncmds.c | 19 +++++++++++++------
src/test/subscription/t/021_twophase.pl | 16 +++++++++++++---
2 files changed, 26 insertions(+), 9 deletions(-)

Browse pgsql-committers by date

  From Date Subject
Next Message Amit Langote 2026-11-05 07:31:27 pgsql: SQL/JSON: Fix NULL result leakage in JSON_VALUE() RETURNING json
Previous Message Michael Paquier 2026-11-05 00:14:01 pgsql: Add stats kind filter to pgstat_drop_matching_entries()