3 #include <ozo/failover/strategy.h>
4 #include <ozo/failover/retry.h>
5 #include <ozo/core/options.h>
6 #include <ozo/ext/std/optional.h>
7 #include <ozo/ext/std/nullopt_t.h>
9 #include <boost/hana/tuple.hpp>
10 #include <boost/hana/empty.hpp>
11 #include <boost/hana/size.hpp>
12 #include <boost/hana/minus.hpp>
13 #include <boost/hana/not_equal.hpp>
14 #include <boost/hana/greater.hpp>
50 namespace ozo::failover {
59 class on_fallback_tag;
60 class close_connection_tag;
68 template <
typename Tag>
71 template <
typename Role>
72 struct can_recover_impl {
73 static constexpr std::false_type apply(
const Role&,
const error_code&) {
74 static_assert(std::is_void_v<Role>,
"no can_recover_impl specified for a given role");
96 struct can_recover_impl<std::decay_t<decltype(master)>> {
97 static constexpr
auto value = hana::make_tuple(
104 static constexpr
auto apply(
const std::decay_t<decltype(
master)>&,
const error_code& ec) {
105 return errc::match_code(
value, ec);
125 struct can_recover_impl<std::decay_t<decltype(replica)>> {
126 static constexpr
auto value = hana::make_tuple(
132 static constexpr
auto apply(
const std::decay_t<decltype(
replica)>&,
const error_code& ec) {
133 return errc::match_code(
value, ec);
191 template <
typename Role>
193 return can_recover_impl<Role>::apply(
role, ec);
198 template <
typename Source,
typename Role,
typename = hana::when<true>>
199 struct connection_source_supports_role : std::false_type {};
201 template <
typename Source,
typename Role>
202 struct connection_source_supports_role <
204 hana::when_valid<decltype(std::declval<Source>().rebind_role(std::declval<const Role&>()))>
205 > : std::true_type {};
221 template <
typename Source>
227 static_assert(ozo::ConnectionSource<Source>,
"Source should model a ConnectionSource concept");
240 using connection_type =
typename connection_source_traits<source_type>::connection_type;
248 template <
typename OtherRole>
250 return typename detail::connection_source_supports_role<source_type, OtherRole>::type{};
259 template <
typename T>
261 : source_(std::forward<T>(source)), io_(io) {
270 template <
typename OtherRole>
272 static_assert(
is_supported(r),
"role is not supported by a connection provider");
274 return rebind_type{
ozo::unwrap(source_).rebind_role(r), io_};
283 template <
typename OtherRole>
285 static_assert(
is_supported(r),
"role is not supported by a connection provider");
287 return rebind_type{
ozo::unwrap(source_).rebind_role(r), io_};
296 template <
typename OtherRole>
298 static_assert(
is_supported(r),
"role is not supported by a connection provider");
300 return rebind_type{
ozo::unwrap(std::forward<Source>(source_)).rebind_role(r), io_};
303 template <
typename TimeConstra
int,
typename Handler>
304 void async_get_connection(TimeConstraint t,
Handler&& h)
const & {
305 static_assert(ozo::TimeConstraint<TimeConstraint>,
"should model TimeConstraint concept");
306 ozo::unwrap(source_)(io_, std::move(t), std::forward<Handler>(h));
309 template <
typename TimeConstra
int,
typename Handler>
310 void async_get_connection(TimeConstraint t,
Handler&& h) & {
311 static_assert(ozo::TimeConstraint<TimeConstraint>,
"should model TimeConstraint concept");
312 ozo::unwrap(source_)(io_, std::move(t), std::forward<Handler>(h));
315 template <
typename TimeConstra
int,
typename Handler>
316 void async_get_connection(TimeConstraint t,
Handler&& h) && {
317 static_assert(ozo::TimeConstraint<TimeConstraint>,
"should model TimeConstraint concept");
318 ozo::unwrap(std::forward<Source>(source_))(io_, std::move(t), std::forward<Handler>(h));
322 template <
typename T>
323 role_based_connection_provider(T&& source, io_context& io) -> role_based_connection_provider<T>;
334 template <
typename SourcesMap,
typename Role>
336 static_assert(decltype(hana::is_a<hana::map_tag>(std::declval<SourcesMap>()))::
value,
"SourcesMap should be a boost::hana::map");
337 static_assert(decltype(hana::size(std::declval<const SourcesMap&>())!=hana::size_c<0>)::
value,
"sources should not be empty");
342 using source_type = std::decay_t<decltype(sources_[role_])>;
347 using connection_type =
typename connection_source_traits<source_type>::connection_type;
349 template <
typename OtherRole>
350 static constexpr
bool is_supported() {
351 return decltype(hana::contains(std::declval<SourcesMap>(), std::declval<OtherRole>()))::
value;
355 : sources_(std::move(sources)), role_(std::move(
role)) {
356 static_assert(is_supported<Role>(),
"no default role found in sources");
359 template <
typename OtherRole>
360 constexpr
auto rebind_role(OtherRole r)
const & ->
362 return {sources_, r};
365 template <
typename OtherRole>
366 constexpr
auto rebind_role(OtherRole r) && ->
367 Require<is_supported<OtherRole>(), role_based_connection_source<SourcesMap, OtherRole>> {
368 return {std::move(sources_), r};
371 template <
typename TimeConstra
int,
typename Handler>
372 constexpr
void operator() (io_context& io, TimeConstraint t,
Handler&& h)
const & {
373 sources_[role_](io, std::move(t), std::forward<Handler>(h));
376 template <
typename TimeConstra
int,
typename Handler>
377 constexpr
void operator() (io_context& io, TimeConstraint t,
Handler&& h) & {
378 sources_[role_](io, std::move(t), std::forward<Handler>(h));
381 template <
typename TimeConstra
int,
typename Handler>
382 constexpr
void operator() (io_context& io, TimeConstraint t,
Handler&& h) && {
383 std::move(sources_[role_])(io, std::move(t), std::forward<Handler>(h));
386 constexpr
auto operator[] (io_context& io)
const & {
387 return role_based_connection_provider(*
this, io);
390 constexpr
auto operator[] (io_context& io) & {
391 return role_based_connection_provider(*
this, io);
394 constexpr
auto operator[] (io_context& io) && {
395 return role_based_connection_provider(std::move(*
this), io);
418 template <
typename Key,
typename Value,
typename ...Pairs>
420 auto default_role = hana::first(p1);
421 auto map = hana::make_map(std::move(p1), std::forward<Pairs>(ps)...);
425 template <
typename Options,
typename Context,
typename RoleIdx = decltype(hana::size_c<0>)>
426 class role_based_try {
429 static constexpr RoleIdx idx_{};
430 using opt = role_based_options;
439 constexpr role_based_try(Options options, Context ctx)
440 : ctx_(std::move(ctx)), options_(std::move(options)) {
441 static_assert(decltype(hana::is_a<hana::map_tag>(options))::value,
"Options should be boost::hana::map");
444 constexpr role_based_try(
const role_based_try&) =
delete;
445 constexpr role_based_try(role_based_try&&) =
default;
446 constexpr role_based_try& operator = (
const role_based_try&) =
delete;
447 constexpr role_based_try& operator = (role_based_try&&) =
default;
454 constexpr
const Options& options()
const {
return options_;}
455 constexpr Options& options() {
return options_;}
462 static constexpr
auto role_index() {
return idx_;}
469 constexpr
auto role()
const {
return roles_seq()[role_index()];}
477 auto get_context()
const {
479 hana::make_tuple(
ozo::unwrap(ctx_).provider.rebind_role(role()), time_constraint()),
489 auto time_constraint()
const {
490 return detail::get_try_time_constraint(
ozo::unwrap(ctx_).time_constraint, tries_left().value);
498 constexpr decltype(
auto) roles_seq()
const {
507 constexpr
auto tries_left()
const {
508 return decltype(hana::size(roles_seq()) - role_index()){};
521 template <
typename Connection,
typename Initiator>
525 if constexpr (decltype(tries_left() > hana::size_c<1>)::value) {
526 using fallback_try = role_based_try<Options, Context, decltype(role_index() + hana::size_c<1>)>;
527 fallback_try fallback{std::move(options_), std::move(ctx_)};
531 init(std::move(fallback));
534 fallback.initiate_next_try(ec, conn, std::move(init));
540 template <
typename Options,
typename Context,
typename RoleIdx>
541 struct initiate_next_try_impl<role_based_try<Options, Context, RoleIdx>> {
542 template <
typename Connection,
typename Initiator>
543 static void apply(role_based_try<Options, Context, RoleIdx>& a_try,
545 a_try.initiate_next_try(ec, conn, std::forward<Initiator>(init));
556 template <
typename Options = decltype(hana::make_map())>
563 template <
typename OtherOptions>
564 constexpr
static auto rebind_options(OtherOptions&&
options) {
575 static_assert(decltype(hana::is_a<hana::map_tag>(
options))::
value,
"Options should be boost::hana::map");
578 template <
typename Operation,
typename Allocator,
typename Source,
579 typename TimeConstraint,
typename ...Args>
581 role_based_connection_provider<Source> provider, TimeConstraint t, Args&& ...args)
const {
583 static_assert(decltype(this->
has(opt::roles))::
value,
"roles should be specified");
584 static_assert(!decltype(hana::is_empty(this->
get(opt::roles)))::
value,
"roles should not be empty");
586 return role_based_try {
588 basic_context{std::move(provider),
ozo::deadline(t), std::forward<Args>(args)...}
651 template <
typename ...Roles>
653 if constexpr (
sizeof...(roles) != 0) {
654 return role_based_strategy{hana::make_map(role_based_options::roles=hana::make_tuple(roles...))};
664 template <
typename ...Ts,
typename Op>
665 struct construct_initiator_impl<failover::role_based_strategy<Ts...>, Op>
666 : failover::construct_initiator_impl {};