From 9170e85d5a1b8fa191c0d309e113c6598438fdb7 Mon Sep 17 00:00:00 2001 From: ParadoxV5 Date: Mon, 17 Aug 2026 18:16:59 -0600 Subject: [PATCH] MDEV-38849 slave_connections_needed_for_purge prevents independent machine from purging binary logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `@@slave_connections_needed_for_purge`’s default of `1` ensures binary log availability on replication masters, but is not a sensible default suitable for all scenarios, especially for long-term slave servers and standalone (not in a replication setup) servers. The outcome was that standalone server users were confused why automatic binlog purging does not work. This commit changes this default to `0`, which is suitable for both standalone and (when backed by prompt failure recovery) replication setups. `0` also more closely matches the behaviour before MDEV-31404, which added this variable, out of the box. This commit also adds a one-time replication warning when registering a slave, but `@@slave_connections_needed_for_purge` is left unchanged. Rather than enforcing a defence with an unsensible default, this reminder will bring awareness of the risk of automatic binlog purging. This commit also cleans up Galera and MTR workarounds to the introduction of the `@@slave_connections_needed_for_purge=1` default. --- mysql-test/include/mtr_warnings.sql | 1 + mysql-test/main/mysqld--help.result | 3 +- mysql-test/suite/binlog/my.cnf | 4 -- .../binlog_encryption/binlog_index-master.opt | 1 - mysql-test/suite/galera/r/basic.result | 6 --- mysql-test/suite/galera/t/basic.test | 3 -- mysql-test/suite/rpl/my.cnf | 2 - .../r/warn_slaves_not_needed_for_purge.result | 25 +++++++++ .../t/warn_slaves_not_needed_for_purge.test | 52 +++++++++++++++++++ .../sys_vars/r/sysvars_server_embedded.result | 2 +- .../r/sysvars_server_notembedded.result | 2 +- sql/mysqld.cc | 15 ++---- sql/mysqld.h | 1 + sql/repl_failsafe.cc | 8 +++ sql/sql_repl.h | 9 ++++ sql/sys_vars.cc | 22 +++++--- 16 files changed, 120 insertions(+), 36 deletions(-) delete mode 100644 mysql-test/suite/binlog/my.cnf delete mode 100644 mysql-test/suite/binlog_encryption/binlog_index-master.opt create mode 100644 mysql-test/suite/rpl/r/warn_slaves_not_needed_for_purge.result create mode 100644 mysql-test/suite/rpl/t/warn_slaves_not_needed_for_purge.test diff --git a/mysql-test/include/mtr_warnings.sql b/mysql-test/include/mtr_warnings.sql index 88b3eb693773b..fce8eee67667a 100644 --- a/mysql-test/include/mtr_warnings.sql +++ b/mysql-test/include/mtr_warnings.sql @@ -148,6 +148,7 @@ INSERT INTO global_suppressions VALUES ("Slave: .*Duplicate entry"), ("Statement may not be safe to log in statement format"), + ("slave_connections_needed_for_purge"), /* innodb foreign key tests that fail in ALTER or RENAME produce this */ ("InnoDB: Error: in ALTER TABLE `test`.`t[123]`"), diff --git a/mysql-test/main/mysqld--help.result b/mysql-test/main/mysqld--help.result index 2d5145594778a..b6446f144212a 100644 --- a/mysql-test/main/mysqld--help.result +++ b/mysql-test/main/mysqld--help.result @@ -1320,7 +1320,6 @@ The following specify which files/extra groups are read (specified before remain Minimum number of connected slaves required for automatic binary log purge with max_binlog_total_size, binlog_expire_logs_seconds or binlog_expire_logs_days. - Default is 0 when Galera is enabled and 1 otherwise. --slave-ddl-exec-mode=name How replication events should be executed. Legal values are STRICT and IDEMPOTENT (default). In IDEMPOTENT mode, @@ -1954,7 +1953,7 @@ skip-networking FALSE skip-show-database FALSE skip-slave-start FALSE slave-compressed-protocol FALSE -slave-connections-needed-for-purge 1 +slave-connections-needed-for-purge 0 slave-ddl-exec-mode IDEMPOTENT slave-domain-parallel-threads 0 slave-exec-mode STRICT diff --git a/mysql-test/suite/binlog/my.cnf b/mysql-test/suite/binlog/my.cnf deleted file mode 100644 index a563433d165e8..0000000000000 --- a/mysql-test/suite/binlog/my.cnf +++ /dev/null @@ -1,4 +0,0 @@ -!include include/default_my.cnf - -[mariadbd] -slave_connections_needed_for_purge=0 diff --git a/mysql-test/suite/binlog_encryption/binlog_index-master.opt b/mysql-test/suite/binlog_encryption/binlog_index-master.opt deleted file mode 100644 index f2673d0802034..0000000000000 --- a/mysql-test/suite/binlog_encryption/binlog_index-master.opt +++ /dev/null @@ -1 +0,0 @@ ---slave_connections_needed_for_purge=0 diff --git a/mysql-test/suite/galera/r/basic.result b/mysql-test/suite/galera/r/basic.result index 7b4cbd93c7bb2..10f180e7a9474 100644 --- a/mysql-test/suite/galera/r/basic.result +++ b/mysql-test/suite/galera/r/basic.result @@ -1,11 +1,5 @@ connection node_2; connection node_1; -select @@slave_connections_needed_for_purge; -@@slave_connections_needed_for_purge -0 -select VARIABLE_NAME, GLOBAL_VALUE, GLOBAL_VALUE_ORIGIN from information_schema.system_variables where variable_name="slave_connections_needed_for_purge"; -VARIABLE_NAME GLOBAL_VALUE GLOBAL_VALUE_ORIGIN -SLAVE_CONNECTIONS_NEEDED_FOR_PURGE 0 AUTO USE test; CREATE TABLE t1(c1 INT PRIMARY KEY) ENGINE=INNODB; INSERT INTO t1 VALUES (1), (2), (3), (4), (5); diff --git a/mysql-test/suite/galera/t/basic.test b/mysql-test/suite/galera/t/basic.test index a70ee962bc0e7..8fc6eee3b3b46 100644 --- a/mysql-test/suite/galera/t/basic.test +++ b/mysql-test/suite/galera/t/basic.test @@ -1,9 +1,6 @@ --source include/galera_cluster.inc --source include/have_innodb.inc -select @@slave_connections_needed_for_purge; -select VARIABLE_NAME, GLOBAL_VALUE, GLOBAL_VALUE_ORIGIN from information_schema.system_variables where variable_name="slave_connections_needed_for_purge"; - USE test; CREATE TABLE t1(c1 INT PRIMARY KEY) ENGINE=INNODB; INSERT INTO t1 VALUES (1), (2), (3), (4), (5); diff --git a/mysql-test/suite/rpl/my.cnf b/mysql-test/suite/rpl/my.cnf index fa0a2c268fb22..d61e32ed51a59 100644 --- a/mysql-test/suite/rpl/my.cnf +++ b/mysql-test/suite/rpl/my.cnf @@ -6,5 +6,3 @@ # E.g. after !include ../my.cnf, in your `test.cnf`, specify your configuration # in option group e.g [mysqld.x], so that number `x` corresponds to the number # in the rpl server topology. -[mariadbd] -slave_connections_needed_for_purge=0 diff --git a/mysql-test/suite/rpl/r/warn_slaves_not_needed_for_purge.result b/mysql-test/suite/rpl/r/warn_slaves_not_needed_for_purge.result new file mode 100644 index 0000000000000..5383e2ac36ccd --- /dev/null +++ b/mysql-test/suite/rpl/r/warn_slaves_not_needed_for_purge.result @@ -0,0 +1,25 @@ +include/master-slave.inc +[connection master] +NOT FOUND /slave_connections_needed_for_purge/ in mysqld.1.err +connection slave; +START SLAVE IO_THREAD; +include/wait_for_slave_io_to_start.inc +FOUND 1 /slave_connections_needed_for_purge/ in mysqld.1.err +include/stop_slave_io.inc +START SLAVE IO_THREAD; +include/wait_for_slave_io_to_start.inc +include/stop_slave_io.inc +include/rpl_restart_server.inc [server_number=1 parameters: --slave-connections-needed-for-purge 0] +connection slave; +START SLAVE IO_THREAD; +include/wait_for_slave_io_to_start.inc +include/stop_slave_io.inc +include/rpl_restart_server.inc [server_number=1] +connection master; +SET @@GLOBAL.slave_connections_needed_for_purge= DEFAULT; +connection slave; +START SLAVE IO_THREAD; +include/wait_for_slave_io_to_start.inc +connection master; +FOUND 1 /slave_connections_needed_for_purge/ in mysqld.1.err +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/warn_slaves_not_needed_for_purge.test b/mysql-test/suite/rpl/t/warn_slaves_not_needed_for_purge.test new file mode 100644 index 0000000000000..3c46730f74f14 --- /dev/null +++ b/mysql-test/suite/rpl/t/warn_slaves_not_needed_for_purge.test @@ -0,0 +1,52 @@ +# MDEV-38849: Test the warning that the _default_ for +# `@@slave_connections_needed_for_purge` does not match the replication setup. +# (Do _not_ warn if `@@slave_connections_needed_for_purge` is manually set.) + +--source include/have_binlog_format_mixed.inc # no actual binlog content +--let $rpl_skip_start_slave= 1 +--source include/master-slave.inc + + +--let SEARCH_FILE= `SELECT @@log_error` +--let SEARCH_PATTERN= slave_connections_needed_for_purge +# Not connected: should not warn +--source include/search_pattern_in_file.inc + + +--connection slave +START SLAVE IO_THREAD; +--source include/wait_for_slave_io_to_start.inc +# Connected when left as default: should warn +--source include/search_pattern_in_file.inc + + +--source include/stop_slave_io.inc +START SLAVE IO_THREAD; +--source include/wait_for_slave_io_to_start.inc +# Reconnected: should not warn a second time + +--source include/stop_slave_io.inc + --let $rpl_server_parameters= --slave-connections-needed-for-purge 0 + --let $rpl_server_number= 1 + --source include/rpl_restart_server.inc + --connection slave +START SLAVE IO_THREAD; +--source include/wait_for_slave_io_to_start.inc +# Explicitly set to 0 in server options: should not warn + +--source include/stop_slave_io.inc + --let $rpl_server_parameters= + --source include/rpl_restart_server.inc + --connection master + SET @@GLOBAL.slave_connections_needed_for_purge= DEFAULT; + --connection slave +START SLAVE IO_THREAD; +--source include/wait_for_slave_io_to_start.inc +# Explicitly set to DEFAULT in system variables: should _not_ warn + +--connection master +--source include/search_pattern_in_file.inc + + +--let $rpl_only_running_threads= 1 +--source include/rpl_end.inc diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result index 0af805506b1cb..e6783b89dfb38 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -3465,7 +3465,7 @@ COMMAND_LINE_ARGUMENT OPTIONAL VARIABLE_NAME SLAVE_CONNECTIONS_NEEDED_FOR_PURGE VARIABLE_SCOPE GLOBAL VARIABLE_TYPE INT UNSIGNED -VARIABLE_COMMENT Minimum number of connected slaves required for automatic binary log purge with max_binlog_total_size, binlog_expire_logs_seconds or binlog_expire_logs_days. Default is 0 when Galera is enabled and 1 otherwise. +VARIABLE_COMMENT Minimum number of connected slaves required for automatic binary log purge with max_binlog_total_size, binlog_expire_logs_seconds or binlog_expire_logs_days. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 4294967295 NUMERIC_BLOCK_SIZE 1 diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index 5bc9e5ecf3e95..5994a50e85013 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -4035,7 +4035,7 @@ COMMAND_LINE_ARGUMENT OPTIONAL VARIABLE_NAME SLAVE_CONNECTIONS_NEEDED_FOR_PURGE VARIABLE_SCOPE GLOBAL VARIABLE_TYPE INT UNSIGNED -VARIABLE_COMMENT Minimum number of connected slaves required for automatic binary log purge with max_binlog_total_size, binlog_expire_logs_seconds or binlog_expire_logs_days. Default is 0 when Galera is enabled and 1 otherwise. +VARIABLE_COMMENT Minimum number of connected slaves required for automatic binary log purge with max_binlog_total_size, binlog_expire_logs_seconds or binlog_expire_logs_days. NUMERIC_MIN_VALUE 0 NUMERIC_MAX_VALUE 4294967295 NUMERIC_BLOCK_SIZE 1 diff --git a/sql/mysqld.cc b/sql/mysqld.cc index fe62382b14ef0..5c01a44794afd 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -5956,16 +5956,6 @@ int mysqld_main(int argc, char **argv) SYSVAR_AUTOSIZE(global_system_variables.binlog_format, BINLOG_FORMAT_ROW); } binlog_format_used= 1; - if (IS_SYSVAR_AUTOSIZE(&internal_slave_connections_needed_for_purge)) - { - slave_connections_needed_for_purge= - internal_slave_connections_needed_for_purge= 0; - SYSVAR_AUTOSIZE(internal_slave_connections_needed_for_purge, 0); - sql_print_information( - "slave_connections_needed_for_purge changed to 0 because " - "of Galera. Change it to 1 or higher if this Galera node " - "is also Master in a normal replication setup"); - } } #endif /* WITH_WSREP */ @@ -8353,6 +8343,11 @@ mysqld_get_one_option(const struct my_option *opt, const char *argument, } #ifdef HAVE_REPLICATION + case OPT_SLAVE_CONNECTIONS_NEEDED_FOR_PURGE: + warn_slaves_not_needed_for_purge.store(false, + std::memory_order_relaxed); // no other threads to sync with + break; + case (int)OPT_REPLICATE_IGNORE_DB: { cur_rpl_filter->add_ignore_db(argument); diff --git a/sql/mysqld.h b/sql/mysqld.h index 0b00c82e86863..4253d25a47b19 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -828,6 +828,7 @@ enum options_mysqld OPT_COSTS_ROWID_COPY_COST, OPT_EXPIRE_LOGS_DAYS, OPT_BINLOG_EXPIRE_LOGS_SECONDS, + OPT_SLAVE_CONNECTIONS_NEEDED_FOR_PURGE, OPT_CONSOLE, OPT_DEBUG_SYNC_TIMEOUT, OPT_REMOVED_OPTION, diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 164ae02df4d7f..bd572343c3d1f 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -50,6 +50,7 @@ struct Slave_info }; +std::atomic warn_slaves_not_needed_for_purge= {true}; Atomic_counter binlog_dump_thread_count; ulong rpl_status=RPL_NULL; mysql_mutex_t LOCK_rpl_status; @@ -120,6 +121,13 @@ int THD::register_slave(uchar *packet, size_t packet_length) Slave_info *si; uchar *p= packet, *p_end= packet + packet_length; const char *errmsg= "Wrong parameters to function register_slave"; + if (variables.log_warnings >= 1 && // Verbosity Level "Binlog/Replication" + warn_slaves_not_needed_for_purge.exchange(false, + // no need to sync with `@@slave_connections_needed_for_purge` itself + std::memory_order_relaxed)) + sql_print_warning( + "`@@slave_connections_needed_for_purge` defaults to 0, which means " + "this master does not retain older logs even if slaves need them."); if (check_access(this, PRIV_COM_REGISTER_SLAVE, any_db.str, NULL,NULL,0,0)) return 1; diff --git a/sql/sql_repl.h b/sql/sql_repl.h index c03384aa5a3e1..8fb44b5d6406d 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -23,6 +23,15 @@ struct slave_connection_state; +/** + Whether to log a warning once about an unfit + @ref slave_connections_needed_for_purge upon @ref COM_REGISTER_SLAVE + * Starts as `true` (do warn), and becomes `false` + (don't warn) when warning for the first time + * Also becomes `false` when the user intetionally sets + `@@slave_connections_needed_for_purge` +*/ +extern std::atomic warn_slaves_not_needed_for_purge; extern my_bool opt_show_slave_auth_info; extern char *master_host, *master_info_file; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 05a7df0fbdb18..3402599499354 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1326,13 +1326,23 @@ Sys_slave_connections_needed_for_purge( "slave_connections_needed_for_purge", "Minimum number of connected slaves required for automatic binary " "log purge with max_binlog_total_size, binlog_expire_logs_seconds " - "or binlog_expire_logs_days. Default is 0 when Galera is enabled and 1 " - "otherwise.", + "or binlog_expire_logs_days.", GLOBAL_VAR(internal_slave_connections_needed_for_purge), - CMD_LINE(REQUIRED_ARG), - VALID_RANGE(0, UINT_MAX), DEFAULT(1), BLOCK_SIZE(1), - NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), - ON_UPDATE(update_binlog_space_limit)); + CMD_LINE(REQUIRED_ARG, OPT_SLAVE_CONNECTIONS_NEEDED_FOR_PURGE), + VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, +#ifdef HAVE_REPLICATION + ON_CHECK([](sys_var *, THD *, set_var *) + { + warn_slaves_not_needed_for_purge.store(false, + // no need to sync with the variable itself + std::memory_order_relaxed); + return false; + }) +#else + ON_CHECK(0) +#endif + , ON_UPDATE(update_binlog_space_limit)); static Sys_var_mybool Sys_flush(