iceberg-cpp
Loading...
Searching...
No Matches
retry_util.h
Go to the documentation of this file.
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20#pragma once
21
24
25#include <chrono>
26#include <cstdint>
27#include <functional>
28#include <optional>
29#include <type_traits>
30#include <utility>
31
33#include "iceberg/result.h"
34#include "iceberg/util/macros.h"
35
36namespace iceberg {
37
38namespace detail {
39
40template <typename F>
42
43} // namespace detail
44
46struct ICEBERG_EXPORT RetryConfig {
48 int32_t num_retries = 4;
50 int32_t min_wait_ms = 100;
52 int32_t max_wait_ms = 60 * 1000; // 1 minute
54 int32_t total_timeout_ms = 30 * 60 * 1000; // 30 minutes
56 double scale_factor = 2.0;
57};
58
59namespace detail {
60
61class ICEBERG_EXPORT RetryRunnerBase {
62 protected:
63 explicit RetryRunnerBase(RetryConfig config) : config_(std::move(config)) {}
64
65 using Clock = std::chrono::steady_clock;
66 using Duration = std::chrono::milliseconds;
67 using TimePoint = Clock::time_point;
68
71 std::optional<TimePoint> ComputeDeadline() const;
72 bool HasTimedOut(const std::optional<TimePoint>& deadline) const;
73 std::optional<Duration> RetryDelayWithinBudget(
74 int32_t attempt, const std::optional<TimePoint>& deadline) const;
75 bool WaitForNextAttempt(int32_t attempt,
76 const std::optional<TimePoint>& deadline) const;
78 int32_t CalculateDelay(int32_t attempt) const;
79
80 RetryConfig config_;
81};
82
83} // namespace detail
84
85namespace retry {
86
87enum class RetryPolicyMode {
88 kNoRetry,
89 kOnlyRetryOn,
90 kStopRetryOn,
91};
92
93template <RetryPolicyMode Mode, ErrorKind... Kinds>
95 static_assert(Mode != RetryPolicyMode::kNoRetry || sizeof...(Kinds) == 0,
96 "NoRetry must not include error kinds");
97 static_assert(Mode != RetryPolicyMode::kOnlyRetryOn || sizeof...(Kinds) > 0,
98 "OnlyRetryOn must include at least one error kind");
99
100 static constexpr RetryPolicyMode kMode = Mode;
101 static constexpr bool kEnabled = Mode != RetryPolicyMode::kNoRetry;
102
103 static constexpr bool ShouldRetry(ErrorKind kind) {
104 if constexpr (Mode == RetryPolicyMode::kNoRetry) {
105 return false;
106 } else if constexpr (Mode == RetryPolicyMode::kOnlyRetryOn) {
107 return ((kind == Kinds) || ...);
108 } else {
109 return !((kind == Kinds) || ...);
110 }
111 }
112};
113
115
116template <ErrorKind... Kinds>
117using OnlyRetryOn = RetryPolicy<RetryPolicyMode::kOnlyRetryOn, Kinds...>;
118
120template <ErrorKind... Kinds>
121using StopRetryOn = RetryPolicy<RetryPolicyMode::kStopRetryOn, Kinds...>;
122
123template <typename T>
124inline constexpr bool kIsRetryPolicy = false;
125
126template <RetryPolicyMode Mode, ErrorKind... Kinds>
127inline constexpr bool kIsRetryPolicy<RetryPolicy<Mode, Kinds...>> = true;
128
129template <typename T>
130concept Policy = kIsRetryPolicy<std::remove_cvref_t<T>>;
131
132} // namespace retry
133
138template <retry::Policy RetryPolicy>
140 public:
142 explicit RetryRunner(RetryConfig config = {})
143 : detail::RetryRunnerBase(std::move(config)) {}
144
150 template <detail::RetryTask F>
151 auto Run(F&& task, int32_t* attempt_counter = nullptr)
152 -> std::remove_cvref_t<std::invoke_result_t<F&>> {
153 ICEBERG_RETURN_UNEXPECTED(ValidatePolicyConfig());
154
155 const auto deadline = this->ComputeDeadline();
156 int32_t attempt = 0;
157 const int32_t max_attempts = this->config_.num_retries + 1;
158
159 while (true) {
160 ++attempt;
161 if (attempt_counter != nullptr) {
162 *attempt_counter = attempt;
163 }
164
165 auto result = std::invoke(task);
166 if (result.has_value()) {
167 return result;
168 }
169
170 if (!CanRetry(result.error().kind, attempt, max_attempts, deadline)) {
171 return result;
172 }
173
174 if (!this->WaitForNextAttempt(attempt, deadline)) {
175 return result;
176 }
177 }
178 }
179
180 private:
181 using TimePoint = detail::RetryRunnerBase::TimePoint;
182
183 Status ValidatePolicyConfig() const {
184 auto validation = this->ValidateConfig();
185 if (!validation.has_value()) {
186 return validation;
187 }
188 if (this->config_.num_retries > 0 && !RetryPolicy::kEnabled) {
189 return InvalidArgument("Retry policy must be enabled when num_retries > 0");
190 }
191 return {};
192 }
193
194 bool CanRetry(ErrorKind kind, int32_t attempt, int32_t max_attempts,
195 const std::optional<TimePoint>& deadline) const {
196 return attempt < max_attempts && !this->HasTimedOut(deadline) &&
197 RetryPolicy::ShouldRetry(kind);
198 }
199};
200
202ICEBERG_EXPORT inline auto MakeCommitRetryRunner(int32_t num_retries, int32_t min_wait_ms,
203 int32_t max_wait_ms,
204 int32_t total_timeout_ms) {
205 return RetryRunner<retry::OnlyRetryOn<ErrorKind::kCommitFailed,
206 ErrorKind::kRetryableValidationFailed>>(
207 RetryConfig{.num_retries = num_retries,
208 .min_wait_ms = min_wait_ms,
209 .max_wait_ms = max_wait_ms,
210 .total_timeout_ms = total_timeout_ms});
211}
212
213} // namespace iceberg
Utility class for running tasks with retry logic.
Definition retry_util.h:139
auto Run(F &&task, int32_t *attempt_counter=nullptr) -> std::remove_cvref_t< std::invoke_result_t< F & > >
Run a task that returns a Result<T>
Definition retry_util.h:151
RetryRunner(RetryConfig config={})
Construct a RetryRunner with the given configuration.
Definition retry_util.h:142
Definition retry_util.h:61
Status ValidateConfig() const
Validate retry counts and timing bounds.
int32_t CalculateDelay(int32_t attempt) const
Calculate delay with exponential backoff and jitter.
Definition expected.h:1199
Definition result.h:145
Definition retry_util.h:41
Definition retry_util.h:130
Define symbol visibility macros for core Iceberg APIs.
Define common Iceberg utility macros.
Core Apache Iceberg C++ APIs.
Definition arrow_io_util.h:34
ICEBERG_EXPORT auto MakeCommitRetryRunner(int32_t num_retries, int32_t min_wait_ms, int32_t max_wait_ms, int32_t total_timeout_ms)
Helper function to create a RetryRunner with table commit configuration.
Definition retry_util.h:202
ErrorKind
Error types for iceberg.
Definition result.h:36
STL namespace.
Define Result, Status, and error helpers.
Configuration for retry behavior.
Definition retry_util.h:46
int32_t num_retries
Maximum number of retry attempts (not including the first attempt)
Definition retry_util.h:48
Definition retry_util.h:94