diff options
author | anozdrin/alik@quad. <> | 2008-03-12 17:44:40 +0300 |
---|---|---|
committer | anozdrin/alik@quad. <> | 2008-03-12 17:44:40 +0300 |
commit | 18125abf930b8fd5676c11218cd869d49dcd11a2 (patch) | |
tree | fe7296933dc1d17ddfb87e35b61bf5c0a3ed4684 | |
parent | e57679c2041ea549a1f6f1a4c1d7198543522907 (diff) | |
download | mariadb-git-18125abf930b8fd5676c11218cd869d49dcd11a2.tar.gz |
Fix for Bug#33507: Event scheduler creates more threads
than max_connections -- which results in user lockout.
The problem was that the variable thread_count that contains
the number of active threads was interpreted as a number of
active connections.
The fix is to introduce a new counter for active connections.
-rw-r--r-- | mysql-test/r/connect.result | 58 | ||||
-rw-r--r-- | mysql-test/t/connect.test | 127 | ||||
-rw-r--r-- | sql/mysql_priv.h | 5 | ||||
-rw-r--r-- | sql/mysqld.cc | 42 | ||||
-rw-r--r-- | sql/sql_connect.cc | 13 |
5 files changed, 231 insertions, 14 deletions
diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result index 25cf4f90e6d..de4eb28c500 100644 --- a/mysql-test/r/connect.result +++ b/mysql-test/r/connect.result @@ -115,3 +115,61 @@ create temporary table t2(id integer not null auto_increment primary key); set @id := 1; delete from t1 where id like @id; drop table t1; +# ------------------------------------------------------------------ +# -- End of 4.1 tests +# ------------------------------------------------------------------ + +# -- Bug#33507: Event scheduler creates more threads than max_connections +# -- which results in user lockout. + +GRANT USAGE ON *.* TO mysqltest_u1@localhost; + +SET GLOBAL max_connections = 3; +SET GLOBAL event_scheduler = ON; + +# -- Waiting for old connections to close... + + +# -- Disconnecting default connection... + +# -- Check that we allow exactly three user connections, no matter how +# -- many threads are running. + +# -- Connecting (1)... + +# -- Waiting for root connection to close... + +# -- Connecting (2)... +# -- Connecting (3)... +# -- Connecting (4)... +ERROR 08004: Too many connections + +# -- Waiting for the last connection to close... + +# -- Check that we allow one extra SUPER-user connection. + +# -- Connecting super (1)... +# -- Connecting super (2)... +ERROR 00000: Too many connections + +SELECT user FROM information_schema.processlist ORDER BY id; +user +event_scheduler +mysqltest_u1 +mysqltest_u1 +mysqltest_u1 +root + +# -- Resetting variables... +SET GLOBAL max_connections = 151; +SET GLOBAL event_scheduler = OFF; + +# -- That's it. Closing connections... + +# -- Restoring default connection... + +# -- End of Bug#33507. + +# ------------------------------------------------------------------ +# -- End of 5.1 tests +# ------------------------------------------------------------------ diff --git a/mysql-test/t/connect.test b/mysql-test/t/connect.test index 2e66c24d877..9e8f0d9b115 100644 --- a/mysql-test/t/connect.test +++ b/mysql-test/t/connect.test @@ -102,4 +102,129 @@ disconnect con7; connection default; drop table t1; -# End of 4.1 tests +--disconnect con1 +--disconnect con2 +--disconnect con3 +--disconnect con4 +--disconnect con5 +--disconnect con6 +--disconnect con10 + +--echo # ------------------------------------------------------------------ +--echo # -- End of 4.1 tests +--echo # ------------------------------------------------------------------ + +--echo +--echo # -- Bug#33507: Event scheduler creates more threads than max_connections +--echo # -- which results in user lockout. +--echo + +GRANT USAGE ON *.* TO mysqltest_u1@localhost; + +# NOTE: if the test case fails sporadically due to spurious connections, +# consider disabling all users. + +--echo + +let $saved_max_connections = `SELECT @@global.max_connections`; + +SET GLOBAL max_connections = 3; +SET GLOBAL event_scheduler = ON; + +--echo +--echo # -- Waiting for old connections to close... +let $wait_condition = + SELECT COUNT(*) = 1 + FROM information_schema.processlist + WHERE db = 'test'; +--source include/wait_condition.inc + +--echo +let $wait_condition = + SELECT COUNT(*) = 1 + FROM information_schema.processlist + WHERE user = 'event_scheduler'; +--source include/wait_condition.inc +--echo + +--echo # -- Disconnecting default connection... +--disconnect default + +--echo +--echo # -- Check that we allow exactly three user connections, no matter how +--echo # -- many threads are running. +--echo + +--echo # -- Connecting (1)... +--connect (con_1,localhost,mysqltest_u1) + +--echo +--echo # -- Waiting for root connection to close... +let $wait_condition = + SELECT COUNT(*) = 1 + FROM information_schema.processlist + WHERE db = 'test'; +--source include/wait_condition.inc +--echo + +--echo # -- Connecting (2)... +--connect (con_2,localhost,mysqltest_u1) + +--echo # -- Connecting (3)... +--connect (con_3,localhost,mysqltest_u1) + +--echo # -- Connecting (4)... +--disable_query_log +--error ER_CON_COUNT_ERROR +--connect (con_4,localhost,mysqltest_u1) +--enable_query_log + +--echo +--echo # -- Waiting for the last connection to close... +let $wait_condition = + SELECT COUNT(*) = 3 + FROM information_schema.processlist + WHERE db = 'test'; +--source include/wait_condition.inc + +--echo +--echo # -- Check that we allow one extra SUPER-user connection. +--echo + +--echo # -- Connecting super (1)... +--connect (con_super_1,localhost,root) + +--echo # -- Connecting super (2)... +--disable_query_log +--error ER_CON_COUNT_ERROR +--connect (con_super_2,localhost,root) +--enable_query_log + +--echo +# Ensure that we have Event Scheduler thread, 3 ordinary user connections and +# one extra super-user connection. +SELECT user FROM information_schema.processlist ORDER BY id; + +--echo +--echo # -- Resetting variables... + +--eval SET GLOBAL max_connections = $saved_max_connections +SET GLOBAL event_scheduler = OFF; + +--echo +--echo # -- That's it. Closing connections... +--disconnect con_1 +--disconnect con_2 +--disconnect con_super_1 + +--echo +--echo # -- Restoring default connection... +--connect (default,localhost,root,,test) + +--echo +--echo # -- End of Bug#33507. +--echo + +--echo # ------------------------------------------------------------------ +--echo # -- End of 5.1 tests +--echo # ------------------------------------------------------------------ diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b52e5aa745c..de40004fc6d 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -974,8 +974,6 @@ void time_out_user_resource_limits(THD *thd, USER_CONN *uc); void decrease_user_connections(USER_CONN *uc); void thd_init_client_charset(THD *thd, uint cs_number); bool setup_connection_thread_globals(THD *thd); -bool login_connection(THD *thd); -void end_connection(THD *thd); bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent); bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create); @@ -1895,6 +1893,7 @@ extern bool opt_disable_networking, opt_skip_show_db; extern my_bool opt_character_set_client_handshake; extern bool volatile abort_loop, shutdown_in_progress; extern uint volatile thread_count, thread_running, global_read_lock; +extern uint connection_count; extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types; extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap; extern my_bool opt_slave_compressed_protocol, use_temp_pool; @@ -1933,7 +1932,7 @@ extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open, LOCK_lock_db, LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_global_read_lock, LOCK_global_system_variables, LOCK_user_conn, LOCK_prepared_stmt_count, - LOCK_bytes_sent, LOCK_bytes_received; + LOCK_bytes_sent, LOCK_bytes_received, LOCK_connection_count; #ifdef HAVE_OPENSSL extern pthread_mutex_t LOCK_des_key_file; #endif diff --git a/sql/mysqld.cc b/sql/mysqld.cc index cdd08be6573..fd9ce9e1cea 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -584,7 +584,8 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count, LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received, LOCK_global_system_variables, - LOCK_user_conn, LOCK_slave_list, LOCK_active_mi; + LOCK_user_conn, LOCK_slave_list, LOCK_active_mi, + LOCK_connection_count; /** The below lock protects access to two global server variables: max_prepared_stmt_count and prepared_stmt_count. These variables @@ -720,6 +721,11 @@ char *des_key_file; struct st_VioSSLFd *ssl_acceptor_fd; #endif /* HAVE_OPENSSL */ +/** + Number of currently active user connections. The variable is protected by + LOCK_connection_count. +*/ +uint connection_count= 0; /* Function declarations */ @@ -1341,6 +1347,7 @@ static void clean_up_mutexes() (void) pthread_mutex_destroy(&LOCK_bytes_sent); (void) pthread_mutex_destroy(&LOCK_bytes_received); (void) pthread_mutex_destroy(&LOCK_user_conn); + (void) pthread_mutex_destroy(&LOCK_connection_count); Events::destroy_mutexes(); #ifdef HAVE_OPENSSL (void) pthread_mutex_destroy(&LOCK_des_key_file); @@ -1783,6 +1790,11 @@ void unlink_thd(THD *thd) DBUG_ENTER("unlink_thd"); DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd)); thd->cleanup(); + + pthread_mutex_lock(&LOCK_connection_count); + --connection_count; + pthread_mutex_unlock(&LOCK_connection_count); + (void) pthread_mutex_lock(&LOCK_thread_count); thread_count--; delete thd; @@ -3453,6 +3465,7 @@ static int init_thread_environment() (void) pthread_mutex_init(&LOCK_global_read_lock, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_prepared_stmt_count, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_uuid_generator, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_connection_count, MY_MUTEX_INIT_FAST); #ifdef HAVE_OPENSSL (void) pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST); #ifndef HAVE_YASSL @@ -4700,6 +4713,11 @@ void create_thread_to_handle_connection(THD *thd) thread_count--; thd->killed= THD::KILL_CONNECTION; // Safety (void) pthread_mutex_unlock(&LOCK_thread_count); + + pthread_mutex_lock(&LOCK_connection_count); + --connection_count; + pthread_mutex_unlock(&LOCK_connection_count); + statistic_increment(aborted_connects,&LOCK_status); /* Can't use my_error() since store_globals has not been called. */ my_snprintf(error_message_buff, sizeof(error_message_buff), @@ -4739,15 +4757,31 @@ static void create_new_thread(THD *thd) if (protocol_version > 9) net->return_errno=1; - /* don't allow too many connections */ - if (thread_count - delayed_insert_threads >= max_connections+1 || abort_loop) + /* + Don't allow too many connections. We roughly check here that we allow + only (max_connections + 1) connections. + */ + + pthread_mutex_lock(&LOCK_connection_count); + + if (connection_count >= max_connections + 1 || abort_loop) { + pthread_mutex_unlock(&LOCK_connection_count); + DBUG_PRINT("error",("Too many connections")); close_connection(thd, ER_CON_COUNT_ERROR, 1); delete thd; DBUG_VOID_RETURN; } + + ++connection_count; + + pthread_mutex_unlock(&LOCK_connection_count); + + /* Start a new thread to handle connection. */ + pthread_mutex_lock(&LOCK_thread_count); + /* The initialization of thread_id is done in create_embedded_thd() for the embedded library. @@ -4755,13 +4789,13 @@ static void create_new_thread(THD *thd) */ thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; - /* Start a new thread to handle connection */ thread_count++; if (thread_count - delayed_insert_threads > max_used_connections) max_used_connections= thread_count - delayed_insert_threads; thread_scheduler.add_connection(thd); + DBUG_VOID_RETURN; } #endif /* EMBEDDED_LIBRARY */ diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index b22a33e3e92..6f8cd6494cd 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -402,10 +402,11 @@ check_user(THD *thd, enum enum_server_command command, if (check_count) { - VOID(pthread_mutex_lock(&LOCK_thread_count)); - bool count_ok= thread_count <= max_connections + delayed_insert_threads - || (thd->main_security_ctx.master_access & SUPER_ACL); - VOID(pthread_mutex_unlock(&LOCK_thread_count)); + pthread_mutex_lock(&LOCK_connection_count); + bool count_ok= connection_count <= max_connections || + (thd->main_security_ctx.master_access & SUPER_ACL); + VOID(pthread_mutex_unlock(&LOCK_connection_count)); + if (!count_ok) { // too many connections my_error(ER_CON_COUNT_ERROR, MYF(0)); @@ -930,7 +931,7 @@ bool setup_connection_thread_globals(THD *thd) */ -bool login_connection(THD *thd) +static bool login_connection(THD *thd) { NET *net= &thd->net; int error; @@ -968,7 +969,7 @@ bool login_connection(THD *thd) This mainly updates status variables */ -void end_connection(THD *thd) +static void end_connection(THD *thd) { NET *net= &thd->net; plugin_thdvar_cleanup(thd); |