123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- #pragma once
- #include "retry_policy.h"
- #include "utils.h"
- #include <library/cpp/retry/protos/retry_options.pb.h>
- #include <util/datetime/base.h>
- #include <util/generic/maybe.h>
- #include <util/generic/typetraits.h>
- #include <util/generic/yexception.h>
- #include <functional>
- struct TRetryOptions {
- ui32 RetryCount;
- // TotalDuration = SleepDuration +/- SleepRandomDelta + (attempt * SleepIncrement) + (2**attempt * SleepExponentialMultiplier)
- TDuration SleepDuration;
- TDuration SleepRandomDelta;
- TDuration SleepIncrement;
- TDuration SleepExponentialMultiplier;
- std::function<void(TDuration)> SleepFunction;
- TRetryOptions(ui32 retryCount = 3, TDuration sleepDuration = TDuration::Seconds(1), TDuration sleepRandomDelta = TDuration::Zero(),
- TDuration sleepIncrement = TDuration::Zero(), TDuration sleepExponentialMultiplier = TDuration::Zero(),
- std::function<void(TDuration)> sleepFunction = [](TDuration d) { Sleep(d); }) // can't use Sleep itself due to Win compilation error
- : RetryCount(retryCount)
- , SleepDuration(sleepDuration)
- , SleepRandomDelta(sleepRandomDelta)
- , SleepIncrement(sleepIncrement)
- , SleepExponentialMultiplier(sleepExponentialMultiplier)
- , SleepFunction(sleepFunction)
- {
- }
- TRetryOptions& WithCount(ui32 retryCount) {
- RetryCount = retryCount;
- return *this;
- }
- TRetryOptions& WithSleep(TDuration sleepDuration) {
- SleepDuration = sleepDuration;
- return *this;
- }
- TRetryOptions& WithRandomDelta(TDuration sleepRandomDelta) {
- SleepRandomDelta = sleepRandomDelta;
- return *this;
- }
- TRetryOptions& WithIncrement(TDuration sleepIncrement) {
- SleepIncrement = sleepIncrement;
- return *this;
- }
- TRetryOptions& WithExponentialMultiplier(TDuration sleepExponentialMultiplier) {
- SleepExponentialMultiplier = sleepExponentialMultiplier;
- return *this;
- }
- TRetryOptions& WithSleepFunction(std::function<void(TDuration)> sleepFunction) {
- SleepFunction = sleepFunction;
- return *this;
- }
- // for compatibility attempt == 0 by default
- TDuration GetTimeToSleep(ui32 attempt = 0) const {
- return SleepDuration + NRetryPrivate::AddRandomDelta(SleepRandomDelta) + NRetryPrivate::AddIncrement(attempt, SleepIncrement) + NRetryPrivate::AddExponentialMultiplier(attempt, SleepExponentialMultiplier);
- }
- static TRetryOptions Count(ui32 retryCount) {
- return TRetryOptions(retryCount);
- }
- static TRetryOptions Default() {
- return TRetryOptions();
- }
- static TRetryOptions NoRetry() {
- return TRetryOptions(0);
- }
- };
- TRetryOptions MakeRetryOptions(const NRetry::TRetryOptionsPB& retryOptions);
- namespace NRetryDetails {
- template <class TException>
- class TRetryOptionsPolicy : public IRetryPolicy<const TException&> {
- public:
- explicit TRetryOptionsPolicy(const TRetryOptions& opts)
- : Opts(opts)
- {
- }
- using IRetryState = typename IRetryPolicy<const TException&>::IRetryState;
- class TRetryState : public IRetryState {
- public:
- explicit TRetryState(const TRetryOptions& opts)
- : Opts(opts)
- {
- }
- TMaybe<TDuration> GetNextRetryDelay(const TException&) override {
- if (Attempt == Opts.RetryCount) {
- return Nothing();
- }
- return Opts.GetTimeToSleep(Attempt++);
- }
- private:
- const TRetryOptions Opts;
- size_t Attempt = 0;
- };
- typename IRetryState::TPtr CreateRetryState() const override {
- return std::make_unique<TRetryState>(Opts);
- }
- private:
- const TRetryOptions Opts;
- };
- } // namespace NRetryDetails
- template <class TException>
- typename IRetryPolicy<const TException&>::TPtr MakeRetryPolicy(const TRetryOptions& opts) {
- return std::make_shared<NRetryDetails::TRetryOptionsPolicy<TException>>(opts);
- }
- template <class TException>
- typename IRetryPolicy<const TException&>::TPtr MakeRetryPolicy(const NRetry::TRetryOptionsPB& opts) {
- return MakeRetryPolicy<TException>(MakeRetryOptions(opts));
- }
- template <typename TResult, typename TException = yexception>
- TMaybe<TResult> DoWithRetry(std::function<TResult()> func, const typename IRetryPolicy<const TException&>::TPtr& retryPolicy, bool throwLast = true, std::function<void(const TException&)> onFail = {}, std::function<void(TDuration)> sleepFunction = {}) {
- typename IRetryPolicy<const TException&>::IRetryState::TPtr retryState;
- while (true) {
- try {
- return func();
- } catch (const TException& ex) {
- if (onFail) {
- onFail(ex);
- }
- if (!retryState) {
- retryState = retryPolicy->CreateRetryState();
- }
- if (const TMaybe<TDuration> delay = retryState->GetNextRetryDelay(ex)) {
- if (*delay) {
- if (sleepFunction) {
- sleepFunction(*delay);
- } else {
- Sleep(*delay);
- }
- }
- } else {
- if (throwLast) {
- throw;
- }
- break;
- }
- }
- }
- return Nothing();
- }
- template <typename TResult, typename TException = yexception>
- TMaybe<TResult> DoWithRetry(std::function<TResult()> func, std::function<void(const TException&)> onFail, TRetryOptions retryOptions, bool throwLast = true) {
- return DoWithRetry<TResult, TException>(std::move(func), MakeRetryPolicy<TException>(retryOptions), throwLast, std::move(onFail), retryOptions.SleepFunction);
- }
- template <typename TResult, typename TException = yexception>
- TMaybe<TResult> DoWithRetry(std::function<TResult()> func, TRetryOptions retryOptions, bool throwLast = true) {
- return DoWithRetry<TResult, TException>(std::move(func), MakeRetryPolicy<TException>(retryOptions), throwLast, {}, retryOptions.SleepFunction);
- }
- template <typename TException = yexception>
- bool DoWithRetry(std::function<void()> func, const typename IRetryPolicy<const TException&>::TPtr& retryPolicy, bool throwLast = true, std::function<void(const TException&)> onFail = {}, std::function<void(TDuration)> sleepFunction = {}) {
- auto f = [&]() {
- func();
- return nullptr;
- };
- return DoWithRetry<void*, TException>(f, retryPolicy, throwLast, std::move(onFail), std::move(sleepFunction)).Defined();
- }
- template <typename TException = yexception>
- bool DoWithRetry(std::function<void()> func, std::function<void(const TException&)> onFail, TRetryOptions retryOptions, bool throwLast) {
- return DoWithRetry<TException>(std::move(func), MakeRetryPolicy<TException>(retryOptions), throwLast, onFail, retryOptions.SleepFunction);
- }
- template <typename TException = yexception>
- bool DoWithRetry(std::function<void()> func, TRetryOptions retryOptions, bool throwLast = true) {
- return DoWithRetry<TException>(std::move(func), MakeRetryPolicy<TException>(retryOptions), throwLast, {}, retryOptions.SleepFunction);
- }
- template <class TRetCode>
- TRetCode DoWithRetryOnRetCode(std::function<TRetCode()> func, const typename IRetryPolicy<TRetCode>::TPtr& retryPolicy, std::function<void(TDuration)> sleepFunction = {}) {
- auto retryState = retryPolicy->CreateRetryState();
- while (true) {
- TRetCode code = func();
- if (const TMaybe<TDuration> delay = retryState->GetNextRetryDelay(code)) {
- if (*delay) {
- if (sleepFunction) {
- sleepFunction(*delay);
- } else {
- Sleep(*delay);
- }
- }
- } else {
- return code;
- }
- }
- }
- bool DoWithRetryOnRetCode(std::function<bool()> func, TRetryOptions retryOptions);
- Y_DECLARE_PODTYPE(TRetryOptions);
|