2019-09-24 22:28:59 +00:00
|
|
|
#if __has_include(<mysql.h>)
|
|
|
|
#include <mysql.h>
|
|
|
|
#include <mysqld_error.h>
|
2018-07-25 11:29:26 +00:00
|
|
|
#else
|
2017-01-24 21:07:08 +00:00
|
|
|
#include <mysql/mysql.h>
|
|
|
|
#include <mysql/mysqld_error.h>
|
2018-07-25 11:29:26 +00:00
|
|
|
#endif
|
2017-04-19 00:25:57 +00:00
|
|
|
|
2017-01-13 20:37:37 +00:00
|
|
|
#include <mysqlxx/Pool.h>
|
|
|
|
|
2019-07-10 20:47:39 +00:00
|
|
|
#include <common/sleep.h>
|
2019-05-19 09:14:23 +00:00
|
|
|
|
2017-01-13 21:24:59 +00:00
|
|
|
#include <Poco/Util/Application.h>
|
|
|
|
#include <Poco/Util/LayeredConfiguration.h>
|
|
|
|
|
2017-01-13 20:37:37 +00:00
|
|
|
|
|
|
|
namespace mysqlxx
|
|
|
|
{
|
|
|
|
|
|
|
|
void Pool::Entry::incrementRefCount()
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
if (!data)
|
|
|
|
return;
|
2020-03-05 20:00:49 +00:00
|
|
|
++data->ref_count;
|
|
|
|
if (data->ref_count == 1)
|
2020-02-27 09:34:06 +00:00
|
|
|
mysql_thread_init();
|
2017-01-13 20:37:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Pool::Entry::decrementRefCount()
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
if (!data)
|
|
|
|
return;
|
2020-03-02 17:21:46 +00:00
|
|
|
if (data->ref_count > 0)
|
|
|
|
{
|
2020-03-05 20:00:49 +00:00
|
|
|
--data->ref_count;
|
|
|
|
if (data->ref_count == 0)
|
2020-02-27 09:34:06 +00:00
|
|
|
mysql_thread_end();
|
|
|
|
}
|
2017-01-13 20:37:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-01-13 21:18:07 +00:00
|
|
|
Pool::Pool(const Poco::Util::AbstractConfiguration & cfg, const std::string & config_name,
|
2017-04-01 07:20:54 +00:00
|
|
|
unsigned default_connections_, unsigned max_connections_,
|
|
|
|
const char * parent_config_name_)
|
|
|
|
: default_connections(default_connections_), max_connections(max_connections_)
|
2017-01-13 21:18:07 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
server = cfg.getString(config_name + ".host");
|
|
|
|
|
|
|
|
if (parent_config_name_)
|
|
|
|
{
|
|
|
|
const std::string parent_config_name(parent_config_name_);
|
|
|
|
db = cfg.getString(config_name + ".db", cfg.getString(parent_config_name + ".db", ""));
|
|
|
|
user = cfg.has(config_name + ".user")
|
|
|
|
? cfg.getString(config_name + ".user")
|
|
|
|
: cfg.getString(parent_config_name + ".user");
|
|
|
|
password = cfg.has(config_name + ".password")
|
|
|
|
? cfg.getString(config_name + ".password")
|
|
|
|
: cfg.getString(parent_config_name + ".password");
|
2017-07-14 12:45:32 +00:00
|
|
|
|
|
|
|
if (!cfg.has(config_name + ".port") && !cfg.has(config_name + ".socket")
|
|
|
|
&& !cfg.has(parent_config_name + ".port") && !cfg.has(parent_config_name + ".socket"))
|
|
|
|
throw Poco::Exception("mysqlxx::Pool configuration: expected port or socket");
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
port = cfg.has(config_name + ".port")
|
|
|
|
? cfg.getInt(config_name + ".port")
|
2017-07-14 12:45:32 +00:00
|
|
|
: cfg.getInt(parent_config_name + ".port", 0);
|
|
|
|
socket = cfg.has(config_name + ".socket")
|
|
|
|
? cfg.getString(config_name + ".socket")
|
|
|
|
: cfg.getString(parent_config_name + ".socket", "");
|
2017-09-09 21:26:02 +00:00
|
|
|
ssl_ca = cfg.has(config_name + ".ssl_ca")
|
|
|
|
? cfg.getString(config_name + ".ssl_ca")
|
|
|
|
: cfg.getString(parent_config_name + ".ssl_ca", "");
|
|
|
|
ssl_cert = cfg.has(config_name + ".ssl_cert")
|
|
|
|
? cfg.getString(config_name + ".ssl_cert")
|
|
|
|
: cfg.getString(parent_config_name + ".ssl_cert", "");
|
|
|
|
ssl_key = cfg.has(config_name + ".ssl_key")
|
|
|
|
? cfg.getString(config_name + ".ssl_key")
|
|
|
|
: cfg.getString(parent_config_name + ".ssl_key", "");
|
2018-10-01 15:55:21 +00:00
|
|
|
|
|
|
|
enable_local_infile = cfg.getBool(config_name + ".enable_local_infile",
|
|
|
|
cfg.getBool(parent_config_name + ".enable_local_infile", MYSQLXX_DEFAULT_ENABLE_LOCAL_INFILE));
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
db = cfg.getString(config_name + ".db", "");
|
|
|
|
user = cfg.getString(config_name + ".user");
|
|
|
|
password = cfg.getString(config_name + ".password");
|
2017-07-14 12:45:32 +00:00
|
|
|
|
|
|
|
if (!cfg.has(config_name + ".port") && !cfg.has(config_name + ".socket"))
|
|
|
|
throw Poco::Exception("mysqlxx::Pool configuration: expected port or socket");
|
|
|
|
|
|
|
|
port = cfg.getInt(config_name + ".port", 0);
|
|
|
|
socket = cfg.getString(config_name + ".socket", "");
|
2017-09-09 21:26:02 +00:00
|
|
|
ssl_ca = cfg.getString(config_name + ".ssl_ca", "");
|
|
|
|
ssl_cert = cfg.getString(config_name + ".ssl_cert", "");
|
|
|
|
ssl_key = cfg.getString(config_name + ".ssl_key", "");
|
2018-10-01 15:55:21 +00:00
|
|
|
|
|
|
|
enable_local_infile = cfg.getBool(
|
|
|
|
config_name + ".enable_local_infile", MYSQLXX_DEFAULT_ENABLE_LOCAL_INFILE);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
connect_timeout = cfg.getInt(config_name + ".connect_timeout",
|
|
|
|
cfg.getInt("mysql_connect_timeout",
|
|
|
|
MYSQLXX_DEFAULT_TIMEOUT));
|
|
|
|
|
|
|
|
rw_timeout =
|
|
|
|
cfg.getInt(config_name + ".rw_timeout",
|
|
|
|
cfg.getInt("mysql_rw_timeout",
|
|
|
|
MYSQLXX_DEFAULT_RW_TIMEOUT));
|
2017-01-13 21:18:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-01-13 20:37:37 +00:00
|
|
|
Pool::~Pool()
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
2017-01-13 20:37:37 +00:00
|
|
|
|
2017-09-07 21:04:48 +00:00
|
|
|
for (auto & connection : connections)
|
|
|
|
delete static_cast<Connection *>(connection);
|
2017-01-13 20:37:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-23 02:12:31 +00:00
|
|
|
Pool::Entry Pool::get()
|
2017-01-13 20:37:37 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
std::unique_lock<std::mutex> lock(mutex);
|
|
|
|
|
|
|
|
initialize();
|
|
|
|
for (;;)
|
|
|
|
{
|
2017-09-07 21:04:48 +00:00
|
|
|
for (auto & connection : connections)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-07 21:04:48 +00:00
|
|
|
if (connection->ref_count == 0)
|
|
|
|
return Entry(connection, this);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (connections.size() < static_cast<size_t>(max_connections))
|
|
|
|
{
|
|
|
|
Connection * conn = allocConnection();
|
|
|
|
if (conn)
|
|
|
|
return Entry(conn, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
lock.unlock();
|
2019-07-10 20:47:39 +00:00
|
|
|
sleepForSeconds(MYSQLXX_POOL_SLEEP_ON_CONNECT_FAIL);
|
2017-04-01 07:20:54 +00:00
|
|
|
lock.lock();
|
|
|
|
}
|
2017-01-13 20:37:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Pool::Entry Pool::tryGet()
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
|
|
|
|
initialize();
|
|
|
|
|
2017-07-18 11:52:46 +00:00
|
|
|
/// Searching for connection which was established but wasn't used.
|
2017-09-07 21:04:48 +00:00
|
|
|
for (auto & connection : connections)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-07 21:04:48 +00:00
|
|
|
if (connection->ref_count == 0)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-07 21:04:48 +00:00
|
|
|
Entry res(connection, this);
|
2017-04-01 07:20:54 +00:00
|
|
|
return res.tryForceConnected() ? res : Entry();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-18 11:52:46 +00:00
|
|
|
/// Throws if pool is overflowed.
|
2017-04-01 07:20:54 +00:00
|
|
|
if (connections.size() >= max_connections)
|
|
|
|
throw Poco::Exception("mysqlxx::Pool is full");
|
|
|
|
|
2017-07-18 11:52:46 +00:00
|
|
|
/// Allocates new connection.
|
2017-04-01 07:20:54 +00:00
|
|
|
Connection * conn = allocConnection(true);
|
|
|
|
if (conn)
|
|
|
|
return Entry(conn, this);
|
|
|
|
|
|
|
|
return Entry();
|
2017-01-13 20:37:37 +00:00
|
|
|
}
|
|
|
|
|
2020-02-27 09:34:06 +00:00
|
|
|
void Pool::removeConnection(Connection* connection)
|
2019-05-24 14:44:32 +00:00
|
|
|
{
|
2020-02-27 09:34:06 +00:00
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
if (connection)
|
2019-05-24 14:44:32 +00:00
|
|
|
{
|
2020-02-27 09:34:06 +00:00
|
|
|
if (connection->ref_count > 0)
|
|
|
|
{
|
|
|
|
connection->conn.disconnect();
|
|
|
|
connection->ref_count = 0;
|
|
|
|
}
|
|
|
|
connections.remove(connection);
|
2019-05-24 14:44:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-02-27 09:34:06 +00:00
|
|
|
void Pool::Entry::disconnect()
|
|
|
|
{
|
|
|
|
pool->removeConnection(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-01-13 21:24:59 +00:00
|
|
|
void Pool::Entry::forceConnected() const
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
if (data == nullptr)
|
|
|
|
throw Poco::RuntimeException("Tried to access NULL database connection.");
|
|
|
|
|
|
|
|
Poco::Util::Application & app = Poco::Util::Application::instance();
|
|
|
|
if (data->conn.ping())
|
|
|
|
return;
|
|
|
|
|
|
|
|
bool first = true;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (first)
|
|
|
|
first = false;
|
|
|
|
else
|
2019-07-10 20:47:39 +00:00
|
|
|
sleepForSeconds(MYSQLXX_POOL_SLEEP_ON_CONNECT_FAIL);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
app.logger().information("MYSQL: Reconnecting to " + pool->description);
|
|
|
|
data->conn.connect(
|
|
|
|
pool->db.c_str(),
|
2017-09-09 21:26:02 +00:00
|
|
|
pool->server.c_str(),
|
|
|
|
pool->user.c_str(),
|
|
|
|
pool->password.c_str(),
|
|
|
|
pool->port,
|
|
|
|
pool->socket.c_str(),
|
|
|
|
pool->ssl_ca.c_str(),
|
|
|
|
pool->ssl_cert.c_str(),
|
|
|
|
pool->ssl_key.c_str(),
|
|
|
|
pool->connect_timeout,
|
2018-10-01 15:55:21 +00:00
|
|
|
pool->rw_timeout,
|
|
|
|
pool->enable_local_infile);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
while (!data->conn.ping());
|
2017-01-13 21:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-01-13 20:37:37 +00:00
|
|
|
void Pool::initialize()
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
if (!initialized)
|
|
|
|
{
|
2018-08-20 19:35:04 +00:00
|
|
|
description = db + "@" + server + ":" + std::to_string(port) + " as user " + user;
|
2017-01-13 20:37:37 +00:00
|
|
|
|
2017-12-27 21:45:05 +00:00
|
|
|
for (unsigned i = 0; i < default_connections; ++i)
|
2017-04-01 07:20:54 +00:00
|
|
|
allocConnection();
|
2017-01-13 20:37:37 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
initialized = true;
|
|
|
|
}
|
2017-01-13 20:37:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-01-13 21:18:07 +00:00
|
|
|
Pool::Connection * Pool::allocConnection(bool dont_throw_if_failed_first_time)
|
2017-01-13 20:37:37 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
Poco::Util::Application & app = Poco::Util::Application::instance();
|
|
|
|
|
|
|
|
std::unique_ptr<Connection> conn(new Connection);
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
app.logger().information("MYSQL: Connecting to " + description);
|
|
|
|
|
|
|
|
conn->conn.connect(
|
|
|
|
db.c_str(),
|
2017-09-09 21:26:02 +00:00
|
|
|
server.c_str(),
|
|
|
|
user.c_str(),
|
|
|
|
password.c_str(),
|
|
|
|
port,
|
|
|
|
socket.c_str(),
|
|
|
|
ssl_ca.c_str(),
|
|
|
|
ssl_cert.c_str(),
|
|
|
|
ssl_key.c_str(),
|
|
|
|
connect_timeout,
|
2018-10-01 15:55:21 +00:00
|
|
|
rw_timeout,
|
|
|
|
enable_local_infile);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
catch (mysqlxx::ConnectionFailed & e)
|
|
|
|
{
|
|
|
|
if ((!was_successful && !dont_throw_if_failed_first_time)
|
|
|
|
|| e.errnum() == ER_ACCESS_DENIED_ERROR
|
|
|
|
|| e.errnum() == ER_DBACCESS_DENIED_ERROR
|
|
|
|
|| e.errnum() == ER_BAD_DB_ERROR)
|
|
|
|
{
|
|
|
|
app.logger().error(e.what());
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
app.logger().error(e.what());
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
was_successful = true;
|
|
|
|
auto * connection = conn.release();
|
|
|
|
connections.push_back(connection);
|
|
|
|
return connection;
|
2017-01-13 20:37:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|