summaryrefslogtreecommitdiff
path: root/src/buildtool/common/remote/retry.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/buildtool/common/remote/retry.hpp')
-rw-r--r--src/buildtool/common/remote/retry.hpp130
1 files changed, 22 insertions, 108 deletions
diff --git a/src/buildtool/common/remote/retry.hpp b/src/buildtool/common/remote/retry.hpp
index 6e164af1..c0ce318b 100644
--- a/src/buildtool/common/remote/retry.hpp
+++ b/src/buildtool/common/remote/retry.hpp
@@ -15,13 +15,14 @@
#ifndef INCLUDED_SRC_BUILDTOOL_COMMON_RETRY_HPP
#define INCLUDED_SRC_BUILDTOOL_COMMON_RETRY_HPP
+#ifndef BOOTSTRAP_BUILD_TOOL
+
+#include <functional>
#include <optional>
-#include <thread>
-#include <utility> // std::move
+#include <string>
+#include <utility>
#include "grpcpp/grpcpp.h"
-#include "src/buildtool/common/remote/retry_config.hpp"
-#include "src/buildtool/logging/log_level.hpp"
#include "src/buildtool/logging/logger.hpp"
// Utility class to help detecting when exit the retry loop. This class can be
@@ -36,117 +37,30 @@
// caller.
struct RetryResponse {
// When set to true, it means the function successfully run
- bool ok{false};
+ bool ok = false;
// When set to true, it means that it is not worthy to retry.
- bool exit_retry_loop{false};
+ bool exit_retry_loop = false;
// error message logged when exit_retry_loop was set to true or when the
// last retry attempt failed
- std::optional<std::string> error_msg{std::nullopt};
+ std::optional<std::string> error_msg = std::nullopt;
};
-template <typename F>
-concept CallableReturningRetryResponse = requires(F const& f) {
- {RetryResponse{f()}};
-};
+using CallableReturningRetryResponse = std::function<RetryResponse(void)>;
-template <CallableReturningRetryResponse F>
-// \p f is the callable invoked with a back off algorithm. The retry loop is
-// interrupted when one of the two member of the returned RetryResponse object
-// is set to true.
-[[nodiscard]] auto WithRetry(F const& f, Logger const& logger) noexcept
- -> bool {
- try {
- auto const& attempts = RetryConfig::GetMaxAttempts();
- for (auto attempt = 1U; attempt <= attempts; ++attempt) {
- auto [ok, fatal, error_msg] = f();
- if (ok) {
- return true;
- }
- if (fatal) {
- if (error_msg) {
- logger.Emit(LogLevel::Error, "{}", *error_msg);
- }
- return false;
- }
- // don't wait if it was the last attempt
- if (attempt < attempts) {
- auto const sleep_for_seconds =
- RetryConfig::GetSleepTimeSeconds(attempt);
- logger.Emit(kRetryLogLevel,
- "Attempt {}/{} failed{} Retrying in {} seconds.",
- attempt,
- attempts,
- error_msg ? fmt::format(": {}", *error_msg) : ".",
- sleep_for_seconds);
- std::this_thread::sleep_for(
- std::chrono::seconds(sleep_for_seconds));
- }
- else {
- if (error_msg) {
- logger.Emit(LogLevel::Error,
- "After {} attempts: {}",
- attempt,
- *error_msg);
- }
- }
- }
- } catch (...) {
- logger.Emit(LogLevel::Error, "WithRetry: caught unknown exception");
- }
- return false;
-}
+/// \brief Calls a function with a retry strategy using a backoff algorithm.
+/// Retry loop interrupts when one of the two members of the function's returned
+/// RetryResponse object is set to true.
+[[nodiscard]] auto WithRetry(CallableReturningRetryResponse const& f,
+ Logger const& logger) noexcept -> bool;
-template <typename F>
-concept CallableReturningGrpcStatus = requires(F const& f) {
- {grpc::Status{f()}};
-};
+using CallableReturningGrpcStatus = std::function<grpc::Status(void)>;
-template <CallableReturningGrpcStatus F>
-// F is the function to be invoked with a back off algorithm
-[[nodiscard]] auto WithRetry(F const& f, Logger const& logger) noexcept
- -> std::pair<bool, grpc::Status> {
- grpc::Status status{};
- try {
- auto attempts = RetryConfig::GetMaxAttempts();
- for (auto attempt = 1U; attempt <= attempts; ++attempt) {
- status = f();
- if (status.ok() or
- status.error_code() != grpc::StatusCode::UNAVAILABLE) {
- return {status.ok(), std::move(status)};
- }
- // don't wait if it was the last attempt
- if (attempt < attempts) {
- auto const sleep_for_seconds =
- RetryConfig::GetSleepTimeSeconds(attempt);
- logger.Emit(
- kRetryLogLevel,
- "Attempt {}/{} failed: {}: {}: Retrying in {} seconds.",
- attempt,
- attempts,
- static_cast<int>(status.error_code()),
- status.error_message(),
- sleep_for_seconds);
- std::this_thread::sleep_for(
- std::chrono::seconds(sleep_for_seconds));
- }
- else {
- // The caller performs a second check on the
- // status.error_code(), and, eventually, emits to Error level
- // there.
- //
- // To avoid duplication of similar errors, we emit to Debug
- // level.
- logger.Emit(LogLevel::Debug,
- "After {} attempts: {}: {}",
- attempt,
- static_cast<int>(status.error_code()),
- status.error_message());
- }
- }
- } catch (...) {
- logger.Emit(LogLevel::Error, "WithRetry: caught unknown exception");
- }
- return {false, std::move(status)};
-}
+/// \brief Calls a function with a retry strategy using a backoff algorithm.
+/// Retry loop interrupts when function returns an error code different from
+/// UNAVAILABLE.
+[[nodiscard]] auto WithRetry(CallableReturningGrpcStatus const& f,
+ Logger const& logger) noexcept
+ -> std::pair<bool, grpc::Status>;
+#endif // BOOTSTRAP_BUILD_TOOL
#endif // INCLUDED_SRC_BUILDTOOL_COMMON_RETRY_HPP