diff --git a/mysql-test/suite/rpl/r/mdev38829_semisync_write_fail.result b/mysql-test/suite/rpl/r/mdev38829_semisync_write_fail.result new file mode 100644 index 0000000000000..f1ffe58f266cb --- /dev/null +++ b/mysql-test/suite/rpl/r/mdev38829_semisync_write_fail.result @@ -0,0 +1,18 @@ +call mtr.add_suppression("Write to binary log failed*"); +SET GLOBAL rpl_semi_sync_master_enabled = 1; +SET GLOBAL rpl_semi_sync_master_wait_point = 'AFTER_SYNC'; +set @saved_dbug = @@global.debug_dbug; +SET GLOBAL debug_dbug='d,injecting_fault_writing'; +CREATE TABLE t1 (i INT); +CREATE TABLE t1 (i INT); +ERROR HY000: Error writing file 'master-bin' ((errno: #) +set @@global.debug_dbug = @saved_dbug; +SELECT 1; +1 +1 +SHOW STATUS LIKE 'Rpl_semi_sync_master_status'; +Variable_name Value +Rpl_semi_sync_master_status ON +SET GLOBAL rpl_semi_sync_master_enabled = 0; +SET GLOBAL rpl_semi_sync_master_wait_point = DEFAULT; +DROP TABLE IF EXISTS t1; diff --git a/mysql-test/suite/rpl/t/mdev38829_semisync_write_fail.test b/mysql-test/suite/rpl/t/mdev38829_semisync_write_fail.test new file mode 100644 index 0000000000000..65cd234012d9c --- /dev/null +++ b/mysql-test/suite/rpl/t/mdev38829_semisync_write_fail.test @@ -0,0 +1,36 @@ +# +# MDEV-38829: Assertion `rpl_semi_sync_master_off_times > +# thd->expected_semi_sync_offs' failed in Repl_semi_sync_master::commit_trx() +# +# A failed direct binlog write (write_gtid_event()/write_event() returning an +# error) left semisync registration skipped, but the semisync wait +# (wait_after_sync() -> commit_trx()) still ran unconditionally. commit_trx() +# then found no entry for the transaction and asserted that the only +# possible cause was a concurrent switch_off()/switch_on(), which is false +# here -- the entry was simply never registered. +# +# Verify that a failed direct binlog write no longer triggers a semisync +# wait for a transaction that was never registered: the query should just +# return a clean write error, and the server must remain fully responsive +# (in particular, no assertion failure / crash). +# +--source include/have_debug.inc +--source include/have_binlog_format_mixed_or_statement.inc + +call mtr.add_suppression("Write to binary log failed*"); + +SET GLOBAL rpl_semi_sync_master_enabled = 1; +SET GLOBAL rpl_semi_sync_master_wait_point = 'AFTER_SYNC'; + +--let $query= CREATE TABLE t1 (i INT) +--source include/binlog_inject_error.inc + +# The server must still be fully alive and responsive here -- this is the +# actual regression check: pre-fix, the statement above aborts the +# (debug-build) server via DBUG_ASSERT instead of returning cleanly. +SELECT 1; +SHOW STATUS LIKE 'Rpl_semi_sync_master_status'; + +SET GLOBAL rpl_semi_sync_master_enabled = 0; +SET GLOBAL rpl_semi_sync_master_wait_point = DEFAULT; +DROP TABLE IF EXISTS t1; diff --git a/sql/log.cc b/sql/log.cc index d70fe6b9170f7..72a8a9f91938a 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -7259,7 +7259,8 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) mysql_mutex_assert_owner(&LOCK_after_binlog_sync); mysql_mutex_assert_not_owner(&LOCK_commit_ordered); #ifdef HAVE_REPLICATION - if (repl_semisync_master.wait_after_sync(log_file_name, offset)) + if (likely(!error) && + repl_semisync_master.wait_after_sync(log_file_name, offset)) { error=1; /* error is already printed inside hook */