|
@@ -1,5 +1,5 @@
|
|
|
#pragma once
|
|
|
-
|
|
|
+#include "retry_policy.h"
|
|
|
#include "utils.h"
|
|
|
|
|
|
#include <library/cpp/retry/protos/retry_options.pb.h>
|
|
@@ -81,20 +81,88 @@ struct TRetryOptions {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+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, std::function<void(const TException&)> onFail, TRetryOptions retryOptions, bool throwLast) {
|
|
|
- for (ui32 attempt = 0; attempt <= retryOptions.RetryCount; ++attempt) {
|
|
|
+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 (TException& ex) {
|
|
|
- onFail(ex);
|
|
|
- if (attempt == retryOptions.RetryCount) {
|
|
|
+ } 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;
|
|
|
}
|
|
|
- } else {
|
|
|
- auto sleep = retryOptions.SleepFunction;
|
|
|
- sleep(retryOptions.GetTimeToSleep(attempt));
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -102,32 +170,53 @@ TMaybe<TResult> DoWithRetry(std::function<TResult()> func, std::function<void(co
|
|
|
}
|
|
|
|
|
|
template <typename TResult, typename TException = yexception>
|
|
|
-TMaybe<TResult> DoWithRetry(std::function<TResult()> func, TRetryOptions retryOptions, bool throwLast) {
|
|
|
- return DoWithRetry<TResult, TException>(func, [](const TException&){}, retryOptions, throwLast);
|
|
|
+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, std::function<void(const TException&)> onFail, TRetryOptions retryOptions, bool throwLast) {
|
|
|
+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, onFail, retryOptions, throwLast).Defined();
|
|
|
+ return DoWithRetry<void*, TException>(f, retryPolicy, throwLast, std::move(onFail), std::move(sleepFunction)).Defined();
|
|
|
}
|
|
|
|
|
|
template <typename TException = yexception>
|
|
|
-bool DoWithRetry(std::function<void()> func, TRetryOptions retryOptions, bool throwLast) {
|
|
|
- auto f = [&]() {
|
|
|
- func();
|
|
|
- return nullptr;
|
|
|
- };
|
|
|
- return DoWithRetry<void*, TException>(f, [](const TException&){}, retryOptions, throwLast).Defined();
|
|
|
+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);
|
|
|
}
|
|
|
|
|
|
-void DoWithRetry(std::function<void()> func, TRetryOptions retryOptions);
|
|
|
+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);
|
|
|
-
|
|
|
-TRetryOptions MakeRetryOptions(const NRetry::TRetryOptionsPB& retryOptions);
|