From af22a70df98a79804e8fa34d8f0af58deee162a3 Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Tue, 21 Feb 2017 16:28:42 +0400 Subject: [PATCH] MDEV-11418 - AliSQL: [Feature] Issue#1 KILL IDLE TRANSACTIONS Terminate idle transactions safely in server layer by setting up socket timeout parameter. Percona provides another patch to resolve similar problem, but it calls server layer's callback in InnoDB plugin to close THD, which crashes in some testcases. See https://bugs.launchpad.net/percona-server/+bug/901060 for more detailed information. 1. export parameter trx_idle_timeout to handle all kinds of transactions, the priority is highest 2. export parameter trx_readonly_idle_timeout to handle read-only transactions 3. export parameter trx_changes_idle_timeout to handle read-write transactions --- include/thread_pool_priv.h | 1 - mysql-test/r/mysqld--help.result | 12 +++++ mysql-test/r/transaction_timeout.result | 51 ++++++++++++++++++ .../sys_vars/r/sysvars_server_embedded.result | 42 +++++++++++++++ .../r/sysvars_server_notembedded.result | 42 +++++++++++++++ mysql-test/t/transaction_timeout.test | 54 +++++++++++++++++++ sql/handler.h | 12 ++++- sql/sql_class.h | 27 ++++++++++ sql/sql_parse.cc | 4 +- sql/sys_vars.cc | 21 ++++++++ sql/threadpool_common.cc | 2 +- 11 files changed, 263 insertions(+), 5 deletions(-) create mode 100644 mysql-test/r/transaction_timeout.result create mode 100644 mysql-test/t/transaction_timeout.test diff --git a/include/thread_pool_priv.h b/include/thread_pool_priv.h index afa2848ae88cd..f5fdbfbdf47bb 100644 --- a/include/thread_pool_priv.h +++ b/include/thread_pool_priv.h @@ -58,7 +58,6 @@ int thd_connection_has_data(THD *thd); void thd_set_net_read_write(THD *thd, uint val); uint thd_get_net_read_write(THD *thd); void thd_set_mysys_var(THD *thd, st_my_thread_var *mysys_var); -ulong thd_get_net_wait_timeout(THD *thd); my_socket thd_get_fd(THD *thd); int thd_store_globals(THD* thd); diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result index 2541687a72975..09120e42702bc 100644 --- a/mysql-test/r/mysqld--help.result +++ b/mysql-test/r/mysqld--help.result @@ -271,6 +271,15 @@ The following options may be given as the first argument: height-balanced. --host-cache-size=# How many host names should be cached to avoid resolving. (Automatically configured unless set explicitly) + --idle-readonly-transaction-timeout=# + The number of seconds the server waits for read-only idle + transaction + --idle-readwrite-transaction-timeout=# + The number of seconds the server waits for read-write + idle transaction + --idle-transaction-timeout=# + The number of seconds the server waits for idle + transaction --ignore-builtin-innodb Disable initialization of builtin InnoDB plugin --ignore-db-dirs=name @@ -1259,6 +1268,9 @@ help TRUE histogram-size 0 histogram-type SINGLE_PREC_HB host-cache-size 279 +idle-readonly-transaction-timeout 0 +idle-readwrite-transaction-timeout 0 +idle-transaction-timeout 0 ignore-builtin-innodb FALSE ignore-db-dirs init-connect diff --git a/mysql-test/r/transaction_timeout.result b/mysql-test/r/transaction_timeout.result new file mode 100644 index 0000000000000..c8db4a0e7441b --- /dev/null +++ b/mysql-test/r/transaction_timeout.result @@ -0,0 +1,51 @@ +CREATE TABLE t1 (a INT) ENGINE=InnoDB; +# Test idle_transaction_timeout +connect c0,localhost,root,,test,,; +SHOW VARIABLES LIKE 'idle_%transaction_timeout'; +Variable_name Value +idle_readonly_transaction_timeout 0 +idle_readwrite_transaction_timeout 0 +idle_transaction_timeout 0 +SET autocommit=0; +SET idle_transaction_timeout=1; +BEGIN; +SELECT * FROM t1; +a +SELECT * FROM t1; +Got one of the listed errors +disconnect c0; +# Test idle_readonly_transaction_timeout +connect c1,localhost,root,,test,,; +SHOW VARIABLES LIKE 'idle_%transaction_timeout'; +Variable_name Value +idle_readonly_transaction_timeout 0 +idle_readwrite_transaction_timeout 0 +idle_transaction_timeout 0 +SET autocommit=0; +SET idle_readonly_transaction_timeout=1; +BEGIN; +SELECT * FROM t1; +a +SELECT * FROM t1; +Got one of the listed errors +disconnect c1; +# Test idle_readwrite_transaction_timeout +connect c2,localhost,root,,test,,; +SHOW VARIABLES LIKE 'idle_%transaction_timeout'; +Variable_name Value +idle_readonly_transaction_timeout 0 +idle_readwrite_transaction_timeout 0 +idle_transaction_timeout 0 +SET autocommit=0; +SET idle_readwrite_transaction_timeout=1; +BEGIN; +SELECT * FROM t1; +a +SELECT * FROM t1; +a +INSERT INTO t1 VALUES (1); +SELECT * FROM t1; +Got one of the listed errors +disconnect c2; +connection default; +DROP TABLE t1; 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 7813391ad98c9..6c790e387d76d 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -1213,6 +1213,48 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY NO COMMAND_LINE_ARGUMENT NULL +VARIABLE_NAME IDLE_READONLY_TRANSACTION_TIMEOUT +SESSION_VALUE 0 +GLOBAL_VALUE 0 +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE 0 +VARIABLE_SCOPE SESSION +VARIABLE_TYPE INT UNSIGNED +VARIABLE_COMMENT The number of seconds the server waits for read-only idle transaction +NUMERIC_MIN_VALUE 0 +NUMERIC_MAX_VALUE 31536000 +NUMERIC_BLOCK_SIZE 1 +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED +VARIABLE_NAME IDLE_READWRITE_TRANSACTION_TIMEOUT +SESSION_VALUE 0 +GLOBAL_VALUE 0 +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE 0 +VARIABLE_SCOPE SESSION +VARIABLE_TYPE INT UNSIGNED +VARIABLE_COMMENT The number of seconds the server waits for read-write idle transaction +NUMERIC_MIN_VALUE 0 +NUMERIC_MAX_VALUE 31536000 +NUMERIC_BLOCK_SIZE 1 +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED +VARIABLE_NAME IDLE_TRANSACTION_TIMEOUT +SESSION_VALUE 0 +GLOBAL_VALUE 0 +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE 0 +VARIABLE_SCOPE SESSION +VARIABLE_TYPE INT UNSIGNED +VARIABLE_COMMENT The number of seconds the server waits for idle transaction +NUMERIC_MIN_VALUE 0 +NUMERIC_MAX_VALUE 31536000 +NUMERIC_BLOCK_SIZE 1 +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME IGNORE_BUILTIN_INNODB SESSION_VALUE NULL GLOBAL_VALUE OFF 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 a999112ab4182..5a465930364b6 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -1325,6 +1325,48 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY NO COMMAND_LINE_ARGUMENT NULL +VARIABLE_NAME IDLE_READONLY_TRANSACTION_TIMEOUT +SESSION_VALUE 0 +GLOBAL_VALUE 0 +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE 0 +VARIABLE_SCOPE SESSION +VARIABLE_TYPE INT UNSIGNED +VARIABLE_COMMENT The number of seconds the server waits for read-only idle transaction +NUMERIC_MIN_VALUE 0 +NUMERIC_MAX_VALUE 31536000 +NUMERIC_BLOCK_SIZE 1 +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED +VARIABLE_NAME IDLE_READWRITE_TRANSACTION_TIMEOUT +SESSION_VALUE 0 +GLOBAL_VALUE 0 +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE 0 +VARIABLE_SCOPE SESSION +VARIABLE_TYPE INT UNSIGNED +VARIABLE_COMMENT The number of seconds the server waits for read-write idle transaction +NUMERIC_MIN_VALUE 0 +NUMERIC_MAX_VALUE 31536000 +NUMERIC_BLOCK_SIZE 1 +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED +VARIABLE_NAME IDLE_TRANSACTION_TIMEOUT +SESSION_VALUE 0 +GLOBAL_VALUE 0 +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE 0 +VARIABLE_SCOPE SESSION +VARIABLE_TYPE INT UNSIGNED +VARIABLE_COMMENT The number of seconds the server waits for idle transaction +NUMERIC_MIN_VALUE 0 +NUMERIC_MAX_VALUE 31536000 +NUMERIC_BLOCK_SIZE 1 +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME IGNORE_BUILTIN_INNODB SESSION_VALUE NULL GLOBAL_VALUE OFF diff --git a/mysql-test/t/transaction_timeout.test b/mysql-test/t/transaction_timeout.test new file mode 100644 index 0000000000000..36d835cc38155 --- /dev/null +++ b/mysql-test/t/transaction_timeout.test @@ -0,0 +1,54 @@ +--source include/no_protocol.inc +--source include/have_innodb.inc +--source include/not_embedded.inc + +CREATE TABLE t1 (a INT) ENGINE=InnoDB; + +--echo # Test idle_transaction_timeout +connect (c0,localhost,root,,test,,); +SHOW VARIABLES LIKE 'idle_%transaction_timeout'; +SET autocommit=0; +SET idle_transaction_timeout=1; + +BEGIN; +SELECT * FROM t1; +sleep 2; + +--error 2006,2013 +SELECT * FROM t1; +disconnect c0; + +--echo # Test idle_readonly_transaction_timeout +connect (c1,localhost,root,,test,,); +SHOW VARIABLES LIKE 'idle_%transaction_timeout'; +SET autocommit=0; +SET idle_readonly_transaction_timeout=1; + +BEGIN; +SELECT * FROM t1; +sleep 2; + +--error 2006,2013 # Gone away +SELECT * FROM t1; +disconnect c1; + +--echo # Test idle_readwrite_transaction_timeout +connect (c2,localhost,root,,test,,); +SHOW VARIABLES LIKE 'idle_%transaction_timeout'; +SET autocommit=0; +SET idle_readwrite_transaction_timeout=1; + +BEGIN; +SELECT * FROM t1; +sleep 2; + +SELECT * FROM t1; +INSERT INTO t1 VALUES (1); +sleep 2; + +--error 2006, 2013 # Gone away +SELECT * FROM t1; +disconnect c2; + +connection default; +DROP TABLE t1; diff --git a/sql/handler.h b/sql/handler.h index 98bfbb93dc910..2af3158f8401c 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1476,7 +1476,7 @@ struct THD_TRANS bool trans_did_wait() const { return (m_unsafe_rollback_flags & DID_WAIT) != 0; } - + bool is_trx_read_write() const; }; @@ -1576,6 +1576,16 @@ class Ha_trx_info }; +inline bool THD_TRANS::is_trx_read_write() const +{ + Ha_trx_info *ha_info; + for (ha_info= ha_list; ha_info; ha_info= ha_info->next()) + if (ha_info->is_trx_read_write()) + return TRUE; + return FALSE; +} + + enum enum_tx_isolation { ISO_READ_UNCOMMITTED, ISO_READ_COMMITTED, ISO_REPEATABLE_READ, ISO_SERIALIZABLE}; diff --git a/sql/sql_class.h b/sql/sql_class.h index d5afa0a89dd18..d96adc892f05d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -695,6 +695,10 @@ typedef struct system_variables my_bool session_track_state_change; ulong threadpool_priority; + + uint idle_transaction_timeout; + uint idle_readonly_transaction_timeout; + uint idle_readwrite_transaction_timeout; } SV; /** @@ -4287,6 +4291,29 @@ class THD :public Statement, current_linfo= 0; mysql_mutex_unlock(&LOCK_thread_count); } + + + uint get_net_wait_timeout() + { + if (in_active_multi_stmt_transaction()) + { + if (transaction.all.is_trx_read_write()) + { + if (variables.idle_readwrite_transaction_timeout > 0) + return variables.idle_readwrite_transaction_timeout; + } + else + { + if (variables.idle_readonly_transaction_timeout > 0) + return variables.idle_readonly_transaction_timeout; + } + + if (variables.idle_transaction_timeout > 0) + return variables.idle_transaction_timeout; + } + + return variables.net_wait_timeout; + } }; inline void add_to_active_threads(THD *thd) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 66939470bfb3a..7c172e2814bcc 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1210,8 +1210,8 @@ bool do_command(THD *thd) the client, the connection is closed or "net_wait_timeout" number of seconds has passed. */ - if(!thd->skip_wait_timeout) - my_net_set_read_timeout(net, thd->variables.net_wait_timeout); + if (!thd->skip_wait_timeout) + my_net_set_read_timeout(net, thd->get_net_wait_timeout()); /* XXX: this code is here only to clear possible errors of init_connect. diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 2608b665ae64f..7a69481b2af0f 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -3519,6 +3519,27 @@ static Sys_var_ulong Sys_net_wait_timeout( VALID_RANGE(1, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)), DEFAULT(NET_WAIT_TIMEOUT), BLOCK_SIZE(1)); +static Sys_var_uint Sys_idle_transaction_timeout( + "idle_transaction_timeout", + "The number of seconds the server waits for idle transaction", + SESSION_VAR(idle_transaction_timeout), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)), + DEFAULT(0), BLOCK_SIZE(1)); + +static Sys_var_uint Sys_idle_readonly_transaction_timeout( + "idle_readonly_transaction_timeout", + "The number of seconds the server waits for read-only idle transaction", + SESSION_VAR(idle_readonly_transaction_timeout), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)), + DEFAULT(0), BLOCK_SIZE(1)); + +static Sys_var_uint Sys_idle_readwrite_transaction_timeout( + "idle_readwrite_transaction_timeout", + "The number of seconds the server waits for read-write idle transaction", + SESSION_VAR(idle_readwrite_transaction_timeout), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)), + DEFAULT(0), BLOCK_SIZE(1)); + static Sys_var_plugin Sys_default_storage_engine( "default_storage_engine", "The default storage engine for new tables", SESSION_VAR(table_plugin), NO_CMD_LINE, diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc index 643ff80de2a2c..44e483dfccfcc 100644 --- a/sql/threadpool_common.cc +++ b/sql/threadpool_common.cc @@ -199,7 +199,7 @@ void tp_callback(TP_connection *c) c->priority= get_priority(c); /* Read next command from client. */ - c->set_io_timeout(thd->variables.net_wait_timeout); + c->set_io_timeout(thd->get_net_wait_timeout()); c->state= TP_STATE_IDLE; if (c->start_io()) goto error;