=== Applying patches on top of PostgreSQL commit ID 5987553fde2cb22b69cf9c7d71a92573b7a3b7c9 === /etc/rc.d/jail: WARNING: Per-jail configuration via jail_* variables is obsolete. Please consider migrating to /etc/jail.conf. Sat May 17 13:58:21 UTC 2025 On branch cf/5664 nothing to commit, working tree clean === using 'git am' to apply patch ./v14-0001-PG17-Approach-3-Fix-slot-synchronization-for-two.patch === Applying: PG17 Approach 3 Fix slot synchronization for two_phase enables slots. Using index info to reconstruct a base tree... M doc/src/sgml/func.sgml M doc/src/sgml/protocol.sgml M doc/src/sgml/ref/alter_subscription.sgml M doc/src/sgml/ref/create_subscription.sgml M src/backend/commands/subscriptioncmds.c M src/backend/replication/logical/logical.c M src/backend/replication/logical/slotsync.c M src/backend/replication/slot.c M src/bin/pg_upgrade/t/003_logical_slots.pl M src/test/recovery/t/040_standby_failover_slots_sync.pl M src/test/regress/expected/subscription.out M src/test/regress/sql/subscription.sql Falling back to patching base and 3-way merge... Auto-merging src/test/regress/sql/subscription.sql Auto-merging src/test/regress/expected/subscription.out Auto-merging src/test/recovery/t/040_standby_failover_slots_sync.pl Auto-merging src/bin/pg_upgrade/t/003_logical_slots.pl Auto-merging src/backend/replication/slot.c CONFLICT (content): Merge conflict in src/backend/replication/slot.c Auto-merging src/backend/replication/logical/slotsync.c Auto-merging src/backend/replication/logical/logical.c Auto-merging src/backend/commands/subscriptioncmds.c CONFLICT (content): Merge conflict in src/backend/commands/subscriptioncmds.c Auto-merging doc/src/sgml/ref/create_subscription.sgml Auto-merging doc/src/sgml/ref/alter_subscription.sgml CONFLICT (content): Merge conflict in doc/src/sgml/ref/alter_subscription.sgml Auto-merging doc/src/sgml/protocol.sgml Auto-merging doc/src/sgml/func.sgml error: Failed to merge in the changes. hint: Use 'git am --show-current-patch=diff' to see the failed patch Patch failed at 0001 PG17 Approach 3 Fix slot synchronization for two_phase enables slots. When you have resolved this problem, run "git am --continue". If you prefer to skip this patch, run "git am --skip" instead. To restore the original branch and stop patching, run "git am --abort". Unstaged changes after reset: M contrib/test_decoding/expected/slot.out M contrib/test_decoding/sql/slot.sql M doc/src/sgml/func.sgml M doc/src/sgml/protocol.sgml M doc/src/sgml/ref/alter_subscription.sgml M doc/src/sgml/ref/create_subscription.sgml M src/backend/commands/subscriptioncmds.c M src/backend/replication/logical/logical.c M src/backend/replication/logical/slotsync.c M src/backend/replication/slot.c M src/bin/pg_upgrade/t/003_logical_slots.pl M src/test/recovery/t/040_standby_failover_slots_sync.pl M src/test/regress/expected/subscription.out M src/test/regress/sql/subscription.sql === using patch(1) to apply patch ./v14-0001-PG17-Approach-3-Fix-slot-synchronization-for-two.patch === patching file contrib/test_decoding/expected/slot.out patching file contrib/test_decoding/sql/slot.sql patching file doc/src/sgml/func.sgml Hunk #1 succeeded at 29768 (offset 692 lines). patching file doc/src/sgml/protocol.sgml Hunk #1 succeeded at 2266 (offset 204 lines). Hunk #2 succeeded at 2316 (offset 204 lines). patching file doc/src/sgml/ref/alter_subscription.sgml Hunk #1 succeeded at 285 with fuzz 2 (offset 28 lines). patching file doc/src/sgml/ref/create_subscription.sgml Hunk #1 succeeded at 361 (offset 7 lines). Hunk #2 succeeded at 441 (offset 7 lines). patching file src/backend/commands/subscriptioncmds.c Hunk #1 succeeded at 608 (offset -40 lines). Hunk #2 succeeded at 1270 with fuzz 2 (offset 13 lines). patching file src/backend/replication/logical/logical.c Hunk #1 succeeded at 594 (offset -19 lines). patching file src/backend/replication/logical/slotsync.c patching file src/backend/replication/slot.c Hunk #1 succeeded at 358 (offset 15 lines). Hunk #2 FAILED at 862. 1 out of 2 hunks FAILED -- saving rejects to file src/backend/replication/slot.c.rej patching file src/bin/pg_upgrade/t/003_logical_slots.pl patching file src/test/recovery/t/040_standby_failover_slots_sync.pl Hunk #1 succeeded at 29 with fuzz 1 (offset 4 lines). Hunk #2 succeeded at 49 (offset 5 lines). Hunk #3 succeeded at 108 with fuzz 1 (offset 6 lines). patching file src/test/regress/expected/subscription.out Hunk #1 succeeded at 476 (offset -3 lines). patching file src/test/regress/sql/subscription.sql Hunk #1 succeeded at 339 (offset -3 lines). Unstaged changes after reset: M contrib/test_decoding/expected/slot.out M contrib/test_decoding/sql/slot.sql M doc/src/sgml/func.sgml M doc/src/sgml/protocol.sgml M doc/src/sgml/ref/alter_subscription.sgml M doc/src/sgml/ref/create_subscription.sgml M src/backend/commands/subscriptioncmds.c M src/backend/replication/logical/logical.c M src/backend/replication/logical/slotsync.c M src/backend/replication/slot.c M src/bin/pg_upgrade/t/003_logical_slots.pl M src/test/recovery/t/040_standby_failover_slots_sync.pl M src/test/regress/expected/subscription.out M src/test/regress/sql/subscription.sql Removing src/backend/replication/slot.c.rej === using 'git apply' to apply patch ./v14-0001-PG17-Approach-3-Fix-slot-synchronization-for-two.patch === Applied patch to 'contrib/test_decoding/expected/slot.out' cleanly. Applied patch to 'contrib/test_decoding/sql/slot.sql' cleanly. Applied patch to 'doc/src/sgml/func.sgml' cleanly. Applied patch to 'doc/src/sgml/protocol.sgml' cleanly. Applied patch to 'doc/src/sgml/ref/alter_subscription.sgml' with conflicts. Applied patch to 'doc/src/sgml/ref/create_subscription.sgml' cleanly. Applied patch to 'src/backend/commands/subscriptioncmds.c' with conflicts. Applied patch to 'src/backend/replication/logical/logical.c' cleanly. Applied patch to 'src/backend/replication/logical/slotsync.c' cleanly. Applied patch to 'src/backend/replication/slot.c' with conflicts. Applied patch to 'src/bin/pg_upgrade/t/003_logical_slots.pl' cleanly. Applied patch to 'src/test/recovery/t/040_standby_failover_slots_sync.pl' cleanly. Applied patch to 'src/test/regress/expected/subscription.out' cleanly. Applied patch to 'src/test/regress/sql/subscription.sql' cleanly. U doc/src/sgml/ref/alter_subscription.sgml U src/backend/commands/subscriptioncmds.c U src/backend/replication/slot.c diff --cc doc/src/sgml/ref/alter_subscription.sgml index fdc648d007f,afa29d4049b..00000000000 --- a/doc/src/sgml/ref/alter_subscription.sgml +++ b/doc/src/sgml/ref/alter_subscription.sgml @@@ -261,29 -259,15 +261,41 @@@ ALTER SUBSCRIPTION failover + and two_phase + parameters can only be altered when the subscription is disabled. + + + + When altering two_phase + from true to false, the backend + process reports an error if any prepared transactions done by the + logical replication worker (from when two_phase + parameter was still true) are found. You can resolve + prepared transactions on the publisher node, or manually roll back them + on the subscriber, and then try again. The transactions prepared by + logical replication worker corresponding to a particular subscription have + the following pattern: pg_gid_%u_%u + (parameters: subscription oid, remote transaction id xid). + To resolve such transactions manually, you need to roll back all + the prepared transactions with corresponding subscription IDs in their + names. Applications can check + pg_prepared_xacts + to find the required prepared transactions. After the two_phase + option is changed from true to false, + the publisher will replicate the transactions again when they are committed. ++======= + When a subscription's + two_phase + is in the pending state, setting failover to true is not + permitted. Once + two_phase + is enabled, + failover + can be set to true only after the slot has consumed all the changes up to the + point where two-phase decoding was enabled. ++>>>>>>> theirs diff --cc src/backend/commands/subscriptioncmds.c index 4aec73bcc6b,54afb3f2e42..00000000000 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@@ -1244,81 -1237,44 +1256,99 @@@ AlterSubscription(ParseState *pstate, A replaces[Anum_pg_subscription_subrunasowner - 1] = true; } - if (IsSet(opts.specified_opts, SUBOPT_FAILOVER)) + if (IsSet(opts.specified_opts, SUBOPT_TWOPHASE_COMMIT)) { - if (!sub->slotname) + /* + * We need to update both the slot and the subscription + * for the two_phase option. We can enable the two_phase + * option for a slot only once the initial data + * synchronization is done. This is to avoid missing some + * data as explained in comments atop worker.c. + */ + update_two_phase = !opts.twophase; + + CheckAlterSubOption(sub, "two_phase", update_two_phase, + isTopLevel); + + /* + * Modifying the two_phase slot option requires a slot + * lookup by slot name, so changing the slot name at the + * same time is not allowed. + */ + if (update_two_phase && + IsSet(opts.specified_opts, SUBOPT_SLOT_NAME)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("slot_name and two_phase cannot be altered at the same time"))); + + /* + * Note that workers may still survive even if the + * subscription has been disabled. + * + * Ensure workers have already been exited to avoid + * getting prepared transactions while we are disabling + * the two_phase option. Otherwise, the changes of an + * already prepared transaction can be replicated again + * along with its corresponding commit, leading to + * duplicate data or errors. + */ + if (logicalrep_workers_find(subid, true, true)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("cannot set option \"%s\" for a subscription that does not have a slot name", - "failover"))); + errmsg("cannot alter two_phase when logical replication worker is still running"), + errhint("Try again after some time."))); /* - * Do not allow changing the failover state if the - * subscription is enabled. This is because the failover - * state of the slot on the publisher cannot be modified - * if the slot is currently acquired by the apply worker. + * two_phase cannot be disabled if there are any + * uncommitted prepared transactions present otherwise it + * can lead to duplicate data or errors as explained in + * the comment above. */ - if (sub->enabled) + if (update_two_phase && + sub->twophasestate == LOGICALREP_TWOPHASE_STATE_ENABLED && + LookupGXactBySubid(subid)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("cannot set %s for enabled subscription", - "failover"))); + errmsg("cannot disable two_phase when prepared transactions are present"), + errhint("Resolve these transactions and try again."))); + + /* Change system catalog accordingly */ + values[Anum_pg_subscription_subtwophasestate - 1] = + CharGetDatum(opts.twophase ? + LOGICALREP_TWOPHASE_STATE_PENDING : + LOGICALREP_TWOPHASE_STATE_DISABLED); + replaces[Anum_pg_subscription_subtwophasestate - 1] = true; + } + if (IsSet(opts.specified_opts, SUBOPT_FAILOVER)) + { /* ++<<<<<<< ours + * Similar to the two_phase case above, we need to update + * the failover option for both the slot and the + * subscription. ++======= + * Do not allow users to enable the failover until + * two_phase state reaches READY state. See comments atop + * slotsync.c for details. + */ + if (sub->twophasestate == LOGICALREP_TWOPHASE_STATE_PENDING && + opts.failover) + ereport(ERROR, + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("cannot enable failover for a subscription with a pending two_phase state"), + errhint("The \"%s\" option can be enabled after \"%s\" state is ready.", + "failover", "two_phase")); + + /* + * The changed failover option of the slot can't be rolled + * back. ++>>>>>>> theirs */ - PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION ... SET (failover)"); + update_failover = true; + + CheckAlterSubOption(sub, "failover", update_failover, + isTopLevel); values[Anum_pg_subscription_subfailover - 1] = BoolGetDatum(opts.failover); diff --cc src/backend/replication/slot.c index 600b87fa9cb,f5f2bcc708b..00000000000 --- a/src/backend/replication/slot.c +++ b/src/backend/replication/slot.c @@@ -873,38 -853,34 +887,55 @@@ ReplicationSlotAlter(const char *name, " on the standby")); } - /* - * Do not allow users to enable failover for temporary slots as we do not - * support syncing temporary slots to the standby. - */ - if (failover && MyReplicationSlot->data.persistency == RS_TEMPORARY) - ereport(ERROR, - errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot enable failover for a temporary replication slot")); + if (failover) + { + /* + * Do not allow users to enable failover for temporary slots as we do + * not support syncing temporary slots to the standby. + */ + if (*failover && MyReplicationSlot->data.persistency == RS_TEMPORARY) + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot enable failover for a temporary replication slot")); ++<<<<<<< ours + if (MyReplicationSlot->data.failover != *failover) + { + SpinLockAcquire(&MyReplicationSlot->mutex); + MyReplicationSlot->data.failover = *failover; + SpinLockRelease(&MyReplicationSlot->mutex); + + update_slot = true; + } + } + + if (two_phase && MyReplicationSlot->data.two_phase != *two_phase) ++======= + /* + * Do not allow users to enable failover for a two_phase enabled slot if + * there are potentially un-decoded transactions that are prepared before + * two_phase_at. See comments atop slotsync.c for details. + */ + if (failover && MyReplicationSlot->data.two_phase && + MyReplicationSlot->data.restart_lsn < MyReplicationSlot->data.two_phase_at) + ereport(ERROR, + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("cannot enable failover for a two-phase enabled replication slot due to unconsumed changes"), + errdetail("The slot need to consume change upto %X/%X to enable failover.", + LSN_FORMAT_ARGS(MyReplicationSlot->data.two_phase_at))); + + if (MyReplicationSlot->data.failover != failover) ++>>>>>>> theirs { SpinLockAcquire(&MyReplicationSlot->mutex); - MyReplicationSlot->data.failover = failover; + MyReplicationSlot->data.two_phase = *two_phase; SpinLockRelease(&MyReplicationSlot->mutex); + update_slot = true; + } + + if (update_slot) + { ReplicationSlotMarkDirty(); ReplicationSlotSave(); }