diff --git a/plugin/thread_pool/CMakeLists.txt b/plugin/thread_pool/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..35cbdff51401d4000f09c5e0ba59261cc4f7c33d --- /dev/null +++ b/plugin/thread_pool/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022 Huawei Technologies Co., Ltd. +# +# 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 + +ADD_COMPILE_DEFINITIONS( + COMPILE_DEFINITIONS MYSQL_DYNAMIC_PLUGIN) + +MYSQL_ADD_PLUGIN(thread_pool + threadpool_common.cc + threadpool_unix.cc + MODULE_ONLY + MODULE_OUTPUT_NAME "thread_pool" + ) + diff --git a/plugin/thread_pool/numa_affinity_manager.h b/plugin/thread_pool/numa_affinity_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..3471d3287369b6f8900544c9a30485570101c915 --- /dev/null +++ b/plugin/thread_pool/numa_affinity_manager.h @@ -0,0 +1,117 @@ +/* Copyright (C) 2012 Monty Program Ab + Copyright (C) 2022 Huawei Technologies Co., Ltd + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef NUMA_AFFINITY_MANAGER_H_ +#define NUMA_AFFINITY_MANAGER_H_ + +#include +#include +#include +#include +#include + +using namespace std; + +class numa_affinity_manager +{ +public: + numa_affinity_manager(){}; + virtual ~numa_affinity_manager(){}; + + bool init() { + initok = false; + cpu_count = get_sys_cpu(); + numa_count = get_sys_numa(); + if (cpu_count <= 0 || numa_count <= 0 || + cpu_count % numa_count != 0) { + return false; + } + + int cpu_per_numa = cpu_count / numa_count; + int start = 0; + numa_cpu_map.clear(); + auto delete_cpumask = [](bitmask *ptr) { + if (ptr != nullptr) { + numa_free_cpumask(ptr); + } + }; + for (int i = 0; i < numa_count; i++) { + auto msk = numa_allocate_cpumask(); + if (msk == nullptr) { + return false; + } + + for (int j = 0; j < cpu_per_numa; j++) { + numa_bitmask_setbit(msk, start + j); + } + numa_cpu_map.emplace_back(msk, delete_cpumask); + start += cpu_per_numa; + } + initok = true; + return true; + } + + bool bind_numa(int group_id) { + if (initok) { + pid_t pid = gettid(); + return (numa_sched_setaffinity( + pid, numa_cpu_map[group_id%numa_cpu_map.size()].get()) == 0); + } + + return false; + } + +protected: + int get_sys_cpu() { + return numa_num_configured_cpus(); + } + + int get_sys_numa() { + return numa_num_configured_nodes(); + } + + pid_t gettid() { + return static_cast(syscall(SYS_gettid)); + } + +public: + void print_cpumask(const string &name, bitmask *msk) { + cout << name << ": "; + for (unsigned int i = 0; i < msk->size; i++) { + if (numa_bitmask_isbitset(msk, i)) { + cout << i << " "; + } + } + cout << endl; + } + void dump() { + cout << "initok: " << initok << endl; + cout << "cpu_count: " << cpu_count << endl; + cout << "numa_count: " << numa_count << endl; + + for (unsigned int i = 0; i < numa_cpu_map.size(); i++) { + string name = "numa_cpu_map[" + to_string(i) + "]"; + print_cpumask(name, numa_cpu_map[i].get()); + } + } + +private: + bool initok{false}; + int cpu_count{0}; + int numa_count{0}; + vector> numa_cpu_map; +}; + +#endif // NUMA_AFFINITY_MANAGER_H_ diff --git a/plugin/thread_pool/threadpool_unix.cc b/plugin/thread_pool/threadpool_unix.cc index cbe30d1d818942d7ddd622771ba4f170edc96674..a9fdf3dbfcd8978abf65402d72b585922d602ded 100644 --- a/plugin/thread_pool/threadpool_unix.cc +++ b/plugin/thread_pool/threadpool_unix.cc @@ -1741,3 +1741,54 @@ int tp_get_idle_thread_count() noexcept { } return sum; } + +/* Report threadpool problems */ + +/** + Delay in microseconds, after which "pool blocked" message is printed. + (30 sec == 30 Mio usec) +*/ +#define BLOCK_MSG_DELAY 30 * 1000000 + +#define MAX_THREADS_REACHED_MSG \ + "Threadpool could not create additional thread to handle queries, because the \ +number of allowed threads was reached. Increasing 'thread_pool_max_threads' \ +parameter can help in this situation.\n \ +If 'admin_port' parameter is set, you can still connect to the database with \ +superuser account (it must be TCP connection using admin_port as TCP port) \ +and troubleshoot the situation. \ +A likely cause of pool blocks are clients that lock resources for long time. \ +'show processlist' or 'show engine innodb status' can give additional hints." + +#define CREATE_THREAD_ERROR_MSG "Can't create threads in threadpool (errno=%d)." + +/** + Write a message when blocking situation in threadpool occurs. + The message is written only when pool blocks for BLOCK_MSG_DELAY (30) seconds. + It will be just a single message for each blocking situation (to prevent + log flood). +*/ +static void print_pool_blocked_message(bool max_threads_reached) noexcept { + ulonglong now = my_microsecond_getsystime(); + static bool msg_written = false; + + if (pool_block_start == 0) { + pool_block_start = now; + msg_written = false; + } + + if (!msg_written && ((now > pool_block_start + BLOCK_MSG_DELAY) || + (now == pool_block_start))) { + if (max_threads_reached) + sql_print_error(MAX_THREADS_REACHED_MSG); + else + sql_print_error(CREATE_THREAD_ERROR_MSG, my_errno); + + if (now > pool_block_start) { + sql_print_information("Threadpool has been blocked for %u seconds\n", + (uint)((now - pool_block_start) / 1000000)); + } + /* avoid reperated messages for the same blocking situation */ + msg_written = true; + } +}