2012-11-02 20:13:41 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <Poco/Net/NetException.h>
|
2013-12-10 17:07:09 +00:00
|
|
|
|
#include <Poco/Net/DNS.h>
|
2012-11-02 20:13:41 +00:00
|
|
|
|
|
|
|
|
|
#include <DB/Client/ConnectionPool.h>
|
|
|
|
|
|
2013-12-11 11:51:01 +00:00
|
|
|
|
#include <boost/bind.hpp>
|
|
|
|
|
|
2012-11-02 20:13:41 +00:00
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/** Пул соединений с отказоустойчивостью.
|
|
|
|
|
* Инициализируется несколькими другими IConnectionPool-ами.
|
|
|
|
|
* При получении соединения, пытается создать или выбрать живое соединение из какого-нибудь пула,
|
|
|
|
|
* перебирая их в некотором порядке, используя не более указанного количества попыток.
|
|
|
|
|
* Предпочитаются пулы с меньшим количеством ошибок;
|
|
|
|
|
* пулы с одинаковым количеством ошибок пробуются в случайном порядке.
|
|
|
|
|
*
|
|
|
|
|
* Замечание: если один из вложенных пулов заблокируется из-за переполнения, то этот пул тоже заблокируется.
|
|
|
|
|
*/
|
|
|
|
|
class ConnectionPoolWithFailover : public IConnectionPool
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
typedef detail::ConnectionPoolEntry Entry;
|
|
|
|
|
|
2013-12-11 11:51:01 +00:00
|
|
|
|
ConnectionPoolWithFailover(ConnectionPools & nested_pools_,
|
2014-02-17 23:56:45 +00:00
|
|
|
|
LoadBalancing load_balancing,
|
|
|
|
|
size_t max_tries_ = DBMS_CONNECTION_POOL_WITH_FAILOVER_DEFAULT_MAX_TRIES,
|
|
|
|
|
size_t decrease_error_period_ = DBMS_CONNECTION_POOL_WITH_FAILOVER_DEFAULT_DECREASE_ERROR_PERIOD)
|
2013-12-11 11:51:01 +00:00
|
|
|
|
: nested_pools(nested_pools_.begin(), nested_pools_.end(), decrease_error_period_), max_tries(max_tries_),
|
|
|
|
|
log(&Logger::get("ConnectionPoolWithFailover")), default_load_balancing(load_balancing)
|
2012-11-02 20:13:41 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Выделяет соединение для работы. */
|
2013-12-11 11:51:01 +00:00
|
|
|
|
Entry get(Settings * settings)
|
2012-11-02 20:13:41 +00:00
|
|
|
|
{
|
2014-02-17 23:56:45 +00:00
|
|
|
|
LoadBalancing load_balancing = default_load_balancing;
|
2013-12-11 11:51:01 +00:00
|
|
|
|
if (settings)
|
|
|
|
|
load_balancing = settings->load_balancing;
|
|
|
|
|
|
2013-09-04 01:11:49 +00:00
|
|
|
|
Poco::ScopedLock<Poco::FastMutex> lock(mutex);
|
|
|
|
|
|
2013-12-11 11:51:01 +00:00
|
|
|
|
nested_pools.update(load_balancing);
|
|
|
|
|
std::sort(nested_pools.begin(), nested_pools.end(), boost::bind(&PoolWithErrorCount::compare, _1, _2, load_balancing));
|
2012-11-02 20:13:41 +00:00
|
|
|
|
|
|
|
|
|
std::stringstream fail_messages;
|
|
|
|
|
|
|
|
|
|
for (size_t try_no = 0; try_no < max_tries; ++try_no)
|
|
|
|
|
{
|
|
|
|
|
for (size_t i = 0, size = nested_pools.size(); i < size; ++i)
|
|
|
|
|
{
|
2012-11-06 18:20:00 +00:00
|
|
|
|
std::stringstream fail_message;
|
2013-10-23 19:35:43 +00:00
|
|
|
|
|
2012-11-02 20:13:41 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Entry res = nested_pools[i].pool->get();
|
|
|
|
|
res->forceConnected();
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2013-10-26 03:20:51 +00:00
|
|
|
|
catch (const Exception & e)
|
2012-11-02 20:13:41 +00:00
|
|
|
|
{
|
2013-10-23 19:35:43 +00:00
|
|
|
|
if (e.code() != ErrorCodes::NETWORK_ERROR && e.code() != ErrorCodes::SOCKET_TIMEOUT)
|
|
|
|
|
throw;
|
|
|
|
|
|
2013-10-26 03:20:51 +00:00
|
|
|
|
fail_message << "Code: " << e.code() << ", e.displayText() = " << e.displayText() << ", e.what() = " << e.what();
|
2012-11-06 18:20:00 +00:00
|
|
|
|
}
|
2012-11-02 20:13:41 +00:00
|
|
|
|
|
2012-11-06 18:20:00 +00:00
|
|
|
|
LOG_WARNING(log, "Connection failed at try №"
|
|
|
|
|
<< (try_no + 1) << ", reason: " << fail_message.str());
|
2012-11-02 20:13:41 +00:00
|
|
|
|
|
2012-11-06 18:20:00 +00:00
|
|
|
|
fail_messages << fail_message.str() << std::endl;
|
|
|
|
|
|
2013-12-11 11:51:01 +00:00
|
|
|
|
++nested_pools[i].random_error_count;
|
|
|
|
|
++nested_pools[i].nearest_hostname_error_count;
|
2012-11-02 20:13:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-26 03:20:51 +00:00
|
|
|
|
throw Exception("All connection tries failed. Log: \n\n" + fail_messages.str() + "\n",
|
2012-11-02 20:13:41 +00:00
|
|
|
|
ErrorCodes::ALL_CONNECTION_TRIES_FAILED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
struct PoolWithErrorCount
|
|
|
|
|
{
|
|
|
|
|
ConnectionPoolPtr pool;
|
2013-12-11 11:51:01 +00:00
|
|
|
|
|
|
|
|
|
UInt64 random_error_count;
|
2012-11-02 20:13:41 +00:00
|
|
|
|
UInt32 random;
|
2012-11-06 18:20:00 +00:00
|
|
|
|
drand48_data rand_state;
|
2013-12-11 11:51:01 +00:00
|
|
|
|
|
|
|
|
|
/// берётся имя локального сервера (Poco::Net::DNS::hostName) и имя хоста из конфига; строки обрезаются до минимальной длины;
|
|
|
|
|
/// затем считается количество отличающихся позиций
|
|
|
|
|
/// Пример example01-01-1 и example01-02-2 отличаются в двух позициях.
|
|
|
|
|
size_t hostname_difference;
|
|
|
|
|
UInt64 nearest_hostname_error_count;
|
2012-11-02 20:13:41 +00:00
|
|
|
|
|
2012-11-07 10:32:48 +00:00
|
|
|
|
PoolWithErrorCount(const ConnectionPoolPtr & pool_)
|
2013-12-11 11:51:01 +00:00
|
|
|
|
: pool(pool_), random_error_count(0), random(0), nearest_hostname_error_count(0)
|
2012-11-06 18:20:00 +00:00
|
|
|
|
{
|
|
|
|
|
/// Инициализация плохая, но это не важно.
|
|
|
|
|
srand48_r(reinterpret_cast<ptrdiff_t>(this), &rand_state);
|
2013-12-10 17:07:09 +00:00
|
|
|
|
|
|
|
|
|
std::string local_hostname = Poco::Net::DNS::hostName();
|
|
|
|
|
|
|
|
|
|
ConnectionPool & connection_pool = dynamic_cast<ConnectionPool &>(*pool);
|
|
|
|
|
const std::string & host = connection_pool.getHost();
|
|
|
|
|
hostname_difference = 0;
|
|
|
|
|
for (size_t i = 0; i < std::min(local_hostname.length(), host.length()); ++i)
|
|
|
|
|
{
|
|
|
|
|
if (local_hostname[i] != host[i])
|
|
|
|
|
++hostname_difference;
|
|
|
|
|
}
|
2012-11-06 18:20:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void randomize()
|
|
|
|
|
{
|
|
|
|
|
long int rand_res;
|
|
|
|
|
lrand48_r(&rand_state, &rand_res);
|
|
|
|
|
random = rand_res;
|
|
|
|
|
}
|
2012-11-02 20:13:41 +00:00
|
|
|
|
|
2014-02-17 23:56:45 +00:00
|
|
|
|
static bool compare(const PoolWithErrorCount & lhs, const PoolWithErrorCount & rhs, LoadBalancing load_balancing_mode)
|
2012-11-02 20:13:41 +00:00
|
|
|
|
{
|
2013-12-10 17:07:09 +00:00
|
|
|
|
if (load_balancing_mode == LoadBalancing::RANDOM)
|
|
|
|
|
{
|
2013-12-11 11:51:01 +00:00
|
|
|
|
return lhs.random_error_count < rhs.random_error_count
|
|
|
|
|
|| (lhs.random_error_count == rhs.random_error_count && lhs.random < rhs.random);
|
2013-12-10 17:07:09 +00:00
|
|
|
|
}
|
|
|
|
|
else if (load_balancing_mode == LoadBalancing::NEAREST_HOSTNAME)
|
|
|
|
|
{
|
2013-12-11 11:51:01 +00:00
|
|
|
|
return lhs.nearest_hostname_error_count < rhs.nearest_hostname_error_count
|
|
|
|
|
|| (lhs.nearest_hostname_error_count == rhs.nearest_hostname_error_count
|
|
|
|
|
&& lhs.hostname_difference < rhs.hostname_difference);
|
2013-12-10 17:07:09 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2014-02-28 01:06:44 +00:00
|
|
|
|
throw Poco::Exception("Unsupported load_balancing_mode: " + toString(static_cast<int>(load_balancing_mode)));
|
2013-12-10 17:07:09 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class PoolsWithErrorCount : public std::vector<PoolWithErrorCount>
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
PoolsWithErrorCount(DB::ConnectionPools::iterator first, DB::ConnectionPools::iterator last,
|
2013-12-11 11:51:01 +00:00
|
|
|
|
size_t decrease_error_period_) :
|
|
|
|
|
std::vector<PoolWithErrorCount>(first, last), last_get_time(0),
|
2013-12-10 17:07:09 +00:00
|
|
|
|
decrease_error_period(decrease_error_period_)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-17 23:56:45 +00:00
|
|
|
|
void update(LoadBalancing load_balancing_mode)
|
2013-12-10 17:07:09 +00:00
|
|
|
|
{
|
|
|
|
|
if (load_balancing_mode == LoadBalancing::RANDOM)
|
|
|
|
|
{
|
|
|
|
|
for (PoolsWithErrorCount::iterator it = begin(); it != end(); ++it)
|
|
|
|
|
it->randomize();
|
|
|
|
|
}
|
2013-12-11 11:51:01 +00:00
|
|
|
|
/// Для режима NEAREST_HOSTNAME каждые N секунд уменьшаем количество ошибок в 2 раза
|
|
|
|
|
if (last_get_time)
|
2013-12-10 17:07:09 +00:00
|
|
|
|
{
|
|
|
|
|
time_t delta = time(0) - last_get_time;
|
|
|
|
|
for (PoolsWithErrorCount::iterator it = begin(); it != end(); ++it)
|
|
|
|
|
{
|
2014-02-17 23:56:45 +00:00
|
|
|
|
it->nearest_hostname_error_count = it->nearest_hostname_error_count >> (delta / decrease_error_period);
|
2013-12-10 17:07:09 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
last_get_time = time(0);
|
2012-11-02 20:13:41 +00:00
|
|
|
|
}
|
2013-12-10 17:07:09 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
/// время, когда последний раз вызывался update
|
|
|
|
|
time_t last_get_time;
|
|
|
|
|
time_t decrease_error_period;
|
2012-11-02 20:13:41 +00:00
|
|
|
|
};
|
|
|
|
|
|
2013-09-04 01:11:49 +00:00
|
|
|
|
Poco::FastMutex mutex;
|
|
|
|
|
|
2012-11-02 20:13:41 +00:00
|
|
|
|
PoolsWithErrorCount nested_pools;
|
2013-12-10 17:07:09 +00:00
|
|
|
|
|
2012-11-02 20:13:41 +00:00
|
|
|
|
size_t max_tries;
|
|
|
|
|
|
|
|
|
|
Logger * log;
|
2013-12-11 11:51:01 +00:00
|
|
|
|
|
2014-02-17 23:56:45 +00:00
|
|
|
|
LoadBalancing default_load_balancing;
|
2012-11-02 20:13:41 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|