mirror of
https://github.com/postgres/postgres.git
synced 2025-05-02 11:44:50 +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:
parent
480bc6e3ed
commit
b29cbd3da4
@ -66,10 +66,11 @@ ALTER SUBSCRIPTION <replaceable class="parameter">name</replaceable> RENAME TO <
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Commands <command>ALTER SUBSCRIPTION ... REFRESH PUBLICATION</command> and
|
Commands <command>ALTER SUBSCRIPTION ... REFRESH PUBLICATION</command>,
|
||||||
<command>ALTER SUBSCRIPTION ... {SET|ADD|DROP} PUBLICATION ...</command>
|
<command>ALTER SUBSCRIPTION ... {SET|ADD|DROP} PUBLICATION ...</command>
|
||||||
with <literal>refresh</literal> option as <literal>true</literal> cannot be
|
with <literal>refresh</literal> option as <literal>true</literal> and
|
||||||
executed inside a transaction block.
|
<command>ALTER SUBSCRIPTION ... SET (failover = on|off)</command>
|
||||||
|
cannot be executed inside a transaction block.
|
||||||
|
|
||||||
These commands also cannot be executed when the subscription has
|
These commands also cannot be executed when the subscription has
|
||||||
<link linkend="sql-createsubscription-params-with-two-phase"><literal>two_phase</literal></link>
|
<link linkend="sql-createsubscription-params-with-two-phase"><literal>two_phase</literal></link>
|
||||||
|
@ -122,8 +122,7 @@ CREATE SUBSCRIPTION <replaceable class="parameter">subscription_name</replaceabl
|
|||||||
(You cannot combine setting <literal>connect</literal>
|
(You cannot combine setting <literal>connect</literal>
|
||||||
to <literal>false</literal> with
|
to <literal>false</literal> with
|
||||||
setting <literal>create_slot</literal>, <literal>enabled</literal>,
|
setting <literal>create_slot</literal>, <literal>enabled</literal>,
|
||||||
<literal>copy_data</literal>, or <literal>failover</literal> to
|
or <literal>copy_data</literal> to <literal>true</literal>.)
|
||||||
<literal>true</literal>.)
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -183,6 +182,21 @@ CREATE SUBSCRIPTION <replaceable class="parameter">subscription_name</replaceabl
|
|||||||
<xref linkend="logical-replication-subscription-examples-deferred-slot"/>
|
<xref linkend="logical-replication-subscription-examples-deferred-slot"/>
|
||||||
for examples.
|
for examples.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
When setting <literal>slot_name</literal> to a valid name and
|
||||||
|
<literal>create_slot</literal> to false, the
|
||||||
|
<literal>failover</literal> property value of the named slot may
|
||||||
|
differ from the counterpart <literal>failover</literal> parameter
|
||||||
|
specified in the subscription. Always ensure the slot property
|
||||||
|
<literal>failover</literal> matches the counterpart parameter of the
|
||||||
|
subscription and vice versa. Otherwise, the slot on the publisher may
|
||||||
|
behave differently from what these subscription options say: for
|
||||||
|
example, the slot on the publisher could either be synced to the
|
||||||
|
standbys even when the subscription's <literal>failover</literal>
|
||||||
|
option is disabled or could be disabled for sync even when the
|
||||||
|
subscription's <literal>failover</literal> option is enabled.
|
||||||
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
@ -1611,11 +1611,7 @@ CREATE DATABASE foo WITH TEMPLATE template0;
|
|||||||
dump can be restored without requiring network access to the remote
|
dump can be restored without requiring network access to the remote
|
||||||
servers. It is then up to the user to reactivate the subscriptions in a
|
servers. It is then up to the user to reactivate the subscriptions in a
|
||||||
suitable way. If the involved hosts have changed, the connection
|
suitable way. If the involved hosts have changed, the connection
|
||||||
information might have to be changed. If the subscription needs to
|
information might have to be changed. It might also be appropriate to
|
||||||
be enabled for
|
|
||||||
<link linkend="sql-createsubscription-params-with-failover"><literal>failover</literal></link>,
|
|
||||||
execute <link linkend="sql-altersubscription-params-set"><literal>ALTER SUBSCRIPTION ... SET (failover = true)</literal></link>
|
|
||||||
after the slot has been created. It might also be appropriate to
|
|
||||||
truncate the target tables before initiating a new full table copy. If users
|
truncate the target tables before initiating a new full table copy. If users
|
||||||
intend to copy initial data during refresh they must create the slot with
|
intend to copy initial data during refresh they must create the slot with
|
||||||
<literal>two_phase = false</literal>. After the initial sync, the
|
<literal>two_phase = false</literal>. After the initial sync, the
|
||||||
|
@ -401,13 +401,6 @@ parse_subscription_options(ParseState *pstate, List *stmt_options,
|
|||||||
errmsg("%s and %s are mutually exclusive options",
|
errmsg("%s and %s are mutually exclusive options",
|
||||||
"connect = false", "copy_data = true")));
|
"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. */
|
/* Change the defaults of other options. */
|
||||||
opts->enabled = false;
|
opts->enabled = false;
|
||||||
opts->create_slot = false;
|
opts->create_slot = false;
|
||||||
@ -836,21 +829,6 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
|
|||||||
(errmsg("created replication slot \"%s\" on publisher",
|
(errmsg("created replication slot \"%s\" on publisher",
|
||||||
opts.slot_name)));
|
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();
|
PG_FINALLY();
|
||||||
{
|
{
|
||||||
@ -1267,6 +1245,12 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
|
|||||||
errmsg("cannot set %s for enabled subscription",
|
errmsg("cannot set %s for enabled subscription",
|
||||||
"failover")));
|
"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] =
|
values[Anum_pg_subscription_subfailover - 1] =
|
||||||
BoolGetDatum(opts.failover);
|
BoolGetDatum(opts.failover);
|
||||||
replaces[Anum_pg_subscription_subfailover - 1] = true;
|
replaces[Anum_pg_subscription_subfailover - 1] = true;
|
||||||
|
@ -4804,12 +4804,17 @@ getSubscriptions(Archive *fout)
|
|||||||
|
|
||||||
if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
|
if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
|
||||||
appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
|
appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
|
||||||
" s.subenabled,\n"
|
" s.subenabled,\n");
|
||||||
" s.subfailover\n");
|
|
||||||
else
|
else
|
||||||
appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
|
appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
|
||||||
" false AS subenabled,\n"
|
" false AS subenabled,\n");
|
||||||
" false AS subfailover\n");
|
|
||||||
|
if (fout->remoteVersion >= 170000)
|
||||||
|
appendPQExpBufferStr(query,
|
||||||
|
" s.subfailover\n");
|
||||||
|
else
|
||||||
|
appendPQExpBuffer(query,
|
||||||
|
" false AS subfailover\n");
|
||||||
|
|
||||||
appendPQExpBufferStr(query,
|
appendPQExpBufferStr(query,
|
||||||
"FROM pg_subscription s\n");
|
"FROM pg_subscription s\n");
|
||||||
@ -5132,6 +5137,9 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
|
|||||||
if (strcmp(subinfo->subrunasowner, "t") == 0)
|
if (strcmp(subinfo->subrunasowner, "t") == 0)
|
||||||
appendPQExpBufferStr(query, ", run_as_owner = true");
|
appendPQExpBufferStr(query, ", run_as_owner = true");
|
||||||
|
|
||||||
|
if (strcmp(subinfo->subfailover, "t") == 0)
|
||||||
|
appendPQExpBufferStr(query, ", failover = true");
|
||||||
|
|
||||||
if (strcmp(subinfo->subsynccommit, "off") != 0)
|
if (strcmp(subinfo->subsynccommit, "off") != 0)
|
||||||
appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
|
appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
|
||||||
|
|
||||||
@ -5165,17 +5173,6 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
|
|||||||
appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
|
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)
|
if (strcmp(subinfo->subenabled, "t") == 0)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -42,26 +42,12 @@ my $slot_creation_time_on_primary = $publisher->safe_psql(
|
|||||||
SELECT current_timestamp;
|
SELECT current_timestamp;
|
||||||
]);
|
]);
|
||||||
|
|
||||||
# Create a slot on the publisher with failover disabled
|
# Create a subscription that enables failover.
|
||||||
$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.
|
|
||||||
$subscriber1->safe_psql('postgres',
|
$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(
|
is( $publisher->safe_psql(
|
||||||
'postgres',
|
'postgres',
|
||||||
q{SELECT failover from pg_replication_slots WHERE slot_name = 'lsub1_slot';}
|
q{SELECT failover from pg_replication_slots WHERE slot_name = 'lsub1_slot';}
|
||||||
|
@ -89,8 +89,6 @@ CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PU
|
|||||||
ERROR: connect = false and enabled = true are mutually exclusive options
|
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);
|
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
|
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);
|
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
|
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);
|
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;
|
SET SESSION AUTHORIZATION regress_subscription_user3;
|
||||||
ALTER SUBSCRIPTION regress_testsub RENAME TO regress_testsub2;
|
ALTER SUBSCRIPTION regress_testsub RENAME TO regress_testsub2;
|
||||||
ERROR: permission denied for database regression
|
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
|
-- ok, owning it is enough for this stuff
|
||||||
ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
|
ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
|
||||||
DROP SUBSCRIPTION regress_testsub;
|
DROP SUBSCRIPTION regress_testsub;
|
||||||
|
@ -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, 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, 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, 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 = 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, enabled = false, create_slot = true);
|
||||||
CREATE SUBSCRIPTION regress_testsub2 CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (slot_name = NONE);
|
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;
|
SET SESSION AUTHORIZATION regress_subscription_user3;
|
||||||
ALTER SUBSCRIPTION regress_testsub RENAME TO regress_testsub2;
|
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
|
-- ok, owning it is enough for this stuff
|
||||||
ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
|
ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
|
||||||
DROP SUBSCRIPTION regress_testsub;
|
DROP SUBSCRIPTION regress_testsub;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user