ClickHouse/base/mysqlxx/PoolWithFailover.cpp

135 lines
5.0 KiB
C++
Raw Normal View History

#include <mysqlxx/PoolWithFailover.h>
2017-07-27 23:28:45 +00:00
/// Duplicate of code from StringUtils.h. Copied here for less dependencies.
static bool startsWith(const std::string & s, const char * prefix)
{
return s.size() >= strlen(prefix) && 0 == memcmp(s.data(), prefix, strlen(prefix));
}
using namespace mysqlxx;
2020-03-08 21:04:10 +00:00
PoolWithFailover::PoolWithFailover(const Poco::Util::AbstractConfiguration & config,
const std::string & config_name, const unsigned default_connections,
const unsigned max_connections, const size_t max_tries)
: max_tries(max_tries)
{
2020-03-08 21:04:10 +00:00
shareable = config.getBool(config_name + ".share_connection", false);
if (config.has(config_name + ".replica"))
{
Poco::Util::AbstractConfiguration::Keys replica_keys;
2020-03-08 21:04:10 +00:00
config.keys(config_name, replica_keys);
2017-07-27 20:22:53 +00:00
for (const auto & replica_config_key : replica_keys)
{
/// There could be another elements in the same level in configuration file, like "password", "port"...
if (startsWith(replica_config_key, "replica"))
{
2017-07-27 20:22:53 +00:00
std::string replica_name = config_name + "." + replica_config_key;
2017-07-27 20:40:32 +00:00
2020-03-08 21:04:10 +00:00
int priority = config.getInt(replica_name + ".priority", 0);
2017-07-27 20:40:32 +00:00
replicas_by_priority[priority].emplace_back(
2020-03-08 21:04:10 +00:00
std::make_shared<Pool>(config, replica_name, default_connections, max_connections, config_name.c_str()));
}
}
}
else
{
2017-07-27 20:40:32 +00:00
replicas_by_priority[0].emplace_back(
2020-03-08 21:04:10 +00:00
std::make_shared<Pool>(config, config_name, default_connections, max_connections));
}
}
PoolWithFailover::PoolWithFailover(const std::string & config_name, const unsigned default_connections,
const unsigned max_connections, const size_t max_tries)
: PoolWithFailover{
Poco::Util::Application::instance().config(), config_name,
2017-07-27 20:22:53 +00:00
default_connections, max_connections, max_tries}
{}
PoolWithFailover::PoolWithFailover(const PoolWithFailover & other)
: max_tries{other.max_tries}, config_name{other.config_name}, shareable{other.shareable}
{
if (shareable)
{
replicas_by_priority = other.replicas_by_priority;
}
else
{
for (const auto & priority_replicas : other.replicas_by_priority)
{
Replicas replicas;
replicas.reserve(priority_replicas.second.size());
for (const auto & pool : priority_replicas.second)
replicas.emplace_back(std::make_shared<Pool>(*pool));
replicas_by_priority.emplace(priority_replicas.first, std::move(replicas));
}
}
}
PoolWithFailover::Entry PoolWithFailover::get()
{
Poco::Util::Application & app = Poco::Util::Application::instance();
std::lock_guard<std::mutex> locker(mutex);
2017-07-27 20:22:53 +00:00
/// If we cannot connect to some replica due to pool overflow, than we will wait and connect.
2017-07-27 20:52:14 +00:00
PoolPtr * full_pool = nullptr;
for (size_t try_no = 0; try_no < max_tries; ++try_no)
{
full_pool = nullptr;
2017-07-27 20:40:32 +00:00
for (auto & priority_replicas : replicas_by_priority)
{
2017-07-27 20:40:32 +00:00
Replicas & replicas = priority_replicas.second;
for (size_t i = 0, size = replicas.size(); i < size; ++i)
{
2017-07-27 20:52:14 +00:00
PoolPtr & pool = replicas[i];
try
{
Entry entry = shareable ? pool->get() : pool->tryGet();
if (!entry.isNull())
{
2017-07-27 20:22:53 +00:00
/// Move all traversed replicas to the end of queue.
/// (No need to move replicas with another priority)
std::rotate(replicas.begin(), replicas.begin() + i + 1, replicas.end());
return entry;
}
}
catch (const Poco::Exception & e)
{
if (e.displayText().find("mysqlxx::Pool is full") != std::string::npos) /// NOTE: String comparison is trashy code.
{
2017-07-27 20:52:14 +00:00
full_pool = &pool;
}
2017-07-27 20:52:14 +00:00
app.logger().warning("Connection to " + pool->getDescription() + " failed: " + e.displayText());
continue;
}
2017-07-27 20:52:14 +00:00
app.logger().warning("Connection to " + pool->getDescription() + " failed.");
}
}
app.logger().error("Connection to all replicas failed " + std::to_string(try_no + 1) + " times");
}
if (full_pool)
{
2017-07-27 20:52:14 +00:00
app.logger().error("All connections failed, trying to wait on a full pool " + (*full_pool)->getDescription());
return (*full_pool)->get();
}
std::stringstream message;
message << "Connections to all replicas failed: ";
2017-07-27 20:52:14 +00:00
for (auto it = replicas_by_priority.begin(); it != replicas_by_priority.end(); ++it)
for (auto jt = it->second.begin(); jt != it->second.end(); ++jt)
message << (it == replicas_by_priority.begin() && jt == it->second.begin() ? "" : ", ") << (*jt)->getDescription();
throw Poco::Exception(message.str());
}