retry_service_config.cc
Go to the documentation of this file.
1 //
2 // Copyright 2018 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
18 
20 
21 #include <stdio.h>
22 #include <string.h>
23 
24 #include <algorithm>
25 #include <map>
26 #include <string>
27 #include <utility>
28 #include <vector>
29 
30 #include "absl/memory/memory.h"
31 #include "absl/types/optional.h"
32 
33 #include <grpc/status.h>
34 #include <grpc/support/log.h>
35 
41 
42 // As per the retry design, we do not allow more than 5 retry attempts.
43 #define MAX_MAX_RETRY_ATTEMPTS 5
44 
45 namespace grpc_core {
46 namespace internal {
47 
50  parser_name());
51 }
52 
54  builder->service_config_parser()->RegisterParser(
55  absl::make_unique<RetryServiceConfigParser>());
56 }
57 
58 namespace {
59 
60 grpc_error_handle ParseRetryThrottling(const Json& json,
61  intptr_t* max_milli_tokens,
62  intptr_t* milli_token_ratio) {
63  if (json.type() != Json::Type::OBJECT) {
65  "field:retryThrottling error:Type should be object");
66  }
67  std::vector<grpc_error_handle> error_list;
68  // Parse maxTokens.
69  auto it = json.object_value().find("maxTokens");
70  if (it == json.object_value().end()) {
71  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
72  "field:retryThrottling field:maxTokens error:Not found"));
73  } else if (it->second.type() != Json::Type::NUMBER) {
74  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
75  "field:retryThrottling field:maxTokens error:Type should be "
76  "number"));
77  } else {
78  *max_milli_tokens =
79  gpr_parse_nonnegative_int(it->second.string_value().c_str()) * 1000;
80  if (*max_milli_tokens <= 0) {
81  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
82  "field:retryThrottling field:maxTokens error:should be "
83  "greater than zero"));
84  }
85  }
86  // Parse tokenRatio.
87  it = json.object_value().find("tokenRatio");
88  if (it == json.object_value().end()) {
89  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
90  "field:retryThrottling field:tokenRatio error:Not found"));
91  } else if (it->second.type() != Json::Type::NUMBER) {
92  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
93  "field:retryThrottling field:tokenRatio error:type should be "
94  "number"));
95  } else {
96  // We support up to 3 decimal digits.
97  size_t whole_len = it->second.string_value().size();
98  const char* value = it->second.string_value().c_str();
99  uint32_t multiplier = 1;
100  uint32_t decimal_value = 0;
101  const char* decimal_point = strchr(value, '.');
102  if (decimal_point != nullptr) {
103  whole_len = static_cast<size_t>(decimal_point - value);
104  multiplier = 1000;
105  size_t decimal_len = strlen(decimal_point + 1);
106  if (decimal_len > 3) decimal_len = 3;
107  if (!gpr_parse_bytes_to_uint32(decimal_point + 1, decimal_len,
108  &decimal_value)) {
109  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
110  "field:retryThrottling field:tokenRatio error:Failed "
111  "parsing"));
112  return GRPC_ERROR_CREATE_FROM_VECTOR("retryThrottling", &error_list);
113  }
114  uint32_t decimal_multiplier = 1;
115  for (size_t i = 0; i < (3 - decimal_len); ++i) {
116  decimal_multiplier *= 10;
117  }
118  decimal_value *= decimal_multiplier;
119  }
120  uint32_t whole_value;
121  if (!gpr_parse_bytes_to_uint32(value, whole_len, &whole_value)) {
122  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
123  "field:retryThrottling field:tokenRatio error:Failed "
124  "parsing"));
125  return GRPC_ERROR_CREATE_FROM_VECTOR("retryThrottling", &error_list);
126  }
127  *milli_token_ratio =
128  static_cast<int>((whole_value * multiplier) + decimal_value);
129  if (*milli_token_ratio <= 0) {
130  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
131  "field:retryThrottling field:tokenRatio error:value should "
132  "be greater than 0"));
133  }
134  }
135  return GRPC_ERROR_CREATE_FROM_VECTOR("retryThrottling", &error_list);
136 }
137 
138 } // namespace
139 
140 std::unique_ptr<ServiceConfigParser::ParsedConfig>
142  const Json& json,
145  auto it = json.object_value().find("retryThrottling");
146  if (it == json.object_value().end()) return nullptr;
147  intptr_t max_milli_tokens = 0;
148  intptr_t milli_token_ratio = 0;
149  *error =
150  ParseRetryThrottling(it->second, &max_milli_tokens, &milli_token_ratio);
151  if (!GRPC_ERROR_IS_NONE(*error)) return nullptr;
152  return absl::make_unique<RetryGlobalConfig>(max_milli_tokens,
153  milli_token_ratio);
154 }
155 
156 namespace {
157 
158 grpc_error_handle ParseRetryPolicy(
159  const grpc_channel_args* args, const Json& json, int* max_attempts,
160  Duration* initial_backoff, Duration* max_backoff, float* backoff_multiplier,
161  StatusCodeSet* retryable_status_codes,
162  absl::optional<Duration>* per_attempt_recv_timeout) {
163  if (json.type() != Json::Type::OBJECT) {
165  "field:retryPolicy error:should be of type object");
166  }
167  std::vector<grpc_error_handle> error_list;
168  // Parse maxAttempts.
169  auto it = json.object_value().find("maxAttempts");
170  if (it == json.object_value().end()) {
171  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
172  "field:maxAttempts error:required field missing"));
173  } else {
174  if (it->second.type() != Json::Type::NUMBER) {
175  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
176  "field:maxAttempts error:should be of type number"));
177  } else {
178  *max_attempts =
179  gpr_parse_nonnegative_int(it->second.string_value().c_str());
180  if (*max_attempts <= 1) {
181  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
182  "field:maxAttempts error:should be at least 2"));
183  } else if (*max_attempts > MAX_MAX_RETRY_ATTEMPTS) {
185  "service config: clamped retryPolicy.maxAttempts at %d",
187  *max_attempts = MAX_MAX_RETRY_ATTEMPTS;
188  }
189  }
190  }
191  // Parse initialBackoff.
192  if (ParseJsonObjectFieldAsDuration(json.object_value(), "initialBackoff",
193  initial_backoff, &error_list) &&
194  *initial_backoff == Duration::Zero()) {
195  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
196  "field:initialBackoff error:must be greater than 0"));
197  }
198  // Parse maxBackoff.
199  if (ParseJsonObjectFieldAsDuration(json.object_value(), "maxBackoff",
200  max_backoff, &error_list) &&
201  *max_backoff == Duration::Zero()) {
202  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
203  "field:maxBackoff error:must be greater than 0"));
204  }
205  // Parse backoffMultiplier.
206  it = json.object_value().find("backoffMultiplier");
207  if (it == json.object_value().end()) {
208  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
209  "field:backoffMultiplier error:required field missing"));
210  } else {
211  if (it->second.type() != Json::Type::NUMBER) {
212  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
213  "field:backoffMultiplier error:should be of type number"));
214  } else {
215  if (sscanf(it->second.string_value().c_str(), "%f", backoff_multiplier) !=
216  1) {
217  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
218  "field:backoffMultiplier error:failed to parse"));
219  } else if (*backoff_multiplier <= 0) {
220  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
221  "field:backoffMultiplier error:must be greater than 0"));
222  }
223  }
224  }
225  // Parse retryableStatusCodes.
226  it = json.object_value().find("retryableStatusCodes");
227  if (it != json.object_value().end()) {
228  if (it->second.type() != Json::Type::ARRAY) {
229  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
230  "field:retryableStatusCodes error:must be of type array"));
231  } else {
232  for (const Json& element : it->second.array_value()) {
233  if (element.type() != Json::Type::STRING) {
234  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
235  "field:retryableStatusCodes error:status codes should be of type "
236  "string"));
237  continue;
238  }
240  if (!grpc_status_code_from_string(element.string_value().c_str(),
241  &status)) {
242  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
243  "field:retryableStatusCodes error:failed to parse status code"));
244  continue;
245  }
246  retryable_status_codes->Add(status);
247  }
248  }
249  }
250  // Parse perAttemptRecvTimeout.
252  false)) {
253  it = json.object_value().find("perAttemptRecvTimeout");
254  if (it != json.object_value().end()) {
255  Duration per_attempt_recv_timeout_value;
256  if (!ParseDurationFromJson(it->second, &per_attempt_recv_timeout_value)) {
257  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
258  "field:perAttemptRecvTimeout error:type must be STRING of the "
259  "form given by google.proto.Duration."));
260  } else {
261  *per_attempt_recv_timeout = per_attempt_recv_timeout_value;
262  // TODO(roth): As part of implementing hedging, relax this check such
263  // that we allow a value of 0 if a hedging policy is specified.
264  if (per_attempt_recv_timeout_value == Duration::Zero()) {
265  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
266  "field:perAttemptRecvTimeout error:must be greater than 0"));
267  }
268  }
269  } else if (retryable_status_codes->Empty()) {
270  // If perAttemptRecvTimeout not present, retryableStatusCodes must be
271  // non-empty.
272  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
273  "field:retryableStatusCodes error:must be non-empty if "
274  "perAttemptRecvTimeout not present"));
275  }
276  } else {
277  // Hedging not enabled, so the error message for
278  // retryableStatusCodes unset should be different.
279  if (retryable_status_codes->Empty()) {
280  error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
281  "field:retryableStatusCodes error:must be non-empty"));
282  }
283  }
284  return GRPC_ERROR_CREATE_FROM_VECTOR("retryPolicy", &error_list);
285 }
286 
287 } // namespace
288 
289 std::unique_ptr<ServiceConfigParser::ParsedConfig>
291  const Json& json,
294  // Parse retry policy.
295  auto it = json.object_value().find("retryPolicy");
296  if (it == json.object_value().end()) return nullptr;
297  int max_attempts = 0;
298  Duration initial_backoff;
299  Duration max_backoff;
300  float backoff_multiplier = 0;
301  StatusCodeSet retryable_status_codes;
302  absl::optional<Duration> per_attempt_recv_timeout;
303  *error = ParseRetryPolicy(args, it->second, &max_attempts, &initial_backoff,
304  &max_backoff, &backoff_multiplier,
305  &retryable_status_codes, &per_attempt_recv_timeout);
306  if (!GRPC_ERROR_IS_NONE(*error)) return nullptr;
307  return absl::make_unique<RetryMethodConfig>(
308  max_attempts, initial_backoff, max_backoff, backoff_multiplier,
309  retryable_status_codes, per_attempt_recv_timeout);
310 }
311 
312 } // namespace internal
313 } // namespace grpc_core
regen-readme.it
it
Definition: regen-readme.py:15
log.h
core_configuration.h
element
static std::function< Slot &(Slot *)> element
Definition: abseil-cpp/absl/container/internal/hash_policy_traits_test.cc:44
grpc_core::internal::StatusCodeSet::Add
void Add(grpc_status_code status)
Definition: status_util.h:47
grpc_core::Json::type
Type type() const
Definition: src/core/lib/json/json.h:174
GPR_DEBUG_ASSERT
#define GPR_DEBUG_ASSERT(x)
Definition: include/grpc/impl/codegen/log.h:103
grpc_core::internal::RetryServiceConfigParser::ParsePerMethodParams
std::unique_ptr< ServiceConfigParser::ParsedConfig > ParsePerMethodParams(const grpc_channel_args *args, const Json &json, grpc_error_handle *error) override
Definition: retry_service_config.cc:290
grpc_core::Json::Type::OBJECT
@ OBJECT
grpc_core::CoreConfiguration::service_config_parser
const ServiceConfigParser & service_config_parser() const
Definition: core_configuration.h:153
grpc_core
Definition: call_metric_recorder.h:31
grpc_core::internal::RetryServiceConfigParser::parser_name
static absl::string_view parser_name()
Definition: retry_service_config.h:105
grpc_core::CoreConfiguration::Builder
Definition: core_configuration.h:41
status_util.h
string.h
grpc_core::internal::RetryServiceConfigParser::ParseGlobalParams
std::unique_ptr< ServiceConfigParser::ParsedConfig > ParseGlobalParams(const grpc_channel_args *, const Json &json, grpc_error_handle *error) override
Definition: retry_service_config.cc:141
error
grpc_error_handle error
Definition: retry_filter.cc:499
grpc_status_code
grpc_status_code
Definition: include/grpc/impl/codegen/status.h:28
grpc_core::Json::object_value
const Object & object_value() const
Definition: src/core/lib/json/json.h:177
status
absl::Status status
Definition: rls.cc:251
grpc_channel_args_find_bool
bool grpc_channel_args_find_bool(const grpc_channel_args *args, const char *name, bool default_value)
Definition: channel_args.cc:465
grpc_channel_args
Definition: grpc_types.h:132
GRPC_ERROR_CREATE_FROM_VECTOR
#define GRPC_ERROR_CREATE_FROM_VECTOR(desc, error_list)
Definition: error.h:314
status.h
uint32_t
unsigned int uint32_t
Definition: stdint-msvc2008.h:80
profile_analyzer.builder
builder
Definition: profile_analyzer.py:159
asyncio_get_stats.args
args
Definition: asyncio_get_stats.py:40
grpc_core::CoreConfiguration::Get
static const CoreConfiguration & Get()
Definition: core_configuration.h:82
Json
JSON (JavaScript Object Notation).
Definition: third_party/bloaty/third_party/protobuf/conformance/third_party/jsoncpp/json.h:227
gpr_log
GPRAPI void gpr_log(const char *file, int line, gpr_log_severity severity, const char *format,...) GPR_PRINT_FORMAT_CHECK(4
GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING
#define GRPC_ARG_EXPERIMENTAL_ENABLE_HEDGING
Definition: grpc_types.h:405
grpc_core::internal::RetryServiceConfigParser::Register
static void Register(CoreConfiguration::Builder *builder)
Definition: retry_service_config.cc:53
json_util.h
absl::optional
Definition: abseil-cpp/absl/types/internal/optional.h:61
MAX_MAX_RETRY_ATTEMPTS
#define MAX_MAX_RETRY_ATTEMPTS
Definition: retry_service_config.cc:43
retry_service_config.h
grpc_core::ServiceConfigParser::GetParserIndex
size_t GetParserIndex(absl::string_view name) const
Definition: lib/service_config/service_config_parser.cc:92
intptr_t
_W64 signed int intptr_t
Definition: stdint-msvc2008.h:118
grpc_core::Json::Type::NUMBER
@ NUMBER
grpc_core::Json::Type::ARRAY
@ ARRAY
GPR_ERROR
#define GPR_ERROR
Definition: include/grpc/impl/codegen/log.h:57
GRPC_ERROR_CREATE_FROM_STATIC_STRING
#define GRPC_ERROR_CREATE_FROM_STATIC_STRING(desc)
Definition: error.h:291
value
const char * value
Definition: hpack_parser_table.cc:165
grpc_core::Duration::Zero
static constexpr Duration Zero()
Definition: src/core/lib/gprpp/time.h:130
grpc_core::internal::StatusCodeSet
A set of grpc_status_code values.
Definition: status_util.h:43
grpc_status_code_from_string
bool grpc_status_code_from_string(const char *status_str, grpc_status_code *status)
Definition: status_util.cc:51
grpc_core::ParseDurationFromJson
bool ParseDurationFromJson(const Json &field, Duration *duration)
Definition: src/core/lib/json/json_util.cc:32
grpc_core::internal::StatusCodeSet::Empty
bool Empty() const
Definition: status_util.h:45
gpr_parse_bytes_to_uint32
int gpr_parse_bytes_to_uint32(const char *buf, size_t len, uint32_t *result)
Definition: string.cc:149
grpc_core::internal::RetryServiceConfigParser::ParserIndex
static size_t ParserIndex()
Definition: retry_service_config.cc:48
channel_args.h
gpr_parse_nonnegative_int
int gpr_parse_nonnegative_int(const char *value)
Definition: string.cc:218
internal
Definition: benchmark/test/output_test_helper.cc:20
Duration
Definition: bloaty/third_party/protobuf/src/google/protobuf/duration.pb.h:69
grpc_core::ParseJsonObjectFieldAsDuration
bool ParseJsonObjectFieldAsDuration(const Json::Object &object, absl::string_view field_name, Duration *output, std::vector< grpc_error_handle > *error_list, bool required)
Definition: src/core/lib/json/json_util.cc:107
grpc_error
Definition: error_internal.h:42
grpc_core::Duration
Definition: src/core/lib/gprpp/time.h:122
grpc_core::Json::Type::STRING
@ STRING
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230
GRPC_ERROR_IS_NONE
#define GRPC_ERROR_IS_NONE(err)
Definition: error.h:241
port_platform.h


grpc
Author(s):
autogenerated on Fri May 16 2025 03:00:05