1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

[MDEV-36841] Warn when slave timeout drops below heartbeat period

When CHANGE MASTER sets master_heartbeat_period to a value greater than
@@GLOBAL.slave_net_timeout, a ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX
warning is emitted. However, the reverse scenario, lowering
slave_net_timeout below an existing master_heartbeat_period, produced no
warning.

Add an on_check callback for slave_net_timeout that iterates all
Master_info entries and warns if any heartbeat period exceeds the
requested timeout. Connections that are not configured are skipped. A
DEFAULT heartbeat period is skipped unless --master-heartbeat-period was
given at startup: without it the period follows slave_net_timeout/2 and
can never exceed the timeout, but with it the period is a fixed value
and is checked like an explicit one.

The warning is reported with a new, generalized error
ER_OPERATION_INVALIDATES_CONFIGURED_SLAVE, which names the affected
replication connection and carries a parameterized description of the
misconfiguration. Naming the connection matters for multi-source: when
several connections exceed the new timeout, the user can see exactly
which ones are problematic. One warning is emitted per affected
connection.

Add a multi_source test, run with --master-heartbeat-period=30, that
configures two named connections with explicit heartbeat periods above
the timeout and a third left at DEFAULT, and verifies a distinct warning
is emitted for each.

All new code of the whole pull request, including one or several files
that are either new files or modified ones, are contributed under the
BSD-new license. I am contributing on behalf of my employer Amazon Web
Services, Inc.
This commit is contained in:
Tony Chen
2026-03-19 22:00:02 +00:00
committed by Georgi Kodinov
parent f6d172c03b
commit bcdb4c4c4a
9 changed files with 139 additions and 4 deletions

View File

@@ -14,6 +14,8 @@ show status like 'Slave_heartbeat_period';;
Variable_name Slave_heartbeat_period
Value 4.000
set @@global.slave_net_timeout= 3 /* must be a warning */;
Warnings:
Warning 4276 This operation leaves replication connection '' with an invalid configuration: the heartbeat period (4.000 seconds) exceeds the requested slave_net_timeout (3 seconds); a sensible value for the period should be less than the timeout
reset slave;
connection master;
drop table if exists t1;

View File

@@ -0,0 +1 @@
--master-heartbeat-period=30

View File

@@ -0,0 +1,17 @@
connect slave,127.0.0.1,root,,,$SERVER_MYPORT_3;
connection slave;
SET @restore_slave_net_timeout= @@global.slave_net_timeout;
SET @@global.slave_net_timeout= 60;
change master 'slave1' to master_port=MYPORT_1, master_host='127.0.0.1', master_user='root', master_ssl_verify_server_cert=0, master_heartbeat_period=40;
change master 'slave2' to master_port=MYPORT_2, master_host='127.0.0.1', master_user='root', master_ssl_verify_server_cert=0, master_heartbeat_period=50;
change master 'slave3' to master_port=MYPORT_1, master_host='localhost', master_user='root', master_ssl_verify_server_cert=0;
SET @@global.slave_net_timeout= 20;
SHOW WARNINGS;
Level Code Message
Warning 4276 This operation leaves replication connection 'slave1' with an invalid configuration: the heartbeat period (40.000 seconds) exceeds the requested slave_net_timeout (20 seconds); a sensible value for the period should be less than the timeout
Warning 4276 This operation leaves replication connection 'slave2' with an invalid configuration: the heartbeat period (50.000 seconds) exceeds the requested slave_net_timeout (20 seconds); a sensible value for the period should be less than the timeout
Warning 4276 This operation leaves replication connection 'slave3' with an invalid configuration: the heartbeat period (30.000 seconds) exceeds the requested slave_net_timeout (20 seconds); a sensible value for the period should be less than the timeout
SET @@global.slave_net_timeout= @restore_slave_net_timeout;
include/reset_master_slave.inc
disconnect slave;
connection default;

View File

@@ -0,0 +1,48 @@
#
# MDEV-36841: Lowering @@global.slave_net_timeout below an explicitly
# configured master_heartbeat_period warns once per replication connection.
#
# With multi-source replication there can be more than one connection whose
# heartbeat period exceeds the new timeout, so the warning names each affected
# connection. This test configures two named connections, each with an
# explicit heartbeat period above the timeout we set next, and verifies that a
# separate warning is emitted for both.
#
# The server is started with --master-heartbeat-period=30, so a third
# connection left at the DEFAULT heartbeat period resolves to a fixed 30
# seconds rather than slave_net_timeout/2, and must warn as well.
#
--source include/not_embedded.inc
--connect (slave,127.0.0.1,root,,,$SERVER_MYPORT_3)
--connection slave
SET @restore_slave_net_timeout= @@global.slave_net_timeout;
SET @@global.slave_net_timeout= 60;
# Two sources, each with an explicit heartbeat period below the current
# timeout (60), so configuring them does not warn.
--replace_result $SERVER_MYPORT_1 MYPORT_1
eval change master 'slave1' to master_port=$SERVER_MYPORT_1, master_host='127.0.0.1', master_user='root', master_ssl_verify_server_cert=0, master_heartbeat_period=40;
--replace_result $SERVER_MYPORT_2 MYPORT_2
eval change master 'slave2' to master_port=$SERVER_MYPORT_2, master_host='127.0.0.1', master_user='root', master_ssl_verify_server_cert=0, master_heartbeat_period=50;
# DEFAULT heartbeat period, i.e. --master-heartbeat-period=30. The
# connection is never started; 'localhost' only avoids the host:port
# conflict with 'slave1'.
--replace_result $SERVER_MYPORT_1 MYPORT_1
eval change master 'slave3' to master_port=$SERVER_MYPORT_1, master_host='localhost', master_user='root', master_ssl_verify_server_cert=0;
# Lowering the timeout below all three heartbeat periods must warn once per
# connection. Warnings are pushed in master_info hash order, which is not
# deterministic, so capture them with a sorted SHOW WARNINGS.
--disable_warnings
SET @@global.slave_net_timeout= 20;
--enable_warnings
--sorted_result
SHOW WARNINGS;
# Clean up
SET @@global.slave_net_timeout= @restore_slave_net_timeout;
--source include/reset_master_slave.inc
--disconnect slave
--connection default

View File

@@ -14,6 +14,8 @@ show status like 'Slave_heartbeat_period';;
Variable_name Slave_heartbeat_period
Value 4.000
set @@global.slave_net_timeout= 3 /* must be a warning */;
Warnings:
Warning 4276 This operation leaves replication connection '' with an invalid configuration: the heartbeat period (4.000 seconds) exceeds the requested slave_net_timeout (3 seconds); a sensible value for the period should be less than the timeout
reset slave;
connection master;
drop table if exists t1;

View File

@@ -54,11 +54,20 @@ Slave_heartbeat_period 25.000
SET @@global.slave_net_timeout=@restore_slave_net_timeout;
include/reset_slave.inc
*** Warning if updated slave_net_timeout < slave_heartbeat_timeout ***
*** No warning if slave_net_timeout lowered with default heartbeat_period ***
SET @@global.slave_net_timeout=FLOOR(SLAVE_HEARTBEAT_TIMEOUT)-1;
SET @@global.slave_net_timeout=@restore_slave_net_timeout;
RESET SLAVE;
*** Warning if slave_net_timeout set below explicit heartbeat_period ***
SET @@global.slave_net_timeout=60;
CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=MASTER_PORT, MASTER_USER='root', MASTER_CONNECT_RETRY=20, MASTER_HEARTBEAT_PERIOD=40;
SET @@global.slave_net_timeout=20;
Warnings:
Warning 4276 This operation leaves replication connection '' with an invalid configuration: the heartbeat period (40.000 seconds) exceeds the requested slave_net_timeout (20 seconds); a sensible value for the period should be less than the timeout
SET @@global.slave_net_timeout=@restore_slave_net_timeout;
RESET SLAVE;
*** Warning if updated slave_heartbeat_timeout > slave_net_timeout ***
CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=MASTER_PORT, MASTER_USER='root', MASTER_CONNECT_RETRY=20, MASTER_HEARTBEAT_PERIOD=SLAVE_NET_TIMEOUT;
Warnings:

View File

@@ -104,8 +104,8 @@ SET @@global.slave_net_timeout=@restore_slave_net_timeout;
--source include/reset_slave.inc
--echo
# Set slave_net_timeout less than current value of slave_heartbeat_period
--echo *** Warning if updated slave_net_timeout < slave_heartbeat_timeout ***
# No warning when heartbeat_period is DEFAULT (automatically slave_net_timeout/2)
--echo *** No warning if slave_net_timeout lowered with default heartbeat_period ***
let $slave_heartbeat_timeout= query_get_value(SHOW GLOBAL STATUS LIKE 'slave_heartbeat_period', Value, 1);
--replace_result $slave_heartbeat_timeout SLAVE_HEARTBEAT_TIMEOUT
eval SET @@global.slave_net_timeout=FLOOR($slave_heartbeat_timeout)-1;
@@ -113,6 +113,16 @@ SET @@global.slave_net_timeout=@restore_slave_net_timeout;
RESET SLAVE;
--echo
# Set slave_net_timeout less than explicitly set slave_heartbeat_period (MDEV-36841)
--echo *** Warning if slave_net_timeout set below explicit heartbeat_period ***
SET @@global.slave_net_timeout=60;
--replace_result $MASTER_MYPORT MASTER_PORT
eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=$MASTER_MYPORT, MASTER_USER='root', MASTER_CONNECT_RETRY=$connect_retry, MASTER_HEARTBEAT_PERIOD=40;
SET @@global.slave_net_timeout=20;
SET @@global.slave_net_timeout=@restore_slave_net_timeout;
RESET SLAVE;
--echo
# Set value of slave_heartbeat_period greater than slave_net_timeout
--echo *** Warning if updated slave_heartbeat_timeout > slave_net_timeout ***
let $slave_net_timeout= query_get_value(SHOW VARIABLES LIKE 'slave_net_timeout', Value, 1);

View File

@@ -12427,3 +12427,5 @@ ER_INVALID_HOST_NETMASK
eng "Invalid masked host '%s' for user '%s'"
ER_ACL_ENTRY_IGNORED_BAD_HOST_MASK
eng "'%s' entry '%s' ignored, the host is not a valid ip/netmask"
ER_OPERATION_INVALIDATES_CONFIGURED_SLAVE
eng "This operation leaves replication connection '%s' with an invalid configuration: %s"

View File

@@ -6151,13 +6151,57 @@ static Sys_var_uint Sys_opt_binlog_partial_rows_event_max_size(
CMD_LINE(REQUIRED_ARG), VALID_RANGE(1024, MAX_MAX_ALLOWED_PACKET),
DEFAULT(MAX_MAX_ALLOWED_PACKET), BLOCK_SIZE(1024));
static bool check_slave_net_timeout(sys_var *self, THD *thd, set_var *var)
{
uint timeout= (uint) var->save_result.ulonglong_value;
mysql_mutex_lock(&LOCK_active_mi);
if (master_info_index)
{
for (uint i= 0; i < master_info_index->master_info_hash.records; i++)
{
Master_info *mi= (Master_info *)
my_hash_element(&master_info_index->master_info_hash, i);
/*
Skip connections that were never configured (e.g. the implicit
default connection), as START SLAVE does.
A DEFAULTed heartbeat period without --master-heartbeat-period
follows slave_net_timeout/2 and so can never exceed the new timeout.
get_value() would still report half of the old timeout, so skip it.
*/
if (!*mi->host ||
(mi->master_heartbeat_period.is_default() &&
master_heartbeat_period_is_auto))
continue;
uint32 period= mi->master_heartbeat_period.get_value();
if (period <= timeout * 1000ULL)
continue;
const char *name= mi->connection_name.str ? mi->connection_name.str : "";
char detail[256];
my_snprintf(detail, sizeof(detail),
"the heartbeat period (%.3f seconds) exceeds the requested "
"slave_net_timeout (%u seconds); a sensible value for the "
"period should be less than the timeout",
period / 1000.0,
timeout);
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_OPERATION_INVALIDATES_CONFIGURED_SLAVE,
ER_THD(thd, ER_OPERATION_INVALIDATES_CONFIGURED_SLAVE),
name, detail);
}
}
mysql_mutex_unlock(&LOCK_active_mi);
return false;
}
static Sys_var_on_access_global<Sys_var_uint,
PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_NET_TIMEOUT>
Sys_slave_net_timeout(
"slave_net_timeout", "Number of seconds to wait for more data "
"from any master/slave connection before aborting the read",
GLOBAL_VAR(slave_net_timeout), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(SLAVE_NET_TIMEOUT), BLOCK_SIZE(1));
VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(SLAVE_NET_TIMEOUT), BLOCK_SIZE(1),
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_slave_net_timeout));
static Sys_var_uint Sys_master_connect_retry(