341 lines
9.6 KiB
C++
341 lines
9.6 KiB
C++
/*
|
|
Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "connection_handler_manager.h"
|
|
|
|
#include "mysql/thread_pool_priv.h" // create_thd
|
|
#include "mysql/service_thd_wait.h"
|
|
#include "mysqld_error.h" // ER_*
|
|
#include "channel_info.h" // Channel_info
|
|
#include "connection_handler_impl.h" // Per_thread_connection_handler
|
|
#include "mysqld.h" // max_connections
|
|
#include "plugin_connection_handler.h" // Plugin_connection_handler
|
|
#include "sql_callback.h" // MYSQL_CALLBACK
|
|
#include "sql_class.h" // THD
|
|
|
|
|
|
// Initialize static members
|
|
uint Connection_handler_manager::connection_count= 0;
|
|
ulong Connection_handler_manager::max_used_connections= 0;
|
|
ulong Connection_handler_manager::max_used_connections_time= 0;
|
|
THD_event_functions* Connection_handler_manager::event_functions= NULL;
|
|
THD_event_functions* Connection_handler_manager::saved_event_functions= NULL;
|
|
mysql_mutex_t Connection_handler_manager::LOCK_connection_count;
|
|
mysql_cond_t Connection_handler_manager::COND_connection_count;
|
|
#ifndef EMBEDDED_LIBRARY
|
|
Connection_handler_manager* Connection_handler_manager::m_instance= NULL;
|
|
ulong Connection_handler_manager::thread_handling=
|
|
SCHEDULER_ONE_THREAD_PER_CONNECTION;
|
|
uint Connection_handler_manager::max_threads= 0;
|
|
|
|
|
|
/**
|
|
Helper functions to allow mysys to call the thread scheduler when
|
|
waiting for locks.
|
|
*/
|
|
|
|
static void scheduler_wait_lock_begin()
|
|
{
|
|
MYSQL_CALLBACK(Connection_handler_manager::event_functions,
|
|
thd_wait_begin, (current_thd, THD_WAIT_TABLE_LOCK));
|
|
}
|
|
|
|
static void scheduler_wait_lock_end()
|
|
{
|
|
MYSQL_CALLBACK(Connection_handler_manager::event_functions,
|
|
thd_wait_end, (current_thd));
|
|
}
|
|
|
|
static void scheduler_wait_sync_begin()
|
|
{
|
|
MYSQL_CALLBACK(Connection_handler_manager::event_functions,
|
|
thd_wait_begin, (current_thd, THD_WAIT_SYNC));
|
|
}
|
|
|
|
static void scheduler_wait_sync_end()
|
|
{
|
|
MYSQL_CALLBACK(Connection_handler_manager::event_functions,
|
|
thd_wait_end, (current_thd));
|
|
}
|
|
|
|
|
|
bool Connection_handler_manager::valid_connection_count()
|
|
{
|
|
bool connection_accepted= true;
|
|
mysql_mutex_lock(&LOCK_connection_count);
|
|
if (connection_count > max_connections)
|
|
{
|
|
connection_accepted= false;
|
|
m_connection_errors_max_connection++;
|
|
}
|
|
mysql_mutex_unlock(&LOCK_connection_count);
|
|
return connection_accepted;
|
|
}
|
|
|
|
|
|
bool Connection_handler_manager::check_and_incr_conn_count()
|
|
{
|
|
bool connection_accepted= true;
|
|
mysql_mutex_lock(&LOCK_connection_count);
|
|
/*
|
|
Here we allow max_connections + 1 clients to connect
|
|
(by checking before we increment by 1).
|
|
|
|
The last connection is reserved for SUPER users. This is
|
|
checked later during authentication where valid_connection_count()
|
|
is called for non-SUPER users only.
|
|
*/
|
|
if (connection_count > max_connections)
|
|
{
|
|
connection_accepted= false;
|
|
m_connection_errors_max_connection++;
|
|
}
|
|
else
|
|
{
|
|
++connection_count;
|
|
if (connection_count > max_used_connections)
|
|
{
|
|
max_used_connections= connection_count;
|
|
max_used_connections_time= (ulong)my_time(0);
|
|
}
|
|
}
|
|
mysql_mutex_unlock(&LOCK_connection_count);
|
|
return connection_accepted;
|
|
}
|
|
|
|
|
|
#ifdef HAVE_PSI_INTERFACE
|
|
static PSI_mutex_key key_LOCK_connection_count;
|
|
|
|
static PSI_mutex_info all_conn_manager_mutexes[]=
|
|
{
|
|
{ &key_LOCK_connection_count, "LOCK_connection_count", PSI_FLAG_GLOBAL}
|
|
};
|
|
|
|
static PSI_cond_key key_COND_connection_count;
|
|
|
|
static PSI_cond_info all_conn_manager_conds[]=
|
|
{
|
|
{ &key_COND_connection_count, "COND_connection_count", PSI_FLAG_GLOBAL}
|
|
};
|
|
#endif
|
|
|
|
bool Connection_handler_manager::init()
|
|
{
|
|
/*
|
|
This is a static member function.
|
|
Per_thread_connection_handler's static members need to be initialized
|
|
even if One_thread_connection_handler is used instead.
|
|
*/
|
|
Per_thread_connection_handler::init();
|
|
|
|
Connection_handler *connection_handler= NULL;
|
|
switch (Connection_handler_manager::thread_handling)
|
|
{
|
|
case SCHEDULER_ONE_THREAD_PER_CONNECTION:
|
|
connection_handler= new (std::nothrow) Per_thread_connection_handler();
|
|
break;
|
|
case SCHEDULER_NO_THREADS:
|
|
connection_handler= new (std::nothrow) One_thread_connection_handler();
|
|
break;
|
|
default:
|
|
DBUG_ASSERT(false);
|
|
}
|
|
|
|
if (connection_handler == NULL)
|
|
{
|
|
// This is a static member function.
|
|
Per_thread_connection_handler::destroy();
|
|
return true;
|
|
}
|
|
|
|
m_instance= new (std::nothrow) Connection_handler_manager(connection_handler);
|
|
|
|
if (m_instance == NULL)
|
|
{
|
|
delete connection_handler;
|
|
// This is a static member function.
|
|
Per_thread_connection_handler::destroy();
|
|
return true;
|
|
}
|
|
|
|
#ifdef HAVE_PSI_INTERFACE
|
|
int count= array_elements(all_conn_manager_mutexes);
|
|
mysql_mutex_register("sql", all_conn_manager_mutexes, count);
|
|
|
|
count= array_elements(all_conn_manager_conds);
|
|
mysql_cond_register("sql", all_conn_manager_conds, count);
|
|
#endif
|
|
|
|
mysql_mutex_init(key_LOCK_connection_count,
|
|
&LOCK_connection_count, MY_MUTEX_INIT_FAST);
|
|
|
|
mysql_cond_init(key_COND_connection_count, &COND_connection_count);
|
|
max_threads= connection_handler->get_max_threads();
|
|
|
|
// Init common callback functions.
|
|
thr_set_lock_wait_callback(scheduler_wait_lock_begin,
|
|
scheduler_wait_lock_end);
|
|
thr_set_sync_wait_callback(scheduler_wait_sync_begin,
|
|
scheduler_wait_sync_end);
|
|
return false;
|
|
}
|
|
|
|
void Connection_handler_manager::wait_till_no_connection()
|
|
{
|
|
mysql_mutex_lock(&LOCK_connection_count);
|
|
while (connection_count > 0)
|
|
{
|
|
mysql_cond_wait(&COND_connection_count, &LOCK_connection_count);
|
|
}
|
|
mysql_mutex_unlock(&LOCK_connection_count);
|
|
}
|
|
|
|
void Connection_handler_manager::destroy_instance()
|
|
{
|
|
Per_thread_connection_handler::destroy();
|
|
|
|
if (m_instance != NULL)
|
|
{
|
|
delete m_instance;
|
|
m_instance= NULL;
|
|
mysql_mutex_destroy(&LOCK_connection_count);
|
|
mysql_cond_destroy(&COND_connection_count);
|
|
}
|
|
}
|
|
|
|
void Connection_handler_manager::reset_max_used_connections()
|
|
{
|
|
mysql_mutex_lock(&LOCK_connection_count);
|
|
max_used_connections= connection_count;
|
|
max_used_connections_time= (ulong)my_time(0);
|
|
mysql_mutex_unlock(&LOCK_connection_count);
|
|
}
|
|
|
|
void Connection_handler_manager::load_connection_handler(
|
|
Connection_handler* conn_handler)
|
|
{
|
|
// We don't support loading more than one dynamic connection handler
|
|
DBUG_ASSERT(Connection_handler_manager::thread_handling !=
|
|
SCHEDULER_TYPES_COUNT);
|
|
m_saved_connection_handler= m_connection_handler;
|
|
m_saved_thread_handling= Connection_handler_manager::thread_handling;
|
|
m_connection_handler= conn_handler;
|
|
Connection_handler_manager::thread_handling= SCHEDULER_TYPES_COUNT;
|
|
max_threads= m_connection_handler->get_max_threads();
|
|
}
|
|
|
|
|
|
bool Connection_handler_manager::unload_connection_handler()
|
|
{
|
|
DBUG_ASSERT(m_saved_connection_handler != NULL);
|
|
if (m_saved_connection_handler == NULL)
|
|
return true;
|
|
delete m_connection_handler;
|
|
m_connection_handler= m_saved_connection_handler;
|
|
Connection_handler_manager::thread_handling= m_saved_thread_handling;
|
|
m_saved_connection_handler= NULL;
|
|
m_saved_thread_handling= 0;
|
|
max_threads= m_connection_handler->get_max_threads();
|
|
return false;
|
|
}
|
|
|
|
|
|
void
|
|
Connection_handler_manager::process_new_connection(Channel_info* channel_info)
|
|
{
|
|
if (abort_loop || !check_and_incr_conn_count())
|
|
{
|
|
channel_info->send_error_and_close_channel(ER_CON_COUNT_ERROR, 0, true);
|
|
delete channel_info;
|
|
return;
|
|
}
|
|
|
|
if (m_connection_handler->add_connection(channel_info))
|
|
{
|
|
inc_aborted_connects();
|
|
delete channel_info;
|
|
}
|
|
}
|
|
|
|
|
|
THD* create_thd(Channel_info* channel_info)
|
|
{
|
|
THD* thd= channel_info->create_thd();
|
|
if (thd == NULL)
|
|
channel_info->send_error_and_close_channel(ER_OUT_OF_RESOURCES, 0, false);
|
|
|
|
return thd;
|
|
}
|
|
|
|
|
|
void destroy_channel_info(Channel_info* channel_info)
|
|
{
|
|
delete channel_info;
|
|
}
|
|
|
|
|
|
void dec_connection_count()
|
|
{
|
|
Connection_handler_manager::dec_connection_count();
|
|
}
|
|
|
|
void increment_aborted_connects()
|
|
{
|
|
Connection_handler_manager::get_instance()->inc_aborted_connects();
|
|
}
|
|
#endif // !EMBEDDED_LIBRARY
|
|
|
|
|
|
extern "C"
|
|
{
|
|
int my_connection_handler_set(Connection_handler_functions *chf,
|
|
THD_event_functions *tef)
|
|
{
|
|
DBUG_ASSERT(chf != NULL && tef != NULL);
|
|
if (chf == NULL || tef == NULL)
|
|
return 1;
|
|
|
|
Plugin_connection_handler *conn_handler=
|
|
new (std::nothrow) Plugin_connection_handler(chf);
|
|
if (conn_handler == NULL)
|
|
return 1;
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
|
Connection_handler_manager::get_instance()->
|
|
load_connection_handler(conn_handler);
|
|
#endif
|
|
Connection_handler_manager::saved_event_functions=
|
|
Connection_handler_manager::event_functions;
|
|
Connection_handler_manager::event_functions= tef;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int my_connection_handler_reset()
|
|
{
|
|
Connection_handler_manager::event_functions=
|
|
Connection_handler_manager::saved_event_functions;
|
|
#ifndef EMBEDDED_LIBRARY
|
|
return Connection_handler_manager::get_instance()->
|
|
unload_connection_handler();
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
};
|