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
70 Status ValidateConfig() const;
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::kNoRetry || sizeof...(Kinds) > 0,
98 "RetryPolicy 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
119template <ErrorKind... Kinds>
120using StopRetryOn = RetryPolicy<RetryPolicyMode::kStopRetryOn, Kinds...>;
121
122template <typename T>
123inline constexpr bool kIsRetryPolicy = false;
124
125template <RetryPolicyMode Mode, ErrorKind... Kinds>
126inline constexpr bool kIsRetryPolicy<RetryPolicy<Mode, Kinds...>> = true;
127
128template <typename T>
129concept Policy = kIsRetryPolicy<std::remove_cvref_t<T>>;
130
131} // namespace retry
132
137template <retry::Policy RetryPolicy>
139 public:
141 explicit RetryRunner(RetryConfig config = {})
142 : detail::RetryRunnerBase(std::move(config)) {}
143
149 template <detail::RetryTask F>
150 auto Run(F&& task, int32_t* attempt_counter = nullptr)
151 -> std::remove_cvref_t<std::invoke_result_t<F&>> {
152 ICEBERG_RETURN_UNEXPECTED(ValidatePolicyConfig());
153
154 const auto deadline = this->ComputeDeadline();
155 int32_t attempt = 0;
156 const int32_t max_attempts = this->config_.num_retries + 1;
157
158 while (true) {
159 ++attempt;
160 if (attempt_counter != nullptr) {
161 *attempt_counter = attempt;
162 }
163
164 auto result = std::invoke(task);
165 if (result.has_value()) {
166 return result;
167 }
168
169 if (!CanRetry(result.error().kind, attempt, max_attempts, deadline)) {
170 return result;
171 }
172
173 if (!this->WaitForNextAttempt(attempt, deadline)) {
174 return result;
175 }
176 }
177 }
178
179 private:
180 using TimePoint = detail::RetryRunnerBase::TimePoint;
181
182 Status ValidatePolicyConfig() const {
183 auto validation = this->ValidateConfig();
184 if (!validation.has_value()) {
185 return validation;
186 }
187 if (this->config_.num_retries > 0 && !RetryPolicy::kEnabled) {
188 return InvalidArgument("Retry policy must be enabled when num_retries > 0");
189 }
190 return {};
191 }
192
193 bool CanRetry(ErrorKind kind, int32_t attempt, int32_t max_attempts,
194 const std::optional<TimePoint>& deadline) const {
195 return attempt < max_attempts && !this->HasTimedOut(deadline) &&
196 RetryPolicy::ShouldRetry(kind);
197 }
198};
199
201ICEBERG_EXPORT inline auto MakeCommitRetryRunner(int32_t num_retries, int32_t min_wait_ms,
202 int32_t max_wait_ms,
203 int32_t total_timeout_ms) {
204 return RetryRunner<retry::OnlyRetryOn<ErrorKind::kCommitFailed,
205 ErrorKind::kRetryableValidationFailed>>(
206 RetryConfig{.num_retries = num_retries,
207 .min_wait_ms = min_wait_ms,
208 .max_wait_ms = max_wait_ms,
209 .total_timeout_ms = total_timeout_ms});
210}
211
212} // namespace iceberg
Utility class for running tasks with retry logic.
Definition retry_util.h:138
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:150
RetryRunner(RetryConfig config={})
Construct a RetryRunner with the given configuration.
Definition retry_util.h:141
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 result.h:143
Definition retry_util.h:41
Definition retry_util.h:129
Define symbol visibility macros for core Iceberg APIs.
Define common Iceberg utility macros.
Core Apache Iceberg C++ APIs.
Definition arrow_io_util.h:33
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:201
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