1
0
mirror of https://github.com/postgres/postgres.git synced 2025-12-21 05:21:08 +03:00

Fix the handling of the failover option in subscription commands.

Do not allow ALTER SUBSCRIPTION ... SET (failover = on|off) in a
transaction block as the changed failover option of the slot can't be
rolled back. For the same reason, we refrain from altering the replication
slot's failover property if the subscription is created with a valid
slot_name and create_slot=false.

Reprted-by: Kuroda Hayato
Author: Hou Zhijie
Reviewed-by: Shveta Malik, Bertrand Drouvot, Kuroda Hayato
Discussion: https://postgr.es/m/OS0PR01MB57165542B09DFA4943830BF294082@OS0PR01MB5716.jpnprd01.prod.outlook.com
This commit is contained in:
Amit Kapila
2024-04-23 12:14:57 +05:30
parent 480bc6e3ed
commit b29cbd3da4
8 changed files with 52 additions and 67 deletions

View File

@@ -401,13 +401,6 @@ parse_subscription_options(ParseState *pstate, List *stmt_options,
errmsg("%s and %s are mutually exclusive options",
"connect = false", "copy_data = true")));
if (opts->failover &&
IsSet(opts->specified_opts, SUBOPT_FAILOVER))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("%s and %s are mutually exclusive options",
"connect = false", "failover = true")));
/* Change the defaults of other options. */
opts->enabled = false;
opts->create_slot = false;
@@ -836,21 +829,6 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
(errmsg("created replication slot \"%s\" on publisher",
opts.slot_name)));
}
/*
* If the slot_name is specified without the create_slot option,
* it is possible that the user intends to use an existing slot on
* the publisher, so here we alter the failover property of the
* slot to match the failover value in subscription.
*
* We do not need to change the failover to false if the server
* does not support failover (e.g. pre-PG17).
*/
else if (opts.slot_name &&
(opts.failover || walrcv_server_version(wrconn) >= 170000))
{
walrcv_alter_slot(wrconn, opts.slot_name, opts.failover);
}
}
PG_FINALLY();
{
@@ -1267,6 +1245,12 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
errmsg("cannot set %s for enabled subscription",
"failover")));
/*
* The changed failover option of the slot can't be rolled
* back.
*/
PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION ... SET (failover)");
values[Anum_pg_subscription_subfailover - 1] =
BoolGetDatum(opts.failover);
replaces[Anum_pg_subscription_subfailover - 1] = true;

View File

@@ -4804,12 +4804,17 @@ getSubscriptions(Archive *fout)
if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
" s.subenabled,\n"
" s.subfailover\n");
" s.subenabled,\n");
else
appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
" false AS subenabled,\n"
" false AS subfailover\n");
" false AS subenabled,\n");
if (fout->remoteVersion >= 170000)
appendPQExpBufferStr(query,
" s.subfailover\n");
else
appendPQExpBuffer(query,
" false AS subfailover\n");
appendPQExpBufferStr(query,
"FROM pg_subscription s\n");
@@ -5132,6 +5137,9 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
if (strcmp(subinfo->subrunasowner, "t") == 0)
appendPQExpBufferStr(query, ", run_as_owner = true");
if (strcmp(subinfo->subfailover, "t") == 0)
appendPQExpBufferStr(query, ", failover = true");
if (strcmp(subinfo->subsynccommit, "off") != 0)
appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
@@ -5165,17 +5173,6 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
}
if (strcmp(subinfo->subfailover, "t") == 0)
{
/*
* Enable the failover to allow the subscription's slot to be
* synced to the standbys after the upgrade.
*/
appendPQExpBufferStr(query,
"\n-- For binary upgrade, must preserve the subscriber's failover option.\n");
appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s SET(failover = true);\n", qsubname);
}
if (strcmp(subinfo->subenabled, "t") == 0)
{
/*

View File

@@ -42,26 +42,12 @@ my $slot_creation_time_on_primary = $publisher->safe_psql(
SELECT current_timestamp;
]);
# Create a slot on the publisher with failover disabled
$publisher->safe_psql('postgres',
"SELECT 'init' FROM pg_create_logical_replication_slot('lsub1_slot', 'pgoutput', false, false, false);"
);
# Confirm that the failover flag on the slot is turned off
is( $publisher->safe_psql(
'postgres',
q{SELECT failover from pg_replication_slots WHERE slot_name = 'lsub1_slot';}
),
"f",
'logical slot has failover false on the publisher');
# Create a subscription (using the same slot created above) that enables
# failover.
# Create a subscription that enables failover.
$subscriber1->safe_psql('postgres',
"CREATE SUBSCRIPTION regress_mysub1 CONNECTION '$publisher_connstr' PUBLICATION regress_mypub WITH (slot_name = lsub1_slot, copy_data=false, failover = true, create_slot = false, enabled = false);"
"CREATE SUBSCRIPTION regress_mysub1 CONNECTION '$publisher_connstr' PUBLICATION regress_mypub WITH (slot_name = lsub1_slot, copy_data = false, failover = true, enabled = false);"
);
# Confirm that the failover flag on the slot has now been turned on
# Confirm that the failover flag on the slot is turned on
is( $publisher->safe_psql(
'postgres',
q{SELECT failover from pg_replication_slots WHERE slot_name = 'lsub1_slot';}

View File

@@ -89,8 +89,6 @@ CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PU
ERROR: connect = false and enabled = true are mutually exclusive options
CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, create_slot = true);
ERROR: connect = false and create_slot = true are mutually exclusive options
CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, failover = true);
ERROR: connect = false and failover = true are mutually exclusive options
CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (slot_name = NONE, enabled = true);
ERROR: slot_name = NONE and enabled = true are mutually exclusive options
CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (slot_name = NONE, enabled = false, create_slot = true);
@@ -472,6 +470,11 @@ REVOKE CREATE ON DATABASE REGRESSION FROM regress_subscription_user3;
SET SESSION AUTHORIZATION regress_subscription_user3;
ALTER SUBSCRIPTION regress_testsub RENAME TO regress_testsub2;
ERROR: permission denied for database regression
-- fail - cannot do ALTER SUBSCRIPTION SET (failover) inside transaction block
BEGIN;
ALTER SUBSCRIPTION regress_testsub SET (failover);
ERROR: ALTER SUBSCRIPTION ... SET (failover) cannot run inside a transaction block
COMMIT;
-- ok, owning it is enough for this stuff
ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
DROP SUBSCRIPTION regress_testsub;

View File

@@ -54,7 +54,6 @@ SET SESSION AUTHORIZATION 'regress_subscription_user';
CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, copy_data = true);
CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, enabled = true);
CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, create_slot = true);
CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false, failover = true);
CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (slot_name = NONE, enabled = true);
CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (slot_name = NONE, enabled = false, create_slot = true);
CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (slot_name = NONE);
@@ -333,6 +332,11 @@ REVOKE CREATE ON DATABASE REGRESSION FROM regress_subscription_user3;
SET SESSION AUTHORIZATION regress_subscription_user3;
ALTER SUBSCRIPTION regress_testsub RENAME TO regress_testsub2;
-- fail - cannot do ALTER SUBSCRIPTION SET (failover) inside transaction block
BEGIN;
ALTER SUBSCRIPTION regress_testsub SET (failover);
COMMIT;
-- ok, owning it is enough for this stuff
ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
DROP SUBSCRIPTION regress_testsub;