Skip to content

Commit

Permalink
Disconnect the connection instead of returning error on shutdown
Browse files Browse the repository at this point in the history
Summary:
Port to 8.0

During server shutdown, transactions may still be committed after
semisync has been disabled. The server should check and see if
the thread has been requested to be killed during commit(), and
disconnect the connection if so.

Differential Revision: D22932654

fbshipit-source-id: c0d362f1d93
  • Loading branch information
Herman Lee authored and inikep committed Nov 1, 2021
1 parent 8a8ed2f commit 93d5cbd
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 1 deletion.
14 changes: 14 additions & 0 deletions mysql-test/r/disconnect_on_shutdown.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
SET GLOBAL DEBUG="+d,commit_on_shutdown_testing";
CREATE TABLE t1 (pk INT, PRIMARY KEY (pk)) ENGINE=INNODB;
INSERT INTO t1 VALUES (1);
SET DEBUG_SYNC="commit_wait_for_shutdown SIGNAL here WAIT_FOR shutdown_started";
INSERT INTO t1 values (2);;
SET DEBUG_SYNC="now WAIT_FOR here";
# start the sql client and run shutdown sql command.
ERROR HY000: Lost connection to MySQL server during query
# restart the server
SELECT * FROM t1;
pk
1
2
DROP TABLE t1;
33 changes: 33 additions & 0 deletions mysql-test/r/disconnect_on_shutdown_semisync.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
set @save_master_timeout=@@global.rpl_semi_sync_master_timeout;
set @save_master_wait_no_slave=@@global.rpl_semi_sync_master_wait_no_slave;
set @save_master_enabled=@@global.rpl_semi_sync_master_enabled;
call mtr.add_suppression("SEMISYNC: Forced shutdown. Some updates might not be replicated.");
CREATE TABLE t1 (a INT) ENGINE = INNODB;
INSERT INTO t1 VALUES (1), (2);
[ enable semi-sync on master ]
set global rpl_semi_sync_master_timeout= 600000 /* 600s */;
set global rpl_semi_sync_master_enabled = 1;
show variables like 'rpl_semi_sync_master_enabled';
Variable_name Value
rpl_semi_sync_master_enabled ON
[ status of semi-sync on master should be ON even without any semi-sync slaves ]
show status like 'Rpl_semi_sync_master_clients';
Variable_name Value
Rpl_semi_sync_master_clients 0
show status like 'Rpl_semi_sync_master_status';
Variable_name Value
Rpl_semi_sync_master_status ON
show status like 'Rpl_semi_sync_master_yes_tx';
Variable_name Value
Rpl_semi_sync_master_yes_tx 0
Should wait for semisync ack
INSERT INTO t1 VALUES (3);
Checking for thread to wait
Shutting down the server
ERROR HY000: Lost connection to MySQL server during query
#
# Clean up
#
# restart the server
UNINSTALL PLUGIN rpl_semi_sync_master;
DROP TABLE t1;
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ include/stop_slave_io.inc
CREATE TABLE t(f INT) ENGINE=INNODB;;
[connection master1]
[connection master]
ERROR HY000: Lost connection to MySQL server during query
include/rpl_start_server.inc [server_number=1]
[connection slave]
include/start_slave_io.inc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ SET GLOBAL rpl_semi_sync_master_timeout = 10000000;
--enable_result_log

--source include/rpl_connection_master.inc
--error 0,2013
--error 2013
--reap
--let $rpl_server_number=1
--source include/rpl_start_server.inc
Expand Down
1 change: 1 addition & 0 deletions mysql-test/t/disconnect_on_shutdown-master.opt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--log-bin=1
34 changes: 34 additions & 0 deletions mysql-test/t/disconnect_on_shutdown.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--source include/have_debug_sync.inc

SET GLOBAL DEBUG="+d,commit_on_shutdown_testing";
CREATE TABLE t1 (pk INT, PRIMARY KEY (pk)) ENGINE=INNODB;

--connect(con1, localhost, root,,)
INSERT INTO t1 VALUES (1);

SET DEBUG_SYNC="commit_wait_for_shutdown SIGNAL here WAIT_FOR shutdown_started";
--send INSERT INTO t1 values (2);

connection default;

SET DEBUG_SYNC="now WAIT_FOR here";

--echo # start the sql client and run shutdown sql command.
--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--exec $MYSQL -e "shutdown;" 2>&1
--source include/wait_until_disconnected.inc

connection con1;
--error 2013
--reap
disconnect con1;

connection default;

--echo # restart the server
--exec echo "restart:" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--enable_reconnect
--source include/wait_until_connected_again.inc

SELECT * FROM t1;
DROP TABLE t1;
2 changes: 2 additions & 0 deletions mysql-test/t/disconnect_on_shutdown_semisync-master.opt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
$SEMISYNC_PLUGIN_OPT
--force-restart
68 changes: 68 additions & 0 deletions mysql-test/t/disconnect_on_shutdown_semisync.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
source include/have_semisync_plugin.inc;
source include/have_log_bin.inc;

disable_query_log;
let $value = query_get_value(show variables like 'rpl_semi_sync_master_enabled', Value, 1);
if ($value == No such row)
{
set sql_log_bin=0;
eval INSTALL PLUGIN rpl_semi_sync_master SONAME '$SEMISYNC_MASTER_PLUGIN';
}
set sql_log_bin=1;
enable_query_log;

set @save_master_timeout=@@global.rpl_semi_sync_master_timeout;
set @save_master_wait_no_slave=@@global.rpl_semi_sync_master_wait_no_slave;
set @save_master_enabled=@@global.rpl_semi_sync_master_enabled;

call mtr.add_suppression("SEMISYNC: Forced shutdown. Some updates might not be replicated.");
CREATE TABLE t1 (a INT) ENGINE = INNODB;
INSERT INTO t1 VALUES (1), (2);

echo [ enable semi-sync on master ];
set global rpl_semi_sync_master_timeout= 600000 /* 600s */;
set global rpl_semi_sync_master_enabled = 1;
show variables like 'rpl_semi_sync_master_enabled';

echo [ status of semi-sync on master should be ON even without any semi-sync slaves ];
show status like 'Rpl_semi_sync_master_clients';
show status like 'Rpl_semi_sync_master_status';
show status like 'Rpl_semi_sync_master_yes_tx';

connect (con1,localhost,root,,);
connection con1;
--echo Should wait for semisync ack
--send INSERT INTO t1 VALUES (3)

connection default;
--echo Checking for thread to wait
let $wait_condition=
select count(*)>0 from information_schema.processlist where state='Waiting for semi-sync ACK from slave';
source include/wait_condition.inc;

--echo Shutting down the server
--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--exec $MYSQL -e "shutdown;" 2>&1
--source include/wait_until_disconnected.inc

connection con1;
--error 2013
--reap
disconnect con1;

--echo #
--echo # Clean up
--echo #

connection default;

--echo # restart the server
--exec echo "restart:" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--enable_reconnect
--source include/wait_until_connected_again.inc

disable_warnings;
UNINSTALL PLUGIN rpl_semi_sync_master;
enable_warnings;

DROP TABLE t1;
9 changes: 9 additions & 0 deletions sql/binlog.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9834,6 +9834,15 @@ int MYSQL_BIN_LOG::finish_commit(THD *thd) {
DBUG_ASSERT(!thd_get_cache_mngr(thd)->dbug_any_finalized());
DBUG_PRINT("return", ("Thread ID: %u, commit_error: %d", thd->thread_id(),
thd->commit_error));
/*
During shutdown, forcibly disconnect thd connections for
transactions that are in the commit pipeline
*/
DEBUG_SYNC(thd, "commit_wait_for_shutdown");
if (!thd->slave_thread && thd->killed == THD::KILL_CONNECTION) {
thd->disconnect();
}

/*
flush or sync errors are handled by the leader of the group
(using binlog_error_action). Hence treat only COMMIT_ERRORs as errors.
Expand Down
12 changes: 12 additions & 0 deletions sql/mysqld.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2356,6 +2356,18 @@ static void close_connections(void) {

(void)RUN_HOOK(server_state, after_server_shutdown, (nullptr));

DBUG_EXECUTE_IF("commit_on_shutdown_testing", {
THD *thd = new THD();
thd->thread_stack = reinterpret_cast<char *>(&(thd));
thd->store_globals();

const char act[] = "now signal shutdown_started";
DBUG_ASSERT(!debug_sync_set_action(current_thd, STRING_WITH_LEN(act)));

thd->restore_globals();
delete thd;
};);

/*
All threads have now been aborted. Stop event scheduler thread
after aborting all client connections, otherwise user may
Expand Down

0 comments on commit 93d5cbd

Please sign in to comment.