From b4765cac8890aa6ab2d49a2d7bbb9c051b0506e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Fri, 12 Apr 2024 16:34:30 +0200 Subject: [PATCH] added multi threaded pool test --- backends/postgres/test/CMakeLists.txt | 2 +- include/matador/sql/connection_pool.hpp | 31 +++++-- test/CMakeLists.txt | 2 - test/ConnectionPoolTest.cpp | 108 ++++++++++++++++++++++-- test/DummyConnection.cpp | 28 ------ test/DummyConnection.hpp | 30 ------- 6 files changed, 127 insertions(+), 74 deletions(-) delete mode 100644 test/DummyConnection.cpp delete mode 100644 test/DummyConnection.hpp diff --git a/backends/postgres/test/CMakeLists.txt b/backends/postgres/test/CMakeLists.txt index 01d3804..ed1497f 100644 --- a/backends/postgres/test/CMakeLists.txt +++ b/backends/postgres/test/CMakeLists.txt @@ -12,7 +12,7 @@ list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) include(CTest) include(Catch) -set(POSTGRES_CONNECTION_STRING "postgres://test:test123@127.0.0.1:5432/matador_test") +set(POSTGRES_CONNECTION_STRING "postgres://test:test123@127.0.0.1:15432/test") configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE) diff --git a/include/matador/sql/connection_pool.hpp b/include/matador/sql/connection_pool.hpp index a02ae64..9c977a6 100644 --- a/include/matador/sql/connection_pool.hpp +++ b/include/matador/sql/connection_pool.hpp @@ -86,19 +86,21 @@ public: } connection_pointer acquire() { + std::unique_lock lock(mutex_); + while (idle_connections_.empty()) { + cv.wait(lock); + } + + return get_next_connection(); + } + + connection_pointer try_acquire() { std::unique_lock lock(mutex_); if (idle_connections_.empty()) { return {nullptr, this}; } - 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}; + return get_next_connection(); } connection_pointer acquire(size_t id) { @@ -151,8 +153,21 @@ public: 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> connection_repo_; using pointer = IdConnection*; using connection_map = std::unordered_map; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a1ee163..843f492 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -34,8 +34,6 @@ add_executable(tests models/location.hpp models/optional.hpp ConvertTest.cpp - DummyConnection.hpp - DummyConnection.cpp EntityQueryBuilderTest.cpp models/author.hpp models/book.hpp diff --git a/test/ConnectionPoolTest.cpp b/test/ConnectionPoolTest.cpp index b3b16e6..1f231f1 100644 --- a/test/ConnectionPoolTest.cpp +++ b/test/ConnectionPoolTest.cpp @@ -1,15 +1,42 @@ #include #include +#include -#include "DummyConnection.hpp" +class AutoResetEvent +{ +public: + AutoResetEvent() : state(false) {} + AutoResetEvent(const AutoResetEvent& other) = delete; + + void WaitOne() { + std::unique_lock lock(sync); + underlying.wait(lock, [this](){return state.load();}); + } + + void Set() { + std::unique_lock lock(sync); + state = true; + underlying.notify_all(); + } + + void Reset() { + std::unique_lock lock(sync); + state = false; + } + +private: + std::condition_variable underlying; + std::mutex sync; + std::atomic state; +}; using namespace matador::sql; TEST_CASE("Create connection pool", "[connection pool]") { - using pool_t = connection_pool; + using pool_t = connection_pool; - pool_t pool("sqlite://sqlite.db", 4); + pool_t pool("noop://noop.db", 4); REQUIRE(pool.size() == 4); REQUIRE(pool.idle() == 4); @@ -51,9 +78,9 @@ TEST_CASE("Create connection pool", "[connection pool]") { } TEST_CASE("Acquire connection by id", "[connection pool]") { - using pool_t = connection_pool; + using pool_t = connection_pool; - pool_t pool("sqlite://sqlite.db", 4); + pool_t pool("noop://noop.db", 4); REQUIRE(pool.size() == 4); REQUIRE(pool.idle() == 4); @@ -78,4 +105,75 @@ TEST_CASE("Acquire connection by id", "[connection pool]") { REQUIRE(same_ptr.valid()); REQUIRE(same_ptr.id() == connection_id); +} + +TEST_CASE("Try acquire connection", "[connection pool][try acquire]") { + using pool_t = connection_pool; + + pool_t pool("noop://noop.db", 1); + + REQUIRE(pool.size() == 1); + REQUIRE(pool.idle() == 1); + REQUIRE(pool.inuse() == 0); + + auto ptr = pool.try_acquire(); + REQUIRE(ptr.valid()); + REQUIRE(ptr.id()); + REQUIRE(ptr.id().value() > 0); + REQUIRE(ptr->is_open()); + REQUIRE(pool.size() == 1); + REQUIRE(pool.idle() == 0); + REQUIRE(pool.inuse() == 1); + + auto ptr2 = pool.try_acquire(); + REQUIRE(!ptr2.valid()); + + pool.release(ptr); + REQUIRE(!ptr.valid()); + REQUIRE(pool.size() == 1); + REQUIRE(pool.idle() == 1); + REQUIRE(pool.inuse() == 0); + + ptr2 = pool.try_acquire(); + REQUIRE(ptr2.valid()); + REQUIRE(ptr2.id()); + REQUIRE(ptr2.id().value() > 0); + REQUIRE(ptr2->is_open()); + REQUIRE(pool.size() == 1); + REQUIRE(pool.idle() == 0); + REQUIRE(pool.inuse() == 1); + + pool.release(ptr2); + + AutoResetEvent reset_event1; + AutoResetEvent reset_event2; + AutoResetEvent reset_event3; + + std::thread t([&reset_event1, &reset_event2, &reset_event3, &pool]() { + auto c1 = pool.acquire(); + REQUIRE(c1.valid()); + REQUIRE(c1.id()); + REQUIRE(c1.id().value() > 0); + + reset_event1.Set(); + + reset_event2.WaitOne(); + pool.release(c1); + REQUIRE(!c1.valid()); + reset_event3.Set(); + }); + reset_event1.WaitOne(); + + ptr2 = pool.try_acquire(); + REQUIRE(!ptr2.valid()); + + reset_event2.Set(); + + reset_event3.WaitOne(); + ptr2 = pool.try_acquire(); + REQUIRE(ptr2.valid()); + REQUIRE(ptr2.id()); + REQUIRE(ptr2.id().value() > 0); + + t.join(); } \ No newline at end of file diff --git a/test/DummyConnection.cpp b/test/DummyConnection.cpp deleted file mode 100644 index b4157e7..0000000 --- a/test/DummyConnection.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "DummyConnection.hpp" - -namespace matador::test { - -DummyConnection::DummyConnection(sql::connection_info info) -: connection_info_(std::move(info)) -{} - -DummyConnection::DummyConnection(const std::string &dns) -: DummyConnection(sql::connection_info::parse(dns)) -{} - -void DummyConnection::open() -{ - is_open_ = true; -} - -void DummyConnection::close() -{ - is_open_ = false; -} - -bool DummyConnection::is_open() const -{ - return is_open_; -} - -} \ No newline at end of file diff --git a/test/DummyConnection.hpp b/test/DummyConnection.hpp deleted file mode 100644 index 2e11849..0000000 --- a/test/DummyConnection.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef QUERY_DUMMYCONNECTION_HPP -#define QUERY_DUMMYCONNECTION_HPP - -#include "matador/sql/connection_info.hpp" - -namespace matador::test { - -class DummyConnection -{ -public: - explicit DummyConnection(sql::connection_info info); - explicit DummyConnection(const std::string& dns); - DummyConnection(const DummyConnection &x) = default; - DummyConnection& operator=(const DummyConnection &x) = default; - DummyConnection(DummyConnection &&x) noexcept = default; - DummyConnection& operator=(DummyConnection &&x) noexcept = default; - ~DummyConnection() = default; - - void open(); - void close(); - [[nodiscard]] bool is_open() const; - -private: - sql::connection_info connection_info_; - - bool is_open_{}; -}; - -} -#endif //QUERY_DUMMYCONNECTION_HPP