Skip to content

Commit

Permalink
First implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
anarthal committed Nov 3, 2023
1 parent 4953aea commit 891bba1
Show file tree
Hide file tree
Showing 10 changed files with 1,275 additions and 4 deletions.
1 change: 1 addition & 0 deletions include/boost/mysql.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <boost/mysql/common_server_errc.hpp>
#include <boost/mysql/connection.hpp>
#include <boost/mysql/connection_base.hpp>
#include <boost/mysql/connection_pool.hpp>
#include <boost/mysql/date.hpp>
#include <boost/mysql/datetime.hpp>
#include <boost/mysql/days.hpp>
Expand Down
3 changes: 3 additions & 0 deletions include/boost/mysql/any_connection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ class any_connection : public connection_base<asio::any_io_executor>
);
}

// TODO: do we want to expose this?
void force_close(error_code& ec);

private:
using base_type = connection_base<asio::any_io_executor>;

Expand Down
228 changes: 228 additions & 0 deletions include/boost/mysql/connection_pool.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
//
// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef BOOST_MYSQL_CONNECTION_POOL_HPP
#define BOOST_MYSQL_CONNECTION_POOL_HPP

#include <boost/mysql/any_address.hpp>
#include <boost/mysql/any_connection.hpp>
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/handshake_params.hpp>

#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/connection_pool_helpers.hpp>

#include <boost/asio/any_completion_handler.hpp>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/async_result.hpp>

#include <chrono>
#include <memory>

namespace boost {
namespace mysql {

class connection_pool;

// TODO: review this
struct pool_params
{
any_address_view server_address;
handshake_params handshake_params;
std::size_t initial_size{1};
std::size_t max_size{150}; // TODO: is this MySQL's max by default?
bool enable_thread_safety{true};

std::chrono::steady_clock::duration connect_timeout{std::chrono::seconds(20)};
std::chrono::steady_clock::duration ping_timeout{std::chrono::seconds(5)};
std::chrono::steady_clock::duration reset_timeout{std::chrono::seconds(10)};
std::chrono::steady_clock::duration retry_interval{std::chrono::seconds(10)};
std::chrono::steady_clock::duration ping_interval{std::chrono::hours(1)};
// TODO: SSL
};

class pooled_connection
{
friend class connection_pool;

BOOST_MYSQL_DECL
const any_connection* const_ptr() const noexcept;

any_connection* ptr() noexcept { return const_cast<any_connection*>(const_ptr()); }

pooled_connection(detail::connection_node& node) noexcept : impl_(&node) {}

std::unique_ptr<detail::connection_node, detail::connection_node_deleter> impl_;

public:
pooled_connection() noexcept = default;
pooled_connection(const pooled_connection&) = delete;
pooled_connection(pooled_connection&&) = default;
pooled_connection& operator=(const pooled_connection&) = delete;
pooled_connection& operator=(pooled_connection&&) = default;
~pooled_connection() = default;

bool valid() const noexcept { return impl_.get() != nullptr; }

any_connection& get() noexcept { return *ptr(); }
const any_connection& get() const noexcept { return *const_ptr(); }
any_connection* operator->() noexcept { return ptr(); }
const any_connection* operator->() const noexcept { return const_ptr(); }
};

class connection_pool
{
std::unique_ptr<detail::connection_pool_impl> impl_;

BOOST_MYSQL_DECL
static void async_run_impl(
detail::connection_pool_impl& impl,
asio::any_completion_handler<void(error_code)> handler
);

struct initiate_run
{
template <class Handler>
void operator()(Handler&& handler, detail::connection_pool_impl* impl) const
{
async_run_impl(*impl, std::forward<Handler>(handler));
}
};

BOOST_MYSQL_DECL
static void async_get_connection_impl(
detail::connection_pool_impl& impl,
std::chrono::steady_clock::duration timeout,
diagnostics* diag,
asio::any_completion_handler<void(error_code, pooled_connection)> handler
);

struct initiate_get_connection
{
template <class Handler>
void operator()(
Handler&& handler,
detail::connection_pool_impl* impl,
diagnostics* diag,
std::chrono::steady_clock::duration timeout
) const
{
async_get_connection_impl(*impl, timeout, diag, std::forward<Handler>(handler));
}
};

template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, pooled_connection))
async_get_connection(
std::chrono::steady_clock::duration timeout,
diagnostics* diag,
CompletionToken&& token
)
{
BOOST_ASSERT(valid());
return asio::async_initiate<CompletionToken, void(error_code)>(
initiate_get_connection(),
token,
this,
diag,
timeout
);
}

static constexpr std::chrono::steady_clock::duration get_default_timeout() noexcept
{
return std::chrono::seconds(30);
}

public:
BOOST_MYSQL_DECL
connection_pool(asio::any_io_executor ex, const pool_params& params);

BOOST_MYSQL_DECL
connection_pool(connection_pool&& rhs) noexcept;

BOOST_MYSQL_DECL
connection_pool& operator=(connection_pool&&) noexcept;

#ifndef BOOST_MYSQL_DOXYGEN
connection_pool(const connection_pool&) = delete;
connection_pool& operator=(const connection_pool&) = delete;
#endif

BOOST_MYSQL_DECL
~connection_pool();

bool valid() const noexcept { return impl_.get() != nullptr; }

template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_run(CompletionToken&& token)
{
BOOST_ASSERT(valid());
return asio::async_initiate<CompletionToken, void(error_code)>(initiate_run(), token, this);
}

template <
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::pooled_connection))
CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, pooled_connection))
async_get_connection(CompletionToken&& token)
{
return async_get_connection(get_default_timeout(), nullptr, std::forward<CompletionToken>(token));
}

template <
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::pooled_connection))
CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, pooled_connection))
async_get_connection(diagnostics& diag, CompletionToken&& token)
{
return async_get_connection(get_default_timeout(), &diag, std::forward<CompletionToken>(token));
}

template <
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::pooled_connection))
CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, pooled_connection))
async_get_connection(std::chrono::steady_clock::duration timeout, CompletionToken&& token)
{
return async_get_connection(timeout, nullptr, std::forward<CompletionToken>(token));
}

template <
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, ::boost::mysql::pooled_connection))
CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, pooled_connection))
async_get_connection(
std::chrono::steady_clock::duration timeout,
diagnostics& diag,
CompletionToken&& token
)
{
return async_get_connection(timeout, &diag, std::forward<CompletionToken>(token));
}

void return_connection(pooled_connection&& conn, bool should_reset = true) noexcept
{
BOOST_ASSERT(valid());
if (conn.valid())
detail::return_connection(*conn.impl_.release(), should_reset);
}

BOOST_MYSQL_DECL
void cancel();
};

} // namespace mysql
} // namespace boost

#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/connection_pool.ipp>
#endif

#endif
8 changes: 4 additions & 4 deletions include/boost/mysql/detail/connection_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,21 +257,21 @@ class connection_impl
run_algo(*stream_, *st_, connect_algo_params{&connect_arg, &diag, params}, err);
}

template <class Stream, class CompletionToken>
template <class ConnectArg, class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_connect(
const typename Stream::lowest_layer_type::endpoint_type& endpoint,
const ConnectArg& connect_arg,
const handshake_params& params,
diagnostics& diag,
CompletionToken&& token
)
{
return asio::async_initiate<CompletionToken, void(error_code)>(
connect_initiation<Stream>(),
connect_initiation<ConnectArg>(),
token,
stream_.get(),
st_.get(),
endpoint,
connect_arg,
params,
&diag
);
Expand Down
33 changes: 33 additions & 0 deletions include/boost/mysql/detail/connection_pool_helpers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef BOOST_MYSQL_DETAIL_CONNECTION_POOL_HELPERS_HPP
#define BOOST_MYSQL_DETAIL_CONNECTION_POOL_HELPERS_HPP

#include <boost/mysql/detail/config.hpp>

namespace boost {
namespace mysql {
namespace detail {

// Foward decl
class connection_node;
class connection_pool_impl;

BOOST_MYSQL_DECL
void return_connection(connection_node& node, bool should_reset) noexcept;

struct connection_node_deleter
{
void operator()(detail::connection_node* node) const noexcept { return_connection(*node, true); }
};

} // namespace detail
} // namespace mysql
} // namespace boost

#endif
Loading

0 comments on commit 891bba1

Please sign in to comment.