Commit 18125abf authored by anozdrin/alik@quad's avatar anozdrin/alik@quad

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.
parent e57679c2
......@@ -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
# ------------------------------------------------------------------
......@@ -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 # ------------------------------------------------------------------
......@@ -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
......
......@@ -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 */
......
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment