added connection pool and tests

This commit is contained in:
Sascha Kühl
2025-02-05 15:47:51 +01:00
parent 45a7199ccf
commit bc3ffbda10
20 changed files with 495 additions and 53 deletions
+188
View File
@@ -0,0 +1,188 @@
#ifndef QUERY_CONNECTION_POOL_HPP
#define QUERY_CONNECTION_POOL_HPP
#include "matador/sql/connection_info.hpp"
#include <chrono>
#include <mutex>
#include <string>
#include <optional>
#include <condition_variable>
#include <thread>
#include <unordered_map>
namespace matador::sql {
template < class Connection >
class connection_pool;
template < class Connection >
using IdConnection = std::pair<size_t, Connection>;
template < class Connection >
class connection_ptr
{
public:
connection_ptr(IdConnection<Connection> *c, connection_pool<Connection> *pool)
: connection_(c), pool_(pool) {}
~connection_ptr();
connection_ptr(const connection_ptr &) = delete;
connection_ptr& operator=(const connection_ptr &) = delete;
connection_ptr(connection_ptr &&x) noexcept
: connection_(x.connection_)
, pool_(x.pool_)
{
x.connection_ = nullptr;
x.pool_ = nullptr;
}
connection_ptr& operator=(connection_ptr &&x) noexcept
{
if (this == &x) {
return *this;
}
std::swap(connection_, x.connection_);
std::swap(pool_, x.pool_);
return *this;
}
Connection* operator->() { return &connection_->second; }
Connection& operator*() { return connection_->second; }
[[nodiscard]] std::optional<size_t> id() const
{
if (connection_) {
return connection_->first;
} else {
return std::nullopt;
}
}
[[nodiscard]] bool valid() const { return connection_ != nullptr; }
private:
friend class connection_pool<Connection>;
IdConnection<Connection> *connection_{};
connection_pool<Connection> *pool_{};
};
template < class Connection >
class connection_pool
{
public:
using connection_pointer = connection_ptr<Connection>;
public:
connection_pool(const std::string &dns, size_t count)
: info_(connection_info::parse(dns)) {
connection_repo_.reserve(count);
while (count) {
connection_repo_.emplace_back(count, info_);
auto &conn = connection_repo_.back();
idle_connections_.emplace(conn.first, &conn);
conn.second.open();
--count;
}
}
connection_pointer acquire() {
std::unique_lock<std::mutex> lock(mutex_);
while (idle_connections_.empty()) {
cv.wait(lock);
}
return get_next_connection();
}
connection_pointer try_acquire() {
std::unique_lock<std::mutex> lock(mutex_);
if (idle_connections_.empty()) {
return {nullptr, this};
}
return get_next_connection();
}
connection_pointer acquire(size_t id) {
using namespace std::chrono_literals;
pointer next_connection{nullptr};
auto try_count{0};
std::unique_lock<std::mutex> lock(mutex_);
do {
if (auto it = idle_connections_.find(id); it != idle_connections_.end()) {
next_connection = it->second;
auto node = idle_connections_.extract(it);
inuse_connections_.insert(std::move(node));
} else {
lock.unlock();
std::this_thread::sleep_for(100ms);
lock.lock();
}
} while(try_count++ < 5);
return {next_connection, this};
}
void release(IdConnection<Connection> *c) {
if (c == nullptr) {
return;
}
std::unique_lock<std::mutex> lock(mutex_);
if (auto it = inuse_connections_.find(c->first); it != inuse_connections_.end()) {
auto node = inuse_connections_.extract(it);
idle_connections_.insert(std::move(node));
}
}
void release(connection_ptr<Connection> &c) {
release(c.connection_);
c.connection_ = nullptr;
}
std::size_t size() const { return connection_repo_.size(); }
std::size_t idle() const {
std::lock_guard<std::mutex> guard(mutex_);
return idle_connections_.size();
}
std::size_t inuse() const {
std::lock_guard<std::mutex> guard(mutex_);
return inuse_connections_.size();
}
const connection_info &info() const {
return info_;
}
private:
connection_pointer get_next_connection() {
pointer next_connection{nullptr};
for (auto &item : idle_connections_) {
next_connection = item.second;
auto node = idle_connections_.extract(item.first);
inuse_connections_.insert(std::move(node));
break;
}
return {next_connection, this};
}
private:
mutable std::mutex mutex_;
std::condition_variable cv;
std::vector<IdConnection<Connection>> connection_repo_;
using pointer = IdConnection<Connection>*;
using connection_map = std::unordered_map<size_t, pointer>;
connection_map inuse_connections_;
connection_map idle_connections_;
const connection_info info_;
};
template<class Connection>
connection_ptr<Connection>::~connection_ptr() {
pool_->release(connection_);
}
}
#endif //QUERY_CONNECTION_POOL_HPP
+1 -1
View File
@@ -125,7 +125,7 @@ private:
friend class dialect_builder;
next_placeholder_func placeholder_func_ = [](size_t) { return "?"; };
// to_escaped_string_func to_escaped_string_func_ = [](const utils::blob &val) { return utils::to_string(val); };
to_escaped_string_func to_escaped_string_func_ = [](const utils::blob &val) { return utils::to_string(val); };
escape_identifier_t identifier_escape_type_ = escape_identifier_t::ESCAPE_BOTH_SAME;
+1
View File
@@ -17,6 +17,7 @@ public:
dialect_builder& with_placeholder_func(const dialect::next_placeholder_func &func);
dialect_builder& with_default_schema_name(const std::string &schema_name);
dialect_builder& with_bool_strings(const std::string &true_string, const std::string &false_string);
dialect_builder& with_escape_string_func(const dialect::to_escaped_string_func &func);
dialect build();
+1 -1
View File
@@ -5,7 +5,7 @@ namespace matador::utils {
struct placeholder {};
inline constexpr bool operator==(const placeholder&, const placeholder&) { return true; }
constexpr bool operator==(const placeholder&, const placeholder&) { return true; }
static constexpr placeholder _;
+1
View File
@@ -18,6 +18,7 @@ using database_type = std::variant<
int8_t, int16_t, int32_t, int64_t,
float, double,
bool,
const char*,
std::string,
blob,
nullptr_t>;