mysql5/mysql-5.7.27/sql/conn_handler/connection_handler_manager.cc

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
}
};