Browse Source
MDEV-25975 innodb_disallow_writes causes shutdown to hang
MDEV-25975 innodb_disallow_writes causes shutdown to hang
We will remove the parameter innodb_disallow_writes because it is badly designed and implemented. The parameter was never allowed at startup. It was only internally used by Galera snapshot transfer. If a user executed SET GLOBAL innodb_disallow_writes=ON; the server could hang even on subsequent read operations. During Galera snapshot transfer, we will block writes to implement an rsync friendly snapshot, as follows: sst_flush_tables() will acquire a global lock by executing FLUSH TABLES WITH READ LOCK, which will block any writes at the high level. sst_disable_innodb_writes(), invoked via ha_disable_internal_writes(true), will suspend or disable InnoDB background tasks or threads that could initiate writes. As part of this, log_make_checkpoint() will be invoked to ensure that anything in the InnoDB buf_pool.flush_list will be written to the data files. This has the nice side effect that the Galera joiner will avoid crash recovery. The changes to sql/wsrep.cc and to the tests are based on a prototype that was developed by Jan Lindström. Reviewed by: Jan Lindströmpull/2088/head
30 changed files with 229 additions and 398 deletions
-
12extra/mariabackup/xtrabackup.cc
-
6mysql-test/include/have_innodb_disallow_writes.inc
-
11mysql-test/suite/galera/r/galera_bf_abort_shutdown.result
-
3mysql-test/suite/galera/r/galera_drop_database.result
-
1mysql-test/suite/galera/r/galera_events2.result
-
1mysql-test/suite/galera/r/galera_restart_on_unknown_option.result
-
28mysql-test/suite/galera/r/galera_var_innodb_disallow_writes.result
-
34mysql-test/suite/galera/t/galera_bf_abort_shutdown.test
-
1mysql-test/suite/galera/t/galera_drop_database.test
-
2mysql-test/suite/galera/t/galera_events2.test
-
1mysql-test/suite/galera/t/galera_restart_on_unknown_option.test
-
71mysql-test/suite/galera/t/galera_var_innodb_disallow_writes.test
-
45mysql-test/suite/sys_vars/r/innodb_disallow_writes_basic.result
-
2mysql-test/suite/sys_vars/r/sysvars_innodb.result
-
42mysql-test/suite/sys_vars/t/innodb_disallow_writes_basic.test
-
2mysql-test/suite/sys_vars/t/sysvars_innodb.test
-
14scripts/wsrep_sst_rsync.sh
-
16sql/handler.cc
-
5sql/handler.h
-
92sql/wsrep_sst.cc
-
12storage/innobase/dict/dict0stats_bg.cc
-
30storage/innobase/fts/fts0opt.cc
-
96storage/innobase/handler/ha_innodb.cc
-
7storage/innobase/include/srv0srv.h
-
8storage/innobase/innodb.cmake
-
20storage/innobase/log/log0log.cc
-
29storage/innobase/os/os0file.cc
-
2storage/innobase/row/row0merge.cc
-
28storage/innobase/srv/srv0srv.cc
-
6storage/innobase/srv/srv0start.cc
@ -1,6 +0,0 @@ |
|||
--source include/have_innodb.inc |
|||
|
|||
if (`SELECT COUNT(*) = 0 from INFORMATION_SCHEMA.GLOBAL_VARIABLES |
|||
WHERE VARIABLE_NAME = 'INNODB_DISALLOW_WRITES'`) { |
|||
--skip Test requires 'innodb_disallow_writes' |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
connection node_1; |
|||
connection node_2; |
|||
connection node_1; |
|||
CREATE TABLE t1 (f1 INT PRIMARY KEY); |
|||
connection node_2; |
|||
call mtr.add_suppression("WSREP: Failed to scan the last segment to the end. Last events may be missing. Last recovered event:.*"); |
|||
SET DEBUG_SYNC = 'wsrep_before_certification WAIT_FOR continue'; |
|||
INSERT INTO t1 VALUES (1); |
|||
connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2; |
|||
connection node_1; |
|||
DROP TABLE t1; |
|||
@ -1,28 +0,0 @@ |
|||
connection node_1a; |
|||
SET SESSION wsrep_sync_wait = 0; |
|||
connection node_1; |
|||
CREATE TABLE t1 (f1 INTEGER, f2 varchar(1024)) Engine=InnoDB; |
|||
CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB; |
|||
INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); |
|||
SET GLOBAL innodb_disallow_writes=ON; |
|||
INSERT INTO t1 (f2) SELECT 'abcde ' FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4;; |
|||
connection node_2; |
|||
INSERT INTO t1 (f2) SELECT 'fghij ' FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4; |
|||
SELECT COUNT(*) AS EXPECT_10000 FROM t1; |
|||
EXPECT_10000 |
|||
10000 |
|||
connection node_1a; |
|||
SET GLOBAL innodb_disallow_writes=OFF; |
|||
connection node_1; |
|||
SELECT COUNT(*) AS EXPECT_20000 FROM t1; |
|||
EXPECT_20000 |
|||
20000 |
|||
connection node_2; |
|||
SELECT COUNT(*) AS EXPECT_20000 FROM t1; |
|||
EXPECT_20000 |
|||
20000 |
|||
connection node_1; |
|||
connection node_2; |
|||
DROP TABLE t1; |
|||
DROP TABLE ten; |
|||
disconnect node_1a; |
|||
@ -0,0 +1,34 @@ |
|||
# |
|||
# This test verifies that the server can be shut down even if |
|||
# some of the wsrep transactions are in QUERY_COMMITTING state. |
|||
# In this case the shutdown sequence may do a BF abort for the |
|||
# connection. |
|||
# |
|||
|
|||
--source include/have_innodb.inc |
|||
--source include/galera_cluster.inc |
|||
--source include/have_debug_sync.inc |
|||
|
|||
# Save original auto_increment_offset values. |
|||
--let $node_1=node_1 |
|||
--let $node_2=node_2 |
|||
--source include/auto_increment_offset_save.inc |
|||
|
|||
--connection node_1 |
|||
CREATE TABLE t1 (f1 INT PRIMARY KEY); |
|||
|
|||
--connection node_2 |
|||
call mtr.add_suppression("WSREP: Failed to scan the last segment to the end. Last events may be missing. Last recovered event:.*"); |
|||
SET DEBUG_SYNC = 'wsrep_before_certification WAIT_FOR continue'; |
|||
--send INSERT INTO t1 VALUES (1) |
|||
|
|||
--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2 |
|||
--source include/restart_mysqld.inc |
|||
|
|||
# Restore original auto_increment_offset values. |
|||
--let $node_2=node_2a |
|||
--source include/auto_increment_offset_restore.inc |
|||
|
|||
--connection node_1 |
|||
|
|||
DROP TABLE t1; |
|||
@ -1,71 +0,0 @@ |
|||
# |
|||
# This test checks that innodb_disallow_writes works as expected |
|||
# |
|||
# Note that we need to enable binlog for this test: If the commit |
|||
# to InnoDB is done in one phase, the transaction is committed in |
|||
# memory before it is persisted to disk. This means that the |
|||
# innodb_disallow_writes=ON may not prevent transaction to |
|||
# become visible to other readers. On the other hand, if the |
|||
# commit is two phase (as it is with binlog), the transaction |
|||
# will be blocked in prepare phase. |
|||
# |
|||
|
|||
--source include/galera_cluster.inc |
|||
--source include/have_innodb.inc |
|||
|
|||
--let $datadir= `SELECT @@datadir` |
|||
|
|||
|
|||
# Open a separate connection to be used to run SHOW PROCESSLIST |
|||
--let $galera_connection_name = node_1a |
|||
--let $galera_server_number = 1 |
|||
--source include/galera_connect.inc |
|||
--connection node_1a |
|||
SET SESSION wsrep_sync_wait = 0; |
|||
|
|||
--connection node_1 |
|||
CREATE TABLE t1 (f1 INTEGER, f2 varchar(1024)) Engine=InnoDB; |
|||
CREATE TABLE ten (f1 INTEGER) ENGINE=InnoDB; |
|||
INSERT INTO ten VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); |
|||
|
|||
SET GLOBAL innodb_disallow_writes=ON; |
|||
--exec find $datadir -type f-exec md5sum {} \; | md5sum >$MYSQLTEST_VARDIR/tmp/innodb_before |
|||
|
|||
# |
|||
# This insert has no effect before innodb_disallow_writes is OFF |
|||
# |
|||
--send INSERT INTO t1 (f2) SELECT 'abcde ' FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4; |
|||
|
|||
--connection node_2 |
|||
INSERT INTO t1 (f2) SELECT 'fghij ' FROM ten AS a1, ten AS a2, ten AS a3, ten AS a4; |
|||
SELECT COUNT(*) AS EXPECT_10000 FROM t1; |
|||
|
|||
--connection node_1a |
|||
--sleep 5 |
|||
|
|||
--exec find $datadir -type f-exec md5sum {} \; | md5sum >$MYSQLTEST_VARDIR/tmp/innodb_after |
|||
|
|||
SET GLOBAL innodb_disallow_writes=OFF; |
|||
|
|||
--connection node_1 |
|||
--reap |
|||
--let $wait_condition = SELECT COUNT(*) = 20000 FROM t1; |
|||
--source include/wait_condition.inc |
|||
|
|||
SELECT COUNT(*) AS EXPECT_20000 FROM t1; |
|||
|
|||
--connection node_2 |
|||
--let $wait_condition = SELECT COUNT(*) = 20000 FROM t1; |
|||
--source include/wait_condition.inc |
|||
SELECT COUNT(*) AS EXPECT_20000 FROM t1; |
|||
|
|||
--connection node_1 |
|||
--diff_files $MYSQLTEST_VARDIR/tmp/innodb_before $MYSQLTEST_VARDIR/tmp/innodb_after |
|||
|
|||
--connection node_2 |
|||
|
|||
DROP TABLE t1; |
|||
DROP TABLE ten; |
|||
|
|||
--disconnect node_1a |
|||
|
|||
@ -1,45 +0,0 @@ |
|||
# |
|||
# innodb_disallow_writes |
|||
# |
|||
# save the initial value |
|||
SET @innodb_disallow_writes_global_saved = @@global.innodb_disallow_writes; |
|||
# default |
|||
SELECT @@global.innodb_disallow_writes; |
|||
@@global.innodb_disallow_writes |
|||
0 |
|||
|
|||
# scope |
|||
SELECT @@session.innodb_disallow_writes; |
|||
ERROR HY000: Variable 'innodb_disallow_writes' is a GLOBAL variable |
|||
SET @@global.innodb_disallow_writes=OFF; |
|||
SELECT @@global.innodb_disallow_writes; |
|||
@@global.innodb_disallow_writes |
|||
0 |
|||
SET @@global.innodb_disallow_writes=ON; |
|||
SELECT @@global.innodb_disallow_writes; |
|||
@@global.innodb_disallow_writes |
|||
1 |
|||
|
|||
# valid values |
|||
SET @@global.innodb_disallow_writes='OFF'; |
|||
SELECT @@global.innodb_disallow_writes; |
|||
@@global.innodb_disallow_writes |
|||
0 |
|||
SET @@global.innodb_disallow_writes=ON; |
|||
SELECT @@global.innodb_disallow_writes; |
|||
@@global.innodb_disallow_writes |
|||
1 |
|||
SET @@global.innodb_disallow_writes=default; |
|||
SELECT @@global.innodb_disallow_writes; |
|||
@@global.innodb_disallow_writes |
|||
0 |
|||
|
|||
# invalid values |
|||
SET @@global.innodb_disallow_writes=NULL; |
|||
ERROR 42000: Variable 'innodb_disallow_writes' can't be set to the value of 'NULL' |
|||
SET @@global.innodb_disallow_writes='junk'; |
|||
ERROR 42000: Variable 'innodb_disallow_writes' can't be set to the value of 'junk' |
|||
|
|||
# restore the initial value |
|||
SET @@global.innodb_disallow_writes = @innodb_disallow_writes_global_saved; |
|||
# End of test |
|||
@ -1,42 +0,0 @@ |
|||
--source include/have_innodb_disallow_writes.inc |
|||
|
|||
--echo # |
|||
--echo # innodb_disallow_writes |
|||
--echo # |
|||
|
|||
--echo # save the initial value |
|||
SET @innodb_disallow_writes_global_saved = @@global.innodb_disallow_writes; |
|||
|
|||
--echo # default |
|||
SELECT @@global.innodb_disallow_writes; |
|||
|
|||
--echo |
|||
--echo # scope |
|||
--error ER_INCORRECT_GLOBAL_LOCAL_VAR |
|||
SELECT @@session.innodb_disallow_writes; |
|||
SET @@global.innodb_disallow_writes=OFF; |
|||
SELECT @@global.innodb_disallow_writes; |
|||
SET @@global.innodb_disallow_writes=ON; |
|||
SELECT @@global.innodb_disallow_writes; |
|||
|
|||
--echo |
|||
--echo # valid values |
|||
SET @@global.innodb_disallow_writes='OFF'; |
|||
SELECT @@global.innodb_disallow_writes; |
|||
SET @@global.innodb_disallow_writes=ON; |
|||
SELECT @@global.innodb_disallow_writes; |
|||
SET @@global.innodb_disallow_writes=default; |
|||
SELECT @@global.innodb_disallow_writes; |
|||
|
|||
--echo |
|||
--echo # invalid values |
|||
--error ER_WRONG_VALUE_FOR_VAR |
|||
SET @@global.innodb_disallow_writes=NULL; |
|||
--error ER_WRONG_VALUE_FOR_VAR |
|||
SET @@global.innodb_disallow_writes='junk'; |
|||
|
|||
--echo |
|||
--echo # restore the initial value |
|||
SET @@global.innodb_disallow_writes = @innodb_disallow_writes_global_saved; |
|||
|
|||
--echo # End of test |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue