Hi TAP test codes are added in the patch and manual has become more user-friendly.
Please review the patch and let me know if you see a better solution. I’ll be glad to update it accordingly. ioseph On Wed, Dec 10, 2025 at 06:03:48PM +0900, Ioseph Kim wrote: > Hi > > A failover option has been added to the CREATE SUBSCRITION command, but this > functionality isn't easily accessible using the pg_createsubscriber tool. > > Subscriptions created using pg_createsubscriber must be configured for > failover via an alter operation. > > To address this issue, we've added a simple --enable-failover option to the > pg_createsubscriber tool. > > This patch is simple. It doesn't handle exceptions or provide any TAP test > code. > > Please review this and we hope the development team will refine it further. > > ioseph
>From d9403728478057bd8d033a50d102a8db52af13a4 Mon Sep 17 00:00:00 2001 From: Ioseph Kim <[email protected]> Date: Wed, 10 Dec 2025 17:26:02 +0900 Subject: [PATCH] Adding a '--enable-failover' option to 'pg_createsubscriber' utility A failover option has been added to the CREATE SUBSCRITION command, but this functionality isn't easily accessible using the pg_createsubscriber tool. Subscriptions created using pg_createsubscriber must be configured for failover via an ALTER command. To address this issue, I added a simple --enable-failover option to the pg_createsubscriber tool. --- doc/src/sgml/ref/pg_createsubscriber.sgml | 17 ++++++++++++ src/bin/pg_basebackup/pg_createsubscriber.c | 26 ++++++++++++++----- .../t/040_pg_createsubscriber.pl | 16 ++++++++++++ 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml index bb9cc72576c..022893c4781 100644 --- a/doc/src/sgml/ref/pg_createsubscriber.sgml +++ b/doc/src/sgml/ref/pg_createsubscriber.sgml @@ -272,6 +272,23 @@ PostgreSQL documentation </listitem> </varlistentry> + <varlistentry> + <term><option>--enable-failover</option></term> + <listitem> + <para> + Enables the subscription's + <link linkend="sql-createsubscription-params-with-failover"><literal>failover</literal></link> + option, allowing its logical replication slot to be used after failover. + The default is <literal>false</literal>. + </para> + When this option is enabled, the connection string used in the <option>--publisher-server</option> + option may be adjusted to support failover. For example, by specifying multiple hosts + and using <literal>target_session_attrs=read-write</literal>. + <para> + </para> + </listitem> + </varlistentry> + <varlistentry> <term><option>--publication=<replaceable class="parameter">name</replaceable></option></term> <listitem> diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c index ef6deec14af..82506423f68 100644 --- a/src/bin/pg_basebackup/pg_createsubscriber.c +++ b/src/bin/pg_basebackup/pg_createsubscriber.c @@ -49,6 +49,7 @@ struct CreateSubscriberOptions int recovery_timeout; /* stop recovery after this time */ bool all_dbs; /* all option */ SimpleStringList objecttypes_to_clean; /* list of object types to cleanup */ + bool failover; /* enable failover option of subscription */ }; /* per-database publication/subscription info */ @@ -73,6 +74,8 @@ struct LogicalRepInfos { struct LogicalRepInfo *dbinfo; bool two_phase; /* enable-two-phase option */ + bool failover; /* enable failover option of logical + * replication slot */ bits32 objecttypes_to_clean; /* flags indicating which object types * to clean up on subscriber */ }; @@ -260,6 +263,8 @@ usage(void) printf(_(" --publication=NAME publication name\n")); printf(_(" --replication-slot=NAME replication slot name\n")); printf(_(" --subscription=NAME subscription name\n")); + printf(_(" --enable-failover enable syncing replication slots associated\n" + " with the subscription\n")); printf(_(" -V, --version output version information, then exit\n")); printf(_(" -?, --help show this help, then exit\n")); printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); @@ -507,16 +512,18 @@ store_pub_sub_info(const struct CreateSubscriberOptions *opt, dbinfo[i].subname = subcell->val; else dbinfo[i].subname = NULL; + dbinfos.failover = opt->failover; /* Other fields will be filled later */ pg_log_debug("publisher(%d): publication: %s ; replication slot: %s ; connection string: %s", i, dbinfo[i].pubname ? dbinfo[i].pubname : "(auto)", dbinfo[i].replslotname ? dbinfo[i].replslotname : "(auto)", dbinfo[i].pubconninfo); - pg_log_debug("subscriber(%d): subscription: %s ; connection string: %s, two_phase: %s", i, + pg_log_debug("subscriber(%d): subscription: %s ; connection string: %s, two_phase: %s, failover: %s", i, dbinfo[i].subname ? dbinfo[i].subname : "(auto)", dbinfo[i].subconninfo, - dbinfos.two_phase ? "true" : "false"); + dbinfos.two_phase ? "true" : "false", + dbinfos.failover ? "true" : "false"); if (num_pubs > 0) pubcell = pubcell->next; @@ -1395,9 +1402,10 @@ create_logical_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo) slot_name_esc = PQescapeLiteral(conn, slot_name, strlen(slot_name)); appendPQExpBuffer(str, - "SELECT lsn FROM pg_catalog.pg_create_logical_replication_slot(%s, 'pgoutput', false, %s, false)", + "SELECT lsn FROM pg_catalog.pg_create_logical_replication_slot(%s, 'pgoutput', false, %s, %s)", slot_name_esc, - dbinfos.two_phase ? "true" : "false"); + dbinfos.two_phase ? "true" : "false", + dbinfos.failover ? "true" : "false"); PQfreemem(slot_name_esc); @@ -1833,9 +1841,10 @@ create_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo) appendPQExpBuffer(str, "CREATE SUBSCRIPTION %s CONNECTION %s PUBLICATION %s " "WITH (create_slot = false, enabled = false, " - "slot_name = %s, copy_data = false, two_phase = %s)", + "slot_name = %s, copy_data = false, two_phase = %s, failover = %s)", subname_esc, pubconninfo_esc, pubname_esc, replslotname_esc, - dbinfos.two_phase ? "true" : "false"); + dbinfos.two_phase ? "true" : "false", + dbinfos.failover ? "true" : "false"); PQfreemem(pubname_esc); PQfreemem(subname_esc); @@ -2079,6 +2088,7 @@ main(int argc, char **argv) {"replication-slot", required_argument, NULL, 3}, {"subscription", required_argument, NULL, 4}, {"clean", required_argument, NULL, 5}, + {"enable-failover", no_argument, NULL, 6}, {NULL, 0, NULL, 0} }; @@ -2127,6 +2137,7 @@ main(int argc, char **argv) opt.sub_port = DEFAULT_SUB_PORT; opt.sub_username = NULL; opt.two_phase = false; + opt.failover = false; opt.database_names = (SimpleStringList) { 0 @@ -2232,6 +2243,9 @@ main(int argc, char **argv) else pg_fatal("object type \"%s\" specified more than once for --clean", optarg); break; + case 6: + opt.failover = true; + break; default: /* getopt_long already emitted a complaint */ pg_log_error_hint("Try \"%s --help\" for more information.", progname); diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl index 3d6086dc489..b19ceb7df60 100644 --- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl +++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl @@ -463,6 +463,7 @@ command_ok( '--database' => $db1, '--database' => $db2, '--enable-two-phase', + '--enable-failover', '--clean' => 'publications', ], 'run pg_createsubscriber on node S'); @@ -495,6 +496,21 @@ is( $node_s->safe_psql( 't', 'subscriptions are created with the two-phase option enabled'); +# check failover option +is( $node_s->safe_psql( + $db1, + "SELECT count(*) FROM pg_subscription WHERE subfailover" + ), + '2', + 'subscriptions are created with the failover option enabled'); + +is( $node_p->safe_psql( + $db1, + "SELECT count(*) FROM pg_replication_slots WHERE slot_type = 'logical' and failover and slot_name = 'replslot1'" + ), + '1', + 'replication slot are created with the failover option enabled'); + # Confirm the pre-existing subscription has been removed $result = $node_s->safe_psql( 'postgres', qq( -- 2.47.3
