diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml index a700697f887..2ee6eee9e35 100644 --- a/doc/src/sgml/ref/pg_createsubscriber.sgml +++ b/doc/src/sgml/ref/pg_createsubscriber.sgml @@ -482,6 +482,15 @@ PostgreSQL documentation + + + If the standby server contains failover + replication slots, they cannot be synchronized anymore, so drop + them. + + + Update the system identifier on the target server. The diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c index fcb05ec656a..5499e6d96ae 100644 --- a/src/bin/pg_basebackup/pg_createsubscriber.c +++ b/src/bin/pg_basebackup/pg_createsubscriber.c @@ -85,6 +85,7 @@ static void setup_recovery(const struct LogicalRepInfo *dbinfo, const char *data const char *lsn); static void drop_primary_replication_slot(struct LogicalRepInfo *dbinfo, const char *slotname); +static void drop_failover_replication_slots(struct LogicalRepInfo *dbinfo); static char *create_logical_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo); static void drop_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo, @@ -1137,6 +1138,49 @@ drop_primary_replication_slot(struct LogicalRepInfo *dbinfo, const char *slotnam } } +/* + * Drop failover replication slots on subscriber. After the transformation, + * they have no use. + * + * XXX We do not fail here. Instead, we provide a warning so the user can drop + * them later. + */ +static void +drop_failover_replication_slots(struct LogicalRepInfo *dbinfo) +{ + PGconn *conn; + PGresult *res; + + conn = connect_database(dbinfo[0].subconninfo, false); + if (conn != NULL) + { + /* Get failover replication slot names */ + res = PQexec(conn, + "SELECT slot_name FROM pg_catalog.pg_replication_slots WHERE failover"); + + if (PQresultStatus(res) == PGRES_TUPLES_OK) + { + /* Remove failover replication slots from subscriber */ + for (int i = 0; i < PQntuples(res); i++) + drop_replication_slot(conn, &dbinfo[0], PQgetvalue(res, i, 0)); + } + else + { + pg_log_warning("could not obtain failover replication slot information: %s", + PQresultErrorMessage(res)); + pg_log_warning_hint("Drop the failover replication slots on subscriber soon to avoid retention of WAL files."); + } + + PQclear(res); + disconnect_database(conn, false); + } + else + { + pg_log_warning("could not drop failover replication slot"); + pg_log_warning_hint("Drop the failover replication slots on subscriber soon to avoid retention of WAL files."); + } +} + /* * Create a logical replication slot and returns a LSN. * @@ -1268,7 +1312,7 @@ start_standby_server(const struct CreateSubscriberOptions *opt, bool restricted_ PQExpBuffer pg_ctl_cmd = createPQExpBuffer(); int rc; - appendPQExpBuffer(pg_ctl_cmd, "\"%s\" start -D \"%s\" -s", + appendPQExpBuffer(pg_ctl_cmd, "\"%s\" start -D \"%s\" -s -o \"-c sync_replication_slots=off\"", pg_ctl_path, subscriber_dir); if (restricted_access) { @@ -2065,6 +2109,9 @@ main(int argc, char **argv) /* Remove primary_slot_name if it exists on primary */ drop_primary_replication_slot(dbinfo, primary_slot_name); + /* Remove failover replication slots if they exist on subscriber */ + drop_failover_replication_slots(dbinfo); + /* Stop the subscriber */ pg_log_info("stopping the subscriber"); stop_standby_server(subscriber_dir); diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl index a677fefa94f..0516d4e17ed 100644 --- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl +++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl @@ -88,6 +88,7 @@ command_fails( # Set up node P as primary my $node_p = PostgreSQL::Test::Cluster->new('node_p'); +my $pconnstr = $node_p->connstr; $node_p->init(allows_streaming => 'logical'); $node_p->start; @@ -122,6 +123,8 @@ $node_s->init_from_backup($node_p, 'backup_1', has_streaming => 1); $node_s->append_conf( 'postgresql.conf', qq[ primary_slot_name = '$slotname' +primary_conninfo = '$pconnstr dbname=postgres' +hot_standby_feedback = on ]); $node_s->set_standby_mode(); $node_s->start; @@ -260,6 +263,16 @@ max_worker_processes = 8 # Restore default settings on both servers $node_p->restart; +# Create failover slot to test its removal +my $fslotname = 'failover_slot'; +$node_p->safe_psql('pg1', + "SELECT pg_create_logical_replication_slot('$fslotname', 'pgoutput', false, false, true)"); +$node_s->start; +$node_s->safe_psql('postgres', "SELECT pg_sync_replication_slots()"); +my $result = $node_s->safe_psql('postgres', "SELECT slot_name FROM pg_replication_slots WHERE slot_name = '$fslotname' AND synced AND NOT temporary"); +is($result, 'failover_slot', 'failover slot is synced'); +$node_s->stop; + # dry run mode on node S command_ok( [ @@ -318,7 +331,7 @@ command_ok( 'run pg_createsubscriber on node S'); # Confirm the physical replication slot has been removed -my $result = $node_p->safe_psql('pg1', +$result = $node_p->safe_psql('pg1', "SELECT count(*) FROM pg_replication_slots WHERE slot_name = '$slotname'" ); is($result, qq(0), @@ -343,6 +356,11 @@ my @subnames = split("\n", $result); $node_s->wait_for_subscription_sync($node_p, $subnames[0]); $node_s->wait_for_subscription_sync($node_p, $subnames[1]); +# Confirm the failover slot has been removed +$result = $node_s->safe_psql('pg1', + "SELECT count(*) FROM pg_replication_slots WHERE slot_name = '$fslotname'"); +is($result, qq(0), 'failover slot was removed'); + # Check result on database pg1 $result = $node_s->safe_psql('pg1', 'SELECT * FROM tbl1'); is( $result, qq(first row