#pragma once #include "retry_policy.h" #include "utils.h" #include #include #include #include #include #include struct TRetryOptions { ui32 RetryCount; // TotalDuration = SleepDuration +/- SleepRandomDelta + (attempt * SleepIncrement) + (2**attempt * SleepExponentialMultiplier) TDuration SleepDuration; TDuration SleepRandomDelta; TDuration SleepIncrement; TDuration SleepExponentialMultiplier; std::function SleepFunction; TRetryOptions(ui32 retryCount = 3, TDuration sleepDuration = TDuration::Seconds(1), TDuration sleepRandomDelta = TDuration::Zero(), TDuration sleepIncrement = TDuration::Zero(), TDuration sleepExponentialMultiplier = TDuration::Zero(), std::function 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 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 TRetryOptionsPolicy : public IRetryPolicy { public: explicit TRetryOptionsPolicy(const TRetryOptions& opts) : Opts(opts) { } using IRetryState = typename IRetryPolicy::IRetryState; class TRetryState : public IRetryState { public: explicit TRetryState(const TRetryOptions& opts) : Opts(opts) { } TMaybe 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(Opts); } private: const TRetryOptions Opts; }; } // namespace NRetryDetails template typename IRetryPolicy::TPtr MakeRetryPolicy(const TRetryOptions& opts) { return std::make_shared>(opts); } template typename IRetryPolicy::TPtr MakeRetryPolicy(const NRetry::TRetryOptionsPB& opts) { return MakeRetryPolicy(MakeRetryOptions(opts)); } template TMaybe DoWithRetry(std::function func, const typename IRetryPolicy::TPtr& retryPolicy, bool throwLast = true, std::function onFail = {}, std::function sleepFunction = {}) { typename IRetryPolicy::IRetryState::TPtr retryState; while (true) { try { return func(); } catch (const TException& ex) { if (onFail) { onFail(ex); } if (!retryState) { retryState = retryPolicy->CreateRetryState(); } if (const TMaybe delay = retryState->GetNextRetryDelay(ex)) { if (*delay) { if (sleepFunction) { sleepFunction(*delay); } else { Sleep(*delay); } } } else { if (throwLast) { throw; } break; } } } return Nothing(); } template TMaybe DoWithRetry(std::function func, std::function onFail, TRetryOptions retryOptions, bool throwLast = true) { return DoWithRetry(std::move(func), MakeRetryPolicy(retryOptions), throwLast, std::move(onFail), retryOptions.SleepFunction); } template TMaybe DoWithRetry(std::function func, TRetryOptions retryOptions, bool throwLast = true) { return DoWithRetry(std::move(func), MakeRetryPolicy(retryOptions), throwLast, {}, retryOptions.SleepFunction); } template bool DoWithRetry(std::function func, const typename IRetryPolicy::TPtr& retryPolicy, bool throwLast = true, std::function onFail = {}, std::function sleepFunction = {}) { auto f = [&]() { func(); return nullptr; }; return DoWithRetry(f, retryPolicy, throwLast, std::move(onFail), std::move(sleepFunction)).Defined(); } template bool DoWithRetry(std::function func, std::function onFail, TRetryOptions retryOptions, bool throwLast) { return DoWithRetry(std::move(func), MakeRetryPolicy(retryOptions), throwLast, onFail, retryOptions.SleepFunction); } template bool DoWithRetry(std::function func, TRetryOptions retryOptions, bool throwLast = true) { return DoWithRetry(std::move(func), MakeRetryPolicy(retryOptions), throwLast, {}, retryOptions.SleepFunction); } template TRetCode DoWithRetryOnRetCode(std::function func, const typename IRetryPolicy::TPtr& retryPolicy, std::function sleepFunction = {}) { auto retryState = retryPolicy->CreateRetryState(); while (true) { TRetCode code = func(); if (const TMaybe delay = retryState->GetNextRetryDelay(code)) { if (*delay) { if (sleepFunction) { sleepFunction(*delay); } else { Sleep(*delay); } } } else { return code; } } } bool DoWithRetryOnRetCode(std::function func, TRetryOptions retryOptions); Y_DECLARE_PODTYPE(TRetryOptions);