aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Eisentraut <peter_e@gmx.net>2017-09-17 21:37:02 -0400
committerPeter Eisentraut <peter_e@gmx.net>2017-09-17 22:00:34 -0400
commit522b028a0000d08c6d113c2334e669dd31a6b1cd (patch)
tree4b7942f17a0dcd238b75da3c995cec7fd6bef57f
parent90cebfa9ee3a4110c83c6b012ec523adcc1c2468 (diff)
downloadpostgresql-522b028a0000d08c6d113c2334e669dd31a6b1cd.tar.gz
postgresql-522b028a0000d08c6d113c2334e669dd31a6b1cd.zip
Fix DROP SUBSCRIPTION hang
When ALTER SUBSCRIPTION DISABLE is run in the same transaction before DROP SUBSCRIPTION, the latter will hang because workers will still be running, not having seen the DISABLE committed, and DROP SUBSCRIPTION will wait until the workers have vacated the replication origin slots. Previously, DROP SUBSCRIPTION killed the logical replication workers immediately only if it was going to drop the replication slot, otherwise it scheduled the worker killing for the end of the transaction, as a result of 7e174fa793a2df89fe03d002a5087ef67abcdde8. This, however, causes the present problem. To fix, kill the workers immediately in all cases. This covers all cases: A subscription that doesn't have a replication slot must be disabled. It was either disabled in the same transaction, or it was already disabled before the current transaction, but then there shouldn't be any workers left and this won't make a difference. Reported-by: Arseny Sher <a.sher@postgrespro.ru> Discussion: https://www.postgresql.org/message-id/flat/87mv6av84w.fsf%40ars-thinkpad
-rw-r--r--src/backend/commands/subscriptioncmds.c19
-rw-r--r--src/test/subscription/t/007_ddl.pl51
2 files changed, 63 insertions, 7 deletions
diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index 77620dfd429..2960f9da0a2 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -909,9 +909,17 @@ DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
ReleaseSysCache(tup);
/*
- * If we are dropping the replication slot, stop all the subscription
- * workers immediately, so that the slot becomes accessible. Otherwise
- * just schedule the stopping for the end of the transaction.
+ * Stop all the subscription workers immediately.
+ *
+ * This is necessary if we are dropping the replication slot, so that the
+ * slot becomes accessible.
+ *
+ * It is also necessary if the subscription is disabled and was disabled
+ * in the same transaction. Then the workers haven't seen the disabling
+ * yet and will still be running, leading to hangs later when we want to
+ * drop the replication origin. If the subscription was disabled before
+ * this transaction, then there shouldn't be any workers left, so this
+ * won't make a difference.
*
* New workers won't be started because we hold an exclusive lock on the
* subscription till the end of the transaction.
@@ -923,10 +931,7 @@ DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
{
LogicalRepWorker *w = (LogicalRepWorker *) lfirst(lc);
- if (slotname)
- logicalrep_worker_stop(w->subid, w->relid);
- else
- logicalrep_worker_stop_at_commit(w->subid, w->relid);
+ logicalrep_worker_stop(w->subid, w->relid);
}
list_free(subworkers);
diff --git a/src/test/subscription/t/007_ddl.pl b/src/test/subscription/t/007_ddl.pl
new file mode 100644
index 00000000000..3f36238840b
--- /dev/null
+++ b/src/test/subscription/t/007_ddl.pl
@@ -0,0 +1,51 @@
+# Test some logical replication DDL behavior
+use strict;
+use warnings;
+use PostgresNode;
+use TestLib;
+use Test::More tests => 1;
+
+sub wait_for_caught_up
+{
+ my ($node, $appname) = @_;
+
+ $node->poll_query_until('postgres',
+"SELECT pg_current_wal_lsn() <= replay_lsn FROM pg_stat_replication WHERE application_name = '$appname';"
+ ) or die "Timed out while waiting for subscriber to catch up";
+}
+
+my $node_publisher = get_new_node('publisher');
+$node_publisher->init(allows_streaming => 'logical');
+$node_publisher->start;
+
+my $node_subscriber = get_new_node('subscriber');
+$node_subscriber->init(allows_streaming => 'logical');
+$node_subscriber->start;
+
+my $ddl = "CREATE TABLE test1 (a int, b text);";
+$node_publisher->safe_psql('postgres', $ddl);
+$node_subscriber->safe_psql('postgres', $ddl);
+
+my $publisher_connstr = $node_publisher->connstr . ' dbname=postgres';
+my $appname = 'replication_test';
+
+$node_publisher->safe_psql('postgres',
+ "CREATE PUBLICATION mypub FOR ALL TABLES;");
+$node_subscriber->safe_psql('postgres',
+"CREATE SUBSCRIPTION mysub CONNECTION '$publisher_connstr application_name=$appname' PUBLICATION mypub;"
+);
+
+wait_for_caught_up($node_publisher, $appname);
+
+$node_subscriber->safe_psql('postgres', q{
+BEGIN;
+ALTER SUBSCRIPTION mysub DISABLE;
+ALTER SUBSCRIPTION mysub SET (slot_name = NONE);
+DROP SUBSCRIPTION mysub;
+COMMIT;
+});
+
+pass "subscription disable and drop in same transaction did not hang";
+
+$node_subscriber->stop;
+$node_publisher->stop;