From 348e5d6489faae628cd5532e668f7b350a89da07 Mon Sep 17 00:00:00 2001 From: Frank Park Date: Sat, 25 Mar 2017 04:24:22 -0400 Subject: [PATCH 01/13] Added a generic connection pool Added a very basic outline of a generic connection pool. It should be thread-safe, but still needs a lot of work. --- include/sqlpp11/connection_pool.h | 137 ++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 include/sqlpp11/connection_pool.h diff --git a/include/sqlpp11/connection_pool.h b/include/sqlpp11/connection_pool.h new file mode 100644 index 00000000..72323d7b --- /dev/null +++ b/include/sqlpp11/connection_pool.h @@ -0,0 +1,137 @@ +#pragma once +#include +#include + +namespace sqlpp +{ + namespace mysql + { + template + class connection_pool + { + private: + std::mutex connection_pool_mutex; + const std::shared_ptr config; + + struct async_connection : Connection + { + async_connection(std::shared_ptr config) + : Connection(config) + {} + bool is_free = true; + }; + + std::vector connections; + + unsigned int default_pool_size = 0; + unsigned int current_pool_size = 0; + unsigned int max_pool_size = 0; + + void resize(unsigned int target_pool_size) + { + int lock = try_lock(connection_pool_mutex); + if (lock == -1) + { + throw sqlpp::exception("Connection_pool resize should not be called before locking."); + connection_pool_mutex.unlock(); + } + else + { + int diff = current_pool_size - target_pool_size; + if (!diff) + { + return; + } + + // does not guarantee to resize to target_pool_size + if (diff > 0) + { + for (auto it = connections.begin(); it != connections.end();) + { + if (diff && (*it).is_free) + { + it = connections.erase(it); + diff--; + } + else + { + ++it; + } + } + } + + if (diff < 0) + { + connections.reserve(target_pool_size); + for (int i = 0, count = -diff; i < count; i++) + { + try + { + connections.push_back(async_connection(config)); + diff++; + } + catch (const sqlpp::exception& e) + { + std::cerr << "Failed to spawn new connection." << std::endl; + std::cerr << e.what() << std::endl; + } + } + } + current_pool_size = target_pool_size + diff; + } + } + + public: + connection_pool(const std::shared_ptr& config, unsigned int pool_size) + : config(config), default_pool_size(pool_size) + { + std::lock_guard lock(connection_pool_mutex); + try + { + resize(pool_size); + } + catch (const sqlpp::exception& e) + { + std::cerr << "Failed to resize connection pool." << std::endl; + std::cerr << e.what() << std::endl; + } + } + ~connection_pool() = default; + connection_pool(const connection_pool&) = delete; + connection_pool(connection_pool&&) = delete; + connection_pool& operator=(const connection_pool&) = delete; + connection_pool& operator=(connection_pool&&) = delete; + + std::shared_ptr get_connection() + { + std::lock_guard lock(connection_pool_mutex); + for (auto& connection : connections) + { + if (connection.is_free) + { + connection.is_free = false; + return std::shared_ptr(static_cast(&connection)); + } + } + + try + { + resize(current_pool_size + 1); + } + catch (const sqlpp::exception& e) + { + std::cerr << "Failed to resize connection pool." << std::endl; + std::cerr << e.what() << std::endl; + } + + return std::shared_ptr(static_cast(&connections.back())); + } + + void free_connection(const std::shared_ptr& connection) + { + std::lock_guard lock(connection_pool_mutex); + static_cast(connection.get())->is_free = true; + } + }; + } +} From 8048337068a90ffd57c07a3ea3d78ca4d323f1fd Mon Sep 17 00:00:00 2001 From: Frank Park Date: Sat, 25 Mar 2017 06:01:48 -0400 Subject: [PATCH 02/13] fix namespace error --- include/sqlpp11/connection_pool.h | 251 +++++++++++++++--------------- 1 file changed, 124 insertions(+), 127 deletions(-) diff --git a/include/sqlpp11/connection_pool.h b/include/sqlpp11/connection_pool.h index 72323d7b..fb7f86c1 100644 --- a/include/sqlpp11/connection_pool.h +++ b/include/sqlpp11/connection_pool.h @@ -4,134 +4,131 @@ namespace sqlpp { - namespace mysql + template + class connection_pool { - template - class connection_pool + private: + std::mutex connection_pool_mutex; + const std::shared_ptr config; + + struct async_connection : Connection { - private: - std::mutex connection_pool_mutex; - const std::shared_ptr config; - - struct async_connection : Connection - { - async_connection(std::shared_ptr config) - : Connection(config) - {} - bool is_free = true; - }; - - std::vector connections; - - unsigned int default_pool_size = 0; - unsigned int current_pool_size = 0; - unsigned int max_pool_size = 0; - - void resize(unsigned int target_pool_size) - { - int lock = try_lock(connection_pool_mutex); - if (lock == -1) - { - throw sqlpp::exception("Connection_pool resize should not be called before locking."); - connection_pool_mutex.unlock(); - } - else - { - int diff = current_pool_size - target_pool_size; - if (!diff) - { - return; - } - - // does not guarantee to resize to target_pool_size - if (diff > 0) - { - for (auto it = connections.begin(); it != connections.end();) - { - if (diff && (*it).is_free) - { - it = connections.erase(it); - diff--; - } - else - { - ++it; - } - } - } - - if (diff < 0) - { - connections.reserve(target_pool_size); - for (int i = 0, count = -diff; i < count; i++) - { - try - { - connections.push_back(async_connection(config)); - diff++; - } - catch (const sqlpp::exception& e) - { - std::cerr << "Failed to spawn new connection." << std::endl; - std::cerr << e.what() << std::endl; - } - } - } - current_pool_size = target_pool_size + diff; - } - } - - public: - connection_pool(const std::shared_ptr& config, unsigned int pool_size) - : config(config), default_pool_size(pool_size) - { - std::lock_guard lock(connection_pool_mutex); - try - { - resize(pool_size); - } - catch (const sqlpp::exception& e) - { - std::cerr << "Failed to resize connection pool." << std::endl; - std::cerr << e.what() << std::endl; - } - } - ~connection_pool() = default; - connection_pool(const connection_pool&) = delete; - connection_pool(connection_pool&&) = delete; - connection_pool& operator=(const connection_pool&) = delete; - connection_pool& operator=(connection_pool&&) = delete; - - std::shared_ptr get_connection() - { - std::lock_guard lock(connection_pool_mutex); - for (auto& connection : connections) - { - if (connection.is_free) - { - connection.is_free = false; - return std::shared_ptr(static_cast(&connection)); - } - } - - try - { - resize(current_pool_size + 1); - } - catch (const sqlpp::exception& e) - { - std::cerr << "Failed to resize connection pool." << std::endl; - std::cerr << e.what() << std::endl; - } - - return std::shared_ptr(static_cast(&connections.back())); - } - - void free_connection(const std::shared_ptr& connection) - { - std::lock_guard lock(connection_pool_mutex); - static_cast(connection.get())->is_free = true; - } + async_connection(std::shared_ptr config) + : Connection(config) + {} + bool is_free = true; }; - } + + std::vector connections; + + unsigned int default_pool_size = 0; + unsigned int current_pool_size = 0; + unsigned int max_pool_size = 0; + + void resize(unsigned int target_pool_size) + { + int lock = try_lock(connection_pool_mutex); + if (lock == -1) + { + throw sqlpp::exception("Connection_pool resize should not be called before locking."); + connection_pool_mutex.unlock(); + } + else + { + int diff = current_pool_size - target_pool_size; + if (!diff) + { + return; + } + + // does not guarantee to resize to target_pool_size + if (diff > 0) + { + for (auto it = connections.begin(); it != connections.end();) + { + if (diff && (*it).is_free) + { + it = connections.erase(it); + diff--; + } + else + { + ++it; + } + } + } + + if (diff < 0) + { + connections.reserve(target_pool_size); + for (int i = 0, count = -diff; i < count; i++) + { + try + { + connections.push_back(async_connection(config)); + diff++; + } + catch (const sqlpp::exception& e) + { + std::cerr << "Failed to spawn new connection." << std::endl; + std::cerr << e.what() << std::endl; + } + } + } + current_pool_size = target_pool_size + diff; + } + } + + public: + connection_pool(const std::shared_ptr& config, unsigned int pool_size) + : config(config), default_pool_size(pool_size) + { + std::lock_guard lock(connection_pool_mutex); + try + { + resize(pool_size); + } + catch (const sqlpp::exception& e) + { + std::cerr << "Failed to resize connection pool." << std::endl; + std::cerr << e.what() << std::endl; + } + } + ~connection_pool() = default; + connection_pool(const connection_pool&) = delete; + connection_pool(connection_pool&&) = delete; + connection_pool& operator=(const connection_pool&) = delete; + connection_pool& operator=(connection_pool&&) = delete; + + std::shared_ptr get_connection() + { + std::lock_guard lock(connection_pool_mutex); + for (auto& connection : connections) + { + if (connection.is_free) + { + connection.is_free = false; + return std::shared_ptr(static_cast(&connection)); + } + } + + try + { + resize(current_pool_size + 1); + } + catch (const sqlpp::exception& e) + { + std::cerr << "Failed to resize connection pool." << std::endl; + std::cerr << e.what() << std::endl; + } + + return std::shared_ptr(static_cast(&connections.back())); + } + + void free_connection(const std::shared_ptr& connection) + { + std::lock_guard lock(connection_pool_mutex); + static_cast(connection.get())->is_free = true; + } + }; } From 269a72bd93f8565c3c75d9502406c1e18f75d088 Mon Sep 17 00:00:00 2001 From: Frank Park Date: Sat, 25 Mar 2017 07:14:36 -0400 Subject: [PATCH 03/13] change container to stack of unique_ptr --- include/sqlpp11/connection_pool.h | 119 +++++++++--------------------- 1 file changed, 36 insertions(+), 83 deletions(-) diff --git a/include/sqlpp11/connection_pool.h b/include/sqlpp11/connection_pool.h index fb7f86c1..6811a9d7 100644 --- a/include/sqlpp11/connection_pool.h +++ b/include/sqlpp11/connection_pool.h @@ -1,6 +1,7 @@ #pragma once #include -#include +#include +#include namespace sqlpp { @@ -10,74 +11,8 @@ namespace sqlpp private: std::mutex connection_pool_mutex; const std::shared_ptr config; - - struct async_connection : Connection - { - async_connection(std::shared_ptr config) - : Connection(config) - {} - bool is_free = true; - }; - - std::vector connections; - unsigned int default_pool_size = 0; - unsigned int current_pool_size = 0; - unsigned int max_pool_size = 0; - - void resize(unsigned int target_pool_size) - { - int lock = try_lock(connection_pool_mutex); - if (lock == -1) - { - throw sqlpp::exception("Connection_pool resize should not be called before locking."); - connection_pool_mutex.unlock(); - } - else - { - int diff = current_pool_size - target_pool_size; - if (!diff) - { - return; - } - - // does not guarantee to resize to target_pool_size - if (diff > 0) - { - for (auto it = connections.begin(); it != connections.end();) - { - if (diff && (*it).is_free) - { - it = connections.erase(it); - diff--; - } - else - { - ++it; - } - } - } - - if (diff < 0) - { - connections.reserve(target_pool_size); - for (int i = 0, count = -diff; i < count; i++) - { - try - { - connections.push_back(async_connection(config)); - diff++; - } - catch (const sqlpp::exception& e) - { - std::cerr << "Failed to spawn new connection." << std::endl; - std::cerr << e.what() << std::endl; - } - } - } - current_pool_size = target_pool_size + diff; - } - } + std::stack> free_connections; public: connection_pool(const std::shared_ptr& config, unsigned int pool_size) @@ -86,11 +21,14 @@ namespace sqlpp std::lock_guard lock(connection_pool_mutex); try { - resize(pool_size); + for (int i = 0; i < pool_size; i++) + { + free_connections.push(std::make_unique(config)); + } } catch (const sqlpp::exception& e) { - std::cerr << "Failed to resize connection pool." << std::endl; + std::cerr << "Failed to spawn new connection." << std::endl; std::cerr << e.what() << std::endl; } } @@ -100,35 +38,50 @@ namespace sqlpp connection_pool& operator=(const connection_pool&) = delete; connection_pool& operator=(connection_pool&&) = delete; - std::shared_ptr get_connection() + std::unique_ptr get_connection() { std::lock_guard lock(connection_pool_mutex); - for (auto& connection : connections) + if (!free_connections.empty()) { - if (connection.is_free) - { - connection.is_free = false; - return std::shared_ptr(static_cast(&connection)); - } + auto connection = std::move(free_connections.top()); + free_connections.pop(); + return connection; } try { - resize(current_pool_size + 1); + auto connection = std::make_unique(config); + return connection; } catch (const sqlpp::exception& e) { - std::cerr << "Failed to resize connection pool." << std::endl; + std::cerr << "Failed to spawn new connection." << std::endl; std::cerr << e.what() << std::endl; + return std::unique_ptr(); } - - return std::shared_ptr(static_cast(&connections.back())); } - void free_connection(const std::shared_ptr& connection) + void free_connection(std::unique_ptr connection) { std::lock_guard lock(connection_pool_mutex); - static_cast(connection.get())->is_free = true; + if (free_connections.size() >= default_pool_size) + { + // Exceeds default size, do nothing and let unique_ptr self destroy. + } + else + { + // TODO: we don't know if the connection is originally from this connection pool. + // There's no way to check because we don't have access to config info in the Connection class: + // connection.get()->_handle->config --- _handle is private + if (connection.get()) + { + free_connections.push(std::move(connection)); + } + else + { + throw sqlpp::exception("Trying to free an empty connection."); + } + } } }; } From de2204c07f93c4e97665dabb19021ec8204e733a Mon Sep 17 00:00:00 2001 From: Frank Park Date: Sat, 25 Mar 2017 17:01:12 -0400 Subject: [PATCH 04/13] add copyright notice and comment on free_connection --- include/sqlpp11/connection_pool.h | 39 +++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/include/sqlpp11/connection_pool.h b/include/sqlpp11/connection_pool.h index 6811a9d7..571eee26 100644 --- a/include/sqlpp11/connection_pool.h +++ b/include/sqlpp11/connection_pool.h @@ -1,4 +1,32 @@ -#pragma once +/* +* Copyright (c) 2013 - 2016, Roland Bock +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SQLPP_CONNECTION_POOL_H +#define SQLPP_CONNECTION_POOL_H + #include #include #include @@ -60,7 +88,9 @@ namespace sqlpp return std::unique_ptr(); } } - + + // Caller is responsible for making sure the connection being + // returned has the same configuration as the connection pool. void free_connection(std::unique_ptr connection) { std::lock_guard lock(connection_pool_mutex); @@ -70,9 +100,6 @@ namespace sqlpp } else { - // TODO: we don't know if the connection is originally from this connection pool. - // There's no way to check because we don't have access to config info in the Connection class: - // connection.get()->_handle->config --- _handle is private if (connection.get()) { free_connections.push(std::move(connection)); @@ -85,3 +112,5 @@ namespace sqlpp } }; } + +#endif From b05c968bb280351f594eae5483c65434ce671022 Mon Sep 17 00:00:00 2001 From: Frank Park Date: Sun, 26 Mar 2017 21:52:33 -0400 Subject: [PATCH 05/13] add is_connection_compatible along with some minor changes --- include/sqlpp11/connection_pool.h | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/include/sqlpp11/connection_pool.h b/include/sqlpp11/connection_pool.h index 571eee26..d81e8c89 100644 --- a/include/sqlpp11/connection_pool.h +++ b/include/sqlpp11/connection_pool.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013 - 2016, Roland Bock +* Copyright (c) 2013 - 2017, Roland Bock, Frank Park * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -30,6 +30,8 @@ #include #include #include +#include +#include namespace sqlpp { @@ -39,12 +41,14 @@ namespace sqlpp private: std::mutex connection_pool_mutex; const std::shared_ptr config; - unsigned int default_pool_size = 0; + unsigned int maximum_pool_size = 0; std::stack> free_connections; + } + public: connection_pool(const std::shared_ptr& config, unsigned int pool_size) - : config(config), default_pool_size(pool_size) + : config(config), maximum_pool_size(pool_size) { std::lock_guard lock(connection_pool_mutex); try @@ -56,8 +60,8 @@ namespace sqlpp } catch (const sqlpp::exception& e) { - std::cerr << "Failed to spawn new connection." << std::endl; - std::cerr << e.what() << std::endl; + std::cerr << "Failed to spawn a new connection." << std::endl; + throw; } } ~connection_pool() = default; @@ -83,9 +87,8 @@ namespace sqlpp } catch (const sqlpp::exception& e) { - std::cerr << "Failed to spawn new connection." << std::endl; - std::cerr << e.what() << std::endl; - return std::unique_ptr(); + std::cerr << "Failed to spawn a new connection." << std::endl; + throw; } } @@ -94,7 +97,7 @@ namespace sqlpp void free_connection(std::unique_ptr connection) { std::lock_guard lock(connection_pool_mutex); - if (free_connections.size() >= default_pool_size) + if (free_connections.size() >= maximum_pool_size) { // Exceeds default size, do nothing and let unique_ptr self destroy. } @@ -102,7 +105,13 @@ namespace sqlpp { if (connection.get()) { - free_connections.push(std::move(connection)); + { + free_connections.push(std::move(connection)); + } + else + { + throw sqlpp::exception("Trying to free a connection with incompatible config."); + } } else { From 5f0a5dd0e95cb7249a9e076a5126e2c213e2b304 Mon Sep 17 00:00:00 2001 From: Frank Park Date: Sun, 26 Mar 2017 21:53:09 -0400 Subject: [PATCH 06/13] rename function --- include/sqlpp11/connection_pool.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/sqlpp11/connection_pool.h b/include/sqlpp11/connection_pool.h index d81e8c89..72004c80 100644 --- a/include/sqlpp11/connection_pool.h +++ b/include/sqlpp11/connection_pool.h @@ -44,6 +44,8 @@ namespace sqlpp unsigned int maximum_pool_size = 0; std::stack> free_connections; + bool is_connection_compatible(const std::unique_ptr& connection) { + return connection->get_config().get() == this->config.get(); } public: @@ -105,6 +107,7 @@ namespace sqlpp { if (connection.get()) { + if (is_connection_compatible(connection)) { free_connections.push(std::move(connection)); } From 6b36cd56f18a375719a2aab70dcfcc2f880606c1 Mon Sep 17 00:00:00 2001 From: Frank Park Date: Mon, 27 Mar 2017 00:50:59 -0400 Subject: [PATCH 07/13] disable connection pooling on construction --- include/sqlpp11/connection_pool.h | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/include/sqlpp11/connection_pool.h b/include/sqlpp11/connection_pool.h index 72004c80..92c3a1bd 100644 --- a/include/sqlpp11/connection_pool.h +++ b/include/sqlpp11/connection_pool.h @@ -51,21 +51,7 @@ namespace sqlpp public: connection_pool(const std::shared_ptr& config, unsigned int pool_size) : config(config), maximum_pool_size(pool_size) - { - std::lock_guard lock(connection_pool_mutex); - try - { - for (int i = 0; i < pool_size; i++) - { - free_connections.push(std::make_unique(config)); - } - } - catch (const sqlpp::exception& e) - { - std::cerr << "Failed to spawn a new connection." << std::endl; - throw; - } - } + {} ~connection_pool() = default; connection_pool(const connection_pool&) = delete; connection_pool(connection_pool&&) = delete; From 4fa79d552acceca28eb6032f4bc476e9c2c1090f Mon Sep 17 00:00:00 2001 From: Frank Park Date: Mon, 27 Mar 2017 04:58:49 -0400 Subject: [PATCH 08/13] Added async_connection to comply with RAII --- include/sqlpp11/connection_pool.h | 50 ++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/include/sqlpp11/connection_pool.h b/include/sqlpp11/connection_pool.h index 92c3a1bd..79964882 100644 --- a/include/sqlpp11/connection_pool.h +++ b/include/sqlpp11/connection_pool.h @@ -44,10 +44,38 @@ namespace sqlpp unsigned int maximum_pool_size = 0; std::stack> free_connections; - bool is_connection_compatible(const std::unique_ptr& connection) { - return connection->get_config().get() == this->config.get(); - } + struct async_connection : public Connection + { + friend class connection_pool; + private: + connection_pool* origin; + + public: + async_connection(std::unique_ptr& connection, connection_pool* origin) : + Connection(std::move(*connection.get())), origin(origin) {} + + ~async_connection() + { + // is_valid() checks if the Connection object has a valid SQL handle + // to sure we are not moving an empty connection back to the pool + if (this->is_valid()) + { + origin->free_connection(std::make_unique(std::move(*this))); + } + } + + async_connection(const async_connection&) = delete; + async_connection(async_connection&& other) + : Connection(std::move(other)), origin(other.origin) {} + async_connection& operator=(const async_connection&) = delete; + async_connection& operator=(async_connection&&) = delete; + }; + + bool is_connection_compatible(const std::unique_ptr& connection) + { + return connection.get()->get_config().get() == this->config.get(); + } public: connection_pool(const std::shared_ptr& config, unsigned int pool_size) : config(config), maximum_pool_size(pool_size) @@ -58,36 +86,38 @@ namespace sqlpp connection_pool& operator=(const connection_pool&) = delete; connection_pool& operator=(connection_pool&&) = delete; - std::unique_ptr get_connection() + std::unique_ptr get_connection() { std::lock_guard lock(connection_pool_mutex); if (!free_connections.empty()) { auto connection = std::move(free_connections.top()); free_connections.pop(); - return connection; + return std::make_unique(connection, this); } try { - auto connection = std::make_unique(config); - return connection; + return std::make_unique(std::move(std::make_unique(config)), this); } catch (const sqlpp::exception& e) { std::cerr << "Failed to spawn a new connection." << std::endl; + std::cerr << e.what() << std::endl; throw; } } - + // Caller is responsible for making sure the connection being // returned has the same configuration as the connection pool. - void free_connection(std::unique_ptr connection) + void free_connection(std::unique_ptr connection) { std::lock_guard lock(connection_pool_mutex); if (free_connections.size() >= maximum_pool_size) { - // Exceeds default size, do nothing and let unique_ptr self destroy. + // Exceeds default size, empty the async_connection object + // so that it doesn't trigger RAII + Connection destructor = std::move(*connection.get()); } else { From 30caa5d878036746d76c7992d80c2ccfa93bfc10 Mon Sep 17 00:00:00 2001 From: Frank Park Date: Wed, 29 Mar 2017 01:07:15 -0400 Subject: [PATCH 09/13] changed async connection to a wrapper instead of derived class --- include/sqlpp11/connection_pool.h | 100 ++++++++++++++++-------------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/include/sqlpp11/connection_pool.h b/include/sqlpp11/connection_pool.h index 79964882..832742e3 100644 --- a/include/sqlpp11/connection_pool.h +++ b/include/sqlpp11/connection_pool.h @@ -44,86 +44,60 @@ namespace sqlpp unsigned int maximum_pool_size = 0; std::stack> free_connections; - struct async_connection : public Connection + struct async_connection { friend class connection_pool; private: + std::unique_ptr _impl; connection_pool* origin; public: async_connection(std::unique_ptr& connection, connection_pool* origin) : - Connection(std::move(*connection.get())), origin(origin) {} + _impl(std::move(connection)), origin(origin) {} ~async_connection() { - // is_valid() checks if the Connection object has a valid SQL handle - // to sure we are not moving an empty connection back to the pool - if (this->is_valid()) - { - origin->free_connection(std::make_unique(std::move(*this))); - } + origin->free_connection(_impl); + } + + template + auto operator()(Args&&... args) -> decltype(_impl->args(std::forward(args)...)) + { + return _impl->args(std::forward(args)...); + } + + Connection* operator->() + { + return &_impl; } async_connection(const async_connection&) = delete; async_connection(async_connection&& other) - : Connection(std::move(other)), origin(other.origin) {} + : _impl(std::move(other._impl)), origin(other.origin) {} async_connection& operator=(const async_connection&) = delete; async_connection& operator=(async_connection&&) = delete; }; - bool is_connection_compatible(const std::unique_ptr& connection) + bool is_connection_compatible(const async_connection& connection) { - return connection.get()->get_config().get() == this->config.get(); - } - public: - connection_pool(const std::shared_ptr& config, unsigned int pool_size) - : config(config), maximum_pool_size(pool_size) - {} - ~connection_pool() = default; - connection_pool(const connection_pool&) = delete; - connection_pool(connection_pool&&) = delete; - connection_pool& operator=(const connection_pool&) = delete; - connection_pool& operator=(connection_pool&&) = delete; - - std::unique_ptr get_connection() - { - std::lock_guard lock(connection_pool_mutex); - if (!free_connections.empty()) - { - auto connection = std::move(free_connections.top()); - free_connections.pop(); - return std::make_unique(connection, this); - } - - try - { - return std::make_unique(std::move(std::make_unique(config)), this); - } - catch (const sqlpp::exception& e) - { - std::cerr << "Failed to spawn a new connection." << std::endl; - std::cerr << e.what() << std::endl; - throw; - } + return connection._impl->get_config().get() == this->config.get(); } // Caller is responsible for making sure the connection being // returned has the same configuration as the connection pool. - void free_connection(std::unique_ptr connection) + void free_connection(std::unique_ptr& connection) { std::lock_guard lock(connection_pool_mutex); if (free_connections.size() >= maximum_pool_size) { - // Exceeds default size, empty the async_connection object - // so that it doesn't trigger RAII - Connection destructor = std::move(*connection.get()); + // Exceeds default size, do nothign and let connection self destroy. } else { if (connection.get()) { - if (is_connection_compatible(connection)) + if (connection->is_valid()) { free_connections.push(std::move(connection)); } @@ -138,6 +112,38 @@ namespace sqlpp } } } + + public: + connection_pool(const std::shared_ptr& config, unsigned int pool_size) + : config(config), maximum_pool_size(pool_size) + {} + ~connection_pool() = default; + connection_pool(const connection_pool&) = delete; + connection_pool(connection_pool&&) = delete; + connection_pool& operator=(const connection_pool&) = delete; + connection_pool& operator=(connection_pool&&) = delete; + + async_connection get_connection() + { + std::lock_guard lock(connection_pool_mutex); + if (!free_connections.empty()) + { + auto connection = std::move(free_connections.top()); + free_connections.pop(); + return async_connection(connection, this); + } + + try + { + return async_connection(std::move(std::make_unique(config)), this); + } + catch (const sqlpp::exception& e) + { + std::cerr << "Failed to spawn a new connection." << std::endl; + std::cerr << e.what() << std::endl; + throw; + } + } }; } From e0d7d71bb307d36e6ab8c44ee7dcd81d225dbcbd Mon Sep 17 00:00:00 2001 From: Frank Park Date: Wed, 29 Mar 2017 01:27:42 -0400 Subject: [PATCH 10/13] delete old comment --- include/sqlpp11/connection_pool.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/sqlpp11/connection_pool.h b/include/sqlpp11/connection_pool.h index 832742e3..7b91314e 100644 --- a/include/sqlpp11/connection_pool.h +++ b/include/sqlpp11/connection_pool.h @@ -84,8 +84,6 @@ namespace sqlpp return connection._impl->get_config().get() == this->config.get(); } - // Caller is responsible for making sure the connection being - // returned has the same configuration as the connection pool. void free_connection(std::unique_ptr& connection) { std::lock_guard lock(connection_pool_mutex); From ac88aa76561096baf6fbe566d7a41b582ed793c5 Mon Sep 17 00:00:00 2001 From: Frank Park Date: Wed, 29 Mar 2017 01:28:52 -0400 Subject: [PATCH 11/13] delete unused function --- include/sqlpp11/connection_pool.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/sqlpp11/connection_pool.h b/include/sqlpp11/connection_pool.h index 7b91314e..92465dfa 100644 --- a/include/sqlpp11/connection_pool.h +++ b/include/sqlpp11/connection_pool.h @@ -79,11 +79,6 @@ namespace sqlpp async_connection& operator=(async_connection&&) = delete; }; - bool is_connection_compatible(const async_connection& connection) - { - return connection._impl->get_config().get() == this->config.get(); - } - void free_connection(std::unique_ptr& connection) { std::lock_guard lock(connection_pool_mutex); From 71a234c4ba99b3a2e9591964da3073cd26df6e58 Mon Sep 17 00:00:00 2001 From: Frank Park Date: Sun, 2 Apr 2017 10:48:14 -0400 Subject: [PATCH 12/13] Split pool_connection to separate file, add reconnect_policy Also expanded class template and constructor of connection_pool to allow lazy instantiation, added helper function make_connection_pool to make instantiation lazier. --- include/sqlpp11/connection_pool.h | 140 ++++++++++++++++++++---------- include/sqlpp11/pool_connection.h | 76 ++++++++++++++++ 2 files changed, 172 insertions(+), 44 deletions(-) create mode 100644 include/sqlpp11/pool_connection.h diff --git a/include/sqlpp11/connection_pool.h b/include/sqlpp11/connection_pool.h index 92465dfa..5d5181da 100644 --- a/include/sqlpp11/connection_pool.h +++ b/include/sqlpp11/connection_pool.h @@ -29,55 +29,93 @@ #include #include +#include #include #include +#include +#include #include +#include namespace sqlpp { - template + namespace reconnect_policy + { + struct auto_reconnect { + template + void operator()(Connection* connection) + { + if(!connection->is_valid()) + connection->reconnect() + } + template + void clean(Connection* connection) {} + }; + + using namespace std::chrono_literals; + class periodic_reconnect + { + private: + std::chrono::seconds revalidate_after; + std::unordered_map > last_checked; + + public: + periodic_reconnect(const std::chrono::seconds r = 28800s) //default wait_timeout in MySQL + : revalidate_after(r), last_checked() {} + + template + void operator()(Connection* con) + { + auto last = last_checked.find(con); + auto now = std::chrono::system_clock::now(); + if(last == last_checked.end()) + { + if (!con->is_valid()) + { + con->reconnect(); + } + last_checked.emplace_hint(last, con, now); + } + else if(now - last->second > revalidate_after) + { + if (!con->is_valid()) + { + con->reconnect(); + } + last = now; + } + } + template + void clean(Connection* con) { + auto itr = last_checked.find(con); + if(itr != last_checked.end()) + { + last_checked.erase(itr); + } + } + }; + + struct never_reconnect { + template + void operator()(Connection*) {} + template + void clean(Connection*) {} + }; + } + + template ::value, Connection_config::connection>::type, + typename Reconnect_policy = reconnect_policy::auto_reconnect> class connection_pool { + friend pool_connection; + private: std::mutex connection_pool_mutex; const std::shared_ptr config; - unsigned int maximum_pool_size = 0; + size_t maximum_pool_size = 0; std::stack> free_connections; - - struct async_connection - { - friend class connection_pool; - - private: - std::unique_ptr _impl; - connection_pool* origin; - - public: - async_connection(std::unique_ptr& connection, connection_pool* origin) : - _impl(std::move(connection)), origin(origin) {} - - ~async_connection() - { - origin->free_connection(_impl); - } - - template - auto operator()(Args&&... args) -> decltype(_impl->args(std::forward(args)...)) - { - return _impl->args(std::forward(args)...); - } - - Connection* operator->() - { - return &_impl; - } - - async_connection(const async_connection&) = delete; - async_connection(async_connection&& other) - : _impl(std::move(other._impl)), origin(other.origin) {} - async_connection& operator=(const async_connection&) = delete; - async_connection& operator=(async_connection&&) = delete; - }; + Reconnect_policy reconnect_policy; void free_connection(std::unique_ptr& connection) { @@ -107,28 +145,30 @@ namespace sqlpp } public: - connection_pool(const std::shared_ptr& config, unsigned int pool_size) - : config(config), maximum_pool_size(pool_size) - {} + connection_pool(const std::shared_ptr& config, size_t pool_size, + Reconnect_policy reconnect_policy = sqlpp::reconnect_policy::auto_reconnect()) + : config(config), maximum_pool_size(pool_size), reconnect_policy(reconnect_policy) {} ~connection_pool() = default; connection_pool(const connection_pool&) = delete; - connection_pool(connection_pool&&) = delete; + connection_pool(connection_pool&& other) + : config(std::move(other.config)), maximum_pool_size(std::move(other.maximum_pool_size)), + reconnect_policy(std::move(other.reconnect_policy)) {} connection_pool& operator=(const connection_pool&) = delete; connection_pool& operator=(connection_pool&&) = delete; - async_connection get_connection() + pool_connection get_connection() { std::lock_guard lock(connection_pool_mutex); if (!free_connections.empty()) { auto connection = std::move(free_connections.top()); free_connections.pop(); - return async_connection(connection, this); + return pool_connection(connection, this); } try { - return async_connection(std::move(std::make_unique(config)), this); + return pool_connection(std::move(std::make_unique(config)), this); } catch (const sqlpp::exception& e) { @@ -138,6 +178,18 @@ namespace sqlpp } } }; + + template::value, Connection_config::connection>::type, + typename Reconnect_policy = reconnect_policy::auto_reconnect> + connection_pool make_connection_pool( + const std::shared_ptr& config, + size_t max_pool_size, + Reconnect_policy reconnect_policy = reconnect_policy::auto_reconnect() + ) + { + return connection_pool(config, max_pool_size, reconnect_policy); + } } #endif diff --git a/include/sqlpp11/pool_connection.h b/include/sqlpp11/pool_connection.h new file mode 100644 index 00000000..426e0c83 --- /dev/null +++ b/include/sqlpp11/pool_connection.h @@ -0,0 +1,76 @@ +/* +* Copyright (c) 2013 - 2017, Roland Bock, Frank Park +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SQLPP_POOL_CONNECTION_H +#define SQLPP_POOL_CONNECTION_H + +#include + +namespace sqlpp +{ + template > + struct pool_connection + { + private: + std::unique_ptr _impl; + Connection_pool* origin; + + public: + pool_connection(std::unique_ptr& connection, Connection_pool* origin) + : _impl(std::move(connection)), origin(origin) {} + + ~pool_connection() + { + origin->free_connection(_impl); + } + + template + auto operator()(Args&&... args) -> decltype(_impl->args(std::forward(args)...)) + { + return _impl->args(std::forward(args)...); + } + + template + auto operator()(const T& t) -> decltype(_impl->run(t)) + { + return _impl->run(t); + } + + Connection* operator->() + { + return &_impl; + } + + pool_connection(const pool_connection&) = delete; + pool_connection(pool_connection&& other) + : _impl(std::move(other._impl)), origin(other.origin) {} + pool_connection& operator=(const pool_connection&) = delete; + pool_connection& operator=(pool_connection&&) = delete; + }; +} + +#endif From 9f26495ced872d772f063394e7e8f5a75f1921df Mon Sep 17 00:00:00 2001 From: Frank Park Date: Sun, 2 Apr 2017 11:06:12 -0400 Subject: [PATCH 13/13] Reordered template arguments --- include/sqlpp11/connection_pool.h | 29 +++++++++++++---------------- include/sqlpp11/pool_connection.h | 4 ++-- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/include/sqlpp11/connection_pool.h b/include/sqlpp11/connection_pool.h index 5d5181da..67ce9ea9 100644 --- a/include/sqlpp11/connection_pool.h +++ b/include/sqlpp11/connection_pool.h @@ -104,11 +104,11 @@ namespace sqlpp } template ::value, Connection_config::connection>::type, - typename Reconnect_policy = reconnect_policy::auto_reconnect> + typename Reconnect_policy = reconnect_policy::auto_reconnect, + typename Connection = typename std::enable_if::value, Connection_config::connection>::type> class connection_pool { - friend pool_connection; + friend pool_connection; private: std::mutex connection_pool_mutex; @@ -145,9 +145,8 @@ namespace sqlpp } public: - connection_pool(const std::shared_ptr& config, size_t pool_size, - Reconnect_policy reconnect_policy = sqlpp::reconnect_policy::auto_reconnect()) - : config(config), maximum_pool_size(pool_size), reconnect_policy(reconnect_policy) {} + connection_pool(const std::shared_ptr& config, size_t pool_size) + : config(config), maximum_pool_size(pool_size), reconnect_policy(Reconnect_policy()) {} ~connection_pool() = default; connection_pool(const connection_pool&) = delete; connection_pool(connection_pool&& other) @@ -156,19 +155,19 @@ namespace sqlpp connection_pool& operator=(const connection_pool&) = delete; connection_pool& operator=(connection_pool&&) = delete; - pool_connection get_connection() + pool_connection get_connection() { std::lock_guard lock(connection_pool_mutex); if (!free_connections.empty()) { auto connection = std::move(free_connections.top()); free_connections.pop(); - return pool_connection(connection, this); + return pool_connection(connection, this); } try { - return pool_connection(std::move(std::make_unique(config)), this); + return pool_connection(std::move(std::make_unique(config)), this); } catch (const sqlpp::exception& e) { @@ -180,15 +179,13 @@ namespace sqlpp }; template::value, Connection_config::connection>::type, - typename Reconnect_policy = reconnect_policy::auto_reconnect> - connection_pool make_connection_pool( + typename Reconnect_policy = reconnect_policy::auto_reconnect, + typename Connection = typename std::enable_if::value,Connection_config::connection>::type> + connection_pool make_connection_pool( const std::shared_ptr& config, - size_t max_pool_size, - Reconnect_policy reconnect_policy = reconnect_policy::auto_reconnect() - ) + size_t max_pool_size) { - return connection_pool(config, max_pool_size, reconnect_policy); + return connection_pool(config, max_pool_size); } } diff --git a/include/sqlpp11/pool_connection.h b/include/sqlpp11/pool_connection.h index 426e0c83..12ba5bce 100644 --- a/include/sqlpp11/pool_connection.h +++ b/include/sqlpp11/pool_connection.h @@ -31,8 +31,8 @@ namespace sqlpp { - template > + template > struct pool_connection { private: