standard_retry_strategy.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/io/logging.h>
  6. #include <aws/io/retry_strategy.h>
  7. #include <aws/common/byte_buf.h>
  8. #include <aws/common/hash_table.h>
  9. #include <aws/common/mutex.h>
  10. #include <aws/common/string.h>
  11. #include <inttypes.h>
  12. AWS_STRING_FROM_LITERAL(s_empty_string, "");
  13. static struct aws_byte_cursor s_empty_string_cur = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("");
  14. static const size_t s_initial_retry_bucket_capacity = 500u;
  15. static const size_t s_standard_retry_cost = 5u;
  16. static const size_t s_standard_transient_cost = 10u;
  17. static const size_t s_standard_no_retry_cost = 1u;
  18. struct retry_bucket {
  19. struct aws_allocator *allocator;
  20. struct aws_retry_strategy *owner;
  21. struct aws_string *partition_id;
  22. struct aws_byte_cursor partition_id_cur;
  23. struct {
  24. size_t current_capacity;
  25. struct aws_mutex partition_lock;
  26. } synced_data;
  27. };
  28. struct retry_bucket_token {
  29. struct aws_retry_token retry_token;
  30. struct retry_bucket *strategy_bucket;
  31. struct aws_retry_token *exp_backoff_token;
  32. aws_retry_strategy_on_retry_token_acquired_fn *original_on_acquired;
  33. aws_retry_strategy_on_retry_ready_fn *original_on_ready;
  34. size_t last_retry_cost;
  35. void *original_user_data;
  36. };
  37. static bool s_partition_id_equals_byte_cur(const void *seated_cur, const void *cur_ptr) {
  38. return aws_byte_cursor_eq_ignore_case(seated_cur, cur_ptr);
  39. }
  40. static uint64_t s_hash_partition_id(const void *seated_partition_ptr) {
  41. return aws_hash_byte_cursor_ptr_ignore_case(seated_partition_ptr);
  42. }
  43. static void s_destroy_standard_retry_bucket(void *retry_bucket) {
  44. struct retry_bucket *standard_retry_bucket = retry_bucket;
  45. AWS_LOGF_TRACE(
  46. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  47. "id=%p: destroying bucket partition " PRInSTR,
  48. (void *)standard_retry_bucket->owner,
  49. AWS_BYTE_CURSOR_PRI(standard_retry_bucket->partition_id_cur));
  50. aws_string_destroy(standard_retry_bucket->partition_id);
  51. aws_mutex_clean_up(&standard_retry_bucket->synced_data.partition_lock);
  52. aws_mem_release(standard_retry_bucket->allocator, standard_retry_bucket);
  53. }
  54. struct standard_strategy {
  55. struct aws_retry_strategy base;
  56. struct aws_retry_strategy *exponential_backoff_retry_strategy;
  57. size_t max_capacity;
  58. struct {
  59. struct aws_hash_table token_buckets;
  60. struct aws_mutex lock;
  61. } synced_data;
  62. };
  63. static void s_standard_retry_destroy(struct aws_retry_strategy *retry_strategy) {
  64. AWS_LOGF_TRACE(AWS_LS_IO_STANDARD_RETRY_STRATEGY, "id=%p: destroying self", (void *)retry_strategy);
  65. struct standard_strategy *standard_strategy = retry_strategy->impl;
  66. aws_retry_strategy_release(standard_strategy->exponential_backoff_retry_strategy);
  67. aws_hash_table_clean_up(&standard_strategy->synced_data.token_buckets);
  68. aws_mutex_clean_up(&standard_strategy->synced_data.lock);
  69. aws_mem_release(retry_strategy->allocator, standard_strategy);
  70. }
  71. static void s_on_standard_retry_token_acquired(
  72. struct aws_retry_strategy *retry_strategy,
  73. int error_code,
  74. struct aws_retry_token *token,
  75. void *user_data) {
  76. (void)retry_strategy;
  77. (void)token;
  78. struct retry_bucket_token *retry_token = user_data;
  79. AWS_LOGF_DEBUG(
  80. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  81. "id=%p: token acquired callback invoked with error %s with token %p and nested token %p",
  82. (void *)retry_token->retry_token.retry_strategy,
  83. aws_error_str(error_code),
  84. (void *)&retry_token->retry_token,
  85. (void *)token);
  86. AWS_LOGF_TRACE(
  87. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  88. "id=%p: invoking on_retry_token_acquired callback",
  89. (void *)retry_token->retry_token.retry_strategy);
  90. aws_retry_token_acquire(&retry_token->retry_token);
  91. if (!error_code) {
  92. retry_token->exp_backoff_token = token;
  93. retry_token->original_on_acquired(
  94. retry_token->strategy_bucket->owner,
  95. error_code,
  96. &retry_token->retry_token,
  97. retry_token->original_user_data);
  98. AWS_LOGF_TRACE(
  99. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  100. "id=%p: on_retry_token_acquired callback completed",
  101. (void *)retry_token->retry_token.retry_strategy);
  102. } else {
  103. retry_token->original_on_acquired(
  104. retry_token->strategy_bucket->owner, error_code, NULL, retry_token->original_user_data);
  105. AWS_LOGF_TRACE(
  106. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  107. "id=%p: on_retry_token_acquired callback completed",
  108. (void *)retry_token->retry_token.retry_strategy);
  109. }
  110. aws_retry_token_release(&retry_token->retry_token);
  111. }
  112. static int s_standard_retry_acquire_token(
  113. struct aws_retry_strategy *retry_strategy,
  114. const struct aws_byte_cursor *partition_id,
  115. aws_retry_strategy_on_retry_token_acquired_fn *on_acquired,
  116. void *user_data,
  117. uint64_t timeout_ms) {
  118. struct standard_strategy *standard_strategy = retry_strategy->impl;
  119. bool bucket_needs_cleanup = false;
  120. const struct aws_byte_cursor *partition_id_ptr =
  121. !partition_id || partition_id->len == 0 ? &s_empty_string_cur : partition_id;
  122. AWS_LOGF_DEBUG(
  123. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  124. "id=%p: attempting to acquire retry token for partition_id " PRInSTR,
  125. (void *)retry_strategy,
  126. AWS_BYTE_CURSOR_PRI(*partition_id_ptr));
  127. struct retry_bucket_token *token = aws_mem_calloc(retry_strategy->allocator, 1, sizeof(struct retry_bucket_token));
  128. if (!token) {
  129. return AWS_OP_ERR;
  130. }
  131. token->original_user_data = user_data;
  132. token->original_on_acquired = on_acquired;
  133. struct aws_hash_element *element_ptr;
  134. struct retry_bucket *bucket_ptr;
  135. AWS_FATAL_ASSERT(!aws_mutex_lock(&standard_strategy->synced_data.lock) && "Lock acquisition failed.");
  136. aws_hash_table_find(&standard_strategy->synced_data.token_buckets, partition_id_ptr, &element_ptr);
  137. if (!element_ptr) {
  138. AWS_LOGF_DEBUG(
  139. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  140. "id=%p: bucket for partition_id " PRInSTR " does not exist, attempting to create one",
  141. (void *)retry_strategy,
  142. AWS_BYTE_CURSOR_PRI(*partition_id_ptr));
  143. bucket_ptr = aws_mem_calloc(standard_strategy->base.allocator, 1, sizeof(struct retry_bucket));
  144. if (!bucket_ptr) {
  145. AWS_LOGF_ERROR(
  146. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  147. "id=%p: error when allocating bucket %s",
  148. (void *)retry_strategy,
  149. aws_error_debug_str(aws_last_error()));
  150. goto table_locked;
  151. }
  152. bucket_needs_cleanup = true;
  153. bucket_ptr->allocator = standard_strategy->base.allocator;
  154. bucket_ptr->partition_id = partition_id_ptr->len > 0
  155. ? aws_string_new_from_cursor(standard_strategy->base.allocator, partition_id)
  156. : (struct aws_string *)s_empty_string;
  157. if (!bucket_ptr->partition_id) {
  158. AWS_LOGF_ERROR(
  159. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  160. "id=%p: error when allocating partition_id %s",
  161. (void *)retry_strategy,
  162. aws_error_debug_str(aws_last_error()));
  163. goto table_locked;
  164. }
  165. bucket_ptr->partition_id_cur = aws_byte_cursor_from_string(bucket_ptr->partition_id);
  166. AWS_FATAL_ASSERT(!aws_mutex_init(&bucket_ptr->synced_data.partition_lock) && "mutex init failed!");
  167. bucket_ptr->owner = retry_strategy;
  168. bucket_ptr->synced_data.current_capacity = standard_strategy->max_capacity;
  169. AWS_LOGF_DEBUG(
  170. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  171. "id=%p: bucket %p for partition_id " PRInSTR " created",
  172. (void *)retry_strategy,
  173. (void *)bucket_ptr,
  174. AWS_BYTE_CURSOR_PRI(*partition_id_ptr));
  175. if (aws_hash_table_put(
  176. &standard_strategy->synced_data.token_buckets, &bucket_ptr->partition_id_cur, bucket_ptr, NULL)) {
  177. AWS_LOGF_ERROR(
  178. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  179. "id=%p: error when putting bucket to token_bucket table %s",
  180. (void *)retry_strategy,
  181. aws_error_debug_str(aws_last_error()));
  182. goto table_locked;
  183. }
  184. bucket_needs_cleanup = false;
  185. } else {
  186. bucket_ptr = element_ptr->value;
  187. AWS_LOGF_DEBUG(
  188. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  189. "id=%p: bucket %p for partition_id " PRInSTR " found",
  190. (void *)retry_strategy,
  191. (void *)bucket_ptr,
  192. AWS_BYTE_CURSOR_PRI(*partition_id_ptr));
  193. }
  194. AWS_FATAL_ASSERT(!aws_mutex_unlock(&standard_strategy->synced_data.lock) && "Mutex unlock failed");
  195. token->strategy_bucket = bucket_ptr;
  196. token->retry_token.retry_strategy = retry_strategy;
  197. aws_atomic_init_int(&token->retry_token.ref_count, 1u);
  198. aws_retry_strategy_acquire(retry_strategy);
  199. token->retry_token.allocator = retry_strategy->allocator;
  200. token->retry_token.impl = token;
  201. /* don't decrement the capacity counter, but add the retry payback, so making calls that succeed allows for a
  202. * gradual recovery of the bucket capacity. Otherwise, we'd never recover from an outage. */
  203. token->last_retry_cost = s_standard_no_retry_cost;
  204. AWS_LOGF_TRACE(
  205. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  206. "id=%p: allocated token %p for partition_id " PRInSTR,
  207. (void *)retry_strategy,
  208. (void *)&token->retry_token,
  209. AWS_BYTE_CURSOR_PRI(*partition_id_ptr));
  210. if (aws_retry_strategy_acquire_retry_token(
  211. standard_strategy->exponential_backoff_retry_strategy,
  212. partition_id_ptr,
  213. s_on_standard_retry_token_acquired,
  214. token,
  215. timeout_ms)) {
  216. AWS_LOGF_ERROR(
  217. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  218. "id=%p: error when acquiring retry token from backing retry strategy %p: %s",
  219. (void *)retry_strategy,
  220. (void *)standard_strategy->exponential_backoff_retry_strategy,
  221. aws_error_debug_str(aws_last_error()));
  222. goto table_updated;
  223. }
  224. return AWS_OP_SUCCESS;
  225. table_updated:
  226. AWS_FATAL_ASSERT(!aws_mutex_lock(&standard_strategy->synced_data.lock) && "Mutex lock failed");
  227. aws_hash_table_remove(&standard_strategy->synced_data.token_buckets, &bucket_ptr->partition_id_cur, NULL, NULL);
  228. bucket_needs_cleanup = false;
  229. table_locked:
  230. AWS_FATAL_ASSERT(!aws_mutex_unlock(&standard_strategy->synced_data.lock) && "Mutex unlock failed");
  231. if (bucket_needs_cleanup) {
  232. s_destroy_standard_retry_bucket(bucket_ptr);
  233. }
  234. aws_retry_token_release(&token->retry_token);
  235. return AWS_OP_ERR;
  236. }
  237. void s_standard_retry_strategy_on_retry_ready(struct aws_retry_token *token, int error_code, void *user_data) {
  238. (void)token;
  239. struct aws_retry_token *standard_retry_token = user_data;
  240. struct retry_bucket_token *impl = standard_retry_token->impl;
  241. AWS_LOGF_TRACE(
  242. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  243. "id=%p: invoking on_retry_ready callback with error %s, token %p, and nested token %p",
  244. (void *)token->retry_strategy,
  245. aws_error_str(error_code),
  246. (void *)standard_retry_token,
  247. (void *)token);
  248. struct aws_retry_strategy *retry_strategy = token->retry_strategy;
  249. /* we already hold a reference count here due to the previous acquire before scheduling, so don't worry
  250. * about incrementing standard_retry_token here */
  251. impl->original_on_ready(standard_retry_token, error_code, impl->original_user_data);
  252. AWS_LOGF_TRACE(
  253. AWS_LS_IO_STANDARD_RETRY_STRATEGY, "id=%p: on_retry_ready callback completed", (void *)retry_strategy);
  254. /* this is to release the acquire we did before scheduling the retry. Release it now. */
  255. aws_retry_token_release(standard_retry_token);
  256. }
  257. static int s_standard_retry_strategy_schedule_retry(
  258. struct aws_retry_token *token,
  259. enum aws_retry_error_type error_type,
  260. aws_retry_strategy_on_retry_ready_fn *retry_ready,
  261. void *user_data) {
  262. if (error_type == AWS_RETRY_ERROR_TYPE_CLIENT_ERROR) {
  263. return aws_raise_error(AWS_IO_RETRY_PERMISSION_DENIED);
  264. }
  265. struct retry_bucket_token *impl = token->impl;
  266. size_t capacity_consumed = 0;
  267. AWS_FATAL_ASSERT(!aws_mutex_lock(&impl->strategy_bucket->synced_data.partition_lock) && "mutex lock failed");
  268. size_t current_capacity = impl->strategy_bucket->synced_data.current_capacity;
  269. if (current_capacity == 0) {
  270. AWS_FATAL_ASSERT(
  271. !aws_mutex_unlock(&impl->strategy_bucket->synced_data.partition_lock) && "mutex unlock failed");
  272. AWS_LOGF_INFO(
  273. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  274. "token_id=%p: requested to schedule retry but the bucket capacity is empty. Rejecting retry request.",
  275. (void *)token);
  276. return aws_raise_error(AWS_IO_RETRY_PERMISSION_DENIED);
  277. }
  278. if (error_type == AWS_RETRY_ERROR_TYPE_TRANSIENT) {
  279. capacity_consumed = aws_min_size(current_capacity, s_standard_transient_cost);
  280. } else {
  281. /* you may be looking for throttling, but if that happened, the service told us to slow down,
  282. * but is otherwise healthy. Pay a smaller penalty for those. */
  283. capacity_consumed = aws_min_size(current_capacity, s_standard_retry_cost);
  284. }
  285. AWS_LOGF_DEBUG(
  286. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  287. "token_id=%p: reducing retry capacity by %zu from %zu and scheduling retry.",
  288. (void *)token,
  289. capacity_consumed,
  290. current_capacity);
  291. impl->original_user_data = user_data;
  292. impl->original_on_ready = retry_ready;
  293. size_t previous_cost = impl->last_retry_cost;
  294. impl->last_retry_cost = capacity_consumed;
  295. impl->strategy_bucket->synced_data.current_capacity -= capacity_consumed;
  296. AWS_FATAL_ASSERT(!aws_mutex_unlock(&impl->strategy_bucket->synced_data.partition_lock) && "mutex unlock failed");
  297. /* acquire before scheduling to prevent clean up before the callback runs. */
  298. aws_retry_token_acquire(&impl->retry_token);
  299. if (aws_retry_strategy_schedule_retry(
  300. impl->exp_backoff_token, error_type, s_standard_retry_strategy_on_retry_ready, token)) {
  301. /* release for the above acquire */
  302. aws_retry_token_release(&impl->retry_token);
  303. AWS_LOGF_ERROR(
  304. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  305. "token_id=%p: error occurred while scheduling retry: %s.",
  306. (void *)token,
  307. aws_error_debug_str(aws_last_error()));
  308. /* roll it back. */
  309. AWS_FATAL_ASSERT(!aws_mutex_lock(&impl->strategy_bucket->synced_data.partition_lock) && "mutex lock failed");
  310. impl->last_retry_cost = previous_cost;
  311. size_t desired_capacity = impl->strategy_bucket->synced_data.current_capacity + capacity_consumed;
  312. struct standard_strategy *strategy_impl = token->retry_strategy->impl;
  313. impl->strategy_bucket->synced_data.current_capacity =
  314. desired_capacity < strategy_impl->max_capacity ? desired_capacity : strategy_impl->max_capacity;
  315. AWS_FATAL_ASSERT(
  316. !aws_mutex_unlock(&impl->strategy_bucket->synced_data.partition_lock) && "mutex unlock failed");
  317. return AWS_OP_ERR;
  318. }
  319. return AWS_OP_SUCCESS;
  320. }
  321. static int s_standard_retry_strategy_record_success(struct aws_retry_token *token) {
  322. struct retry_bucket_token *impl = token->impl;
  323. AWS_FATAL_ASSERT(!aws_mutex_lock(&impl->strategy_bucket->synced_data.partition_lock) && "mutex lock failed");
  324. AWS_LOGF_DEBUG(
  325. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  326. "token_id=%p: partition=" PRInSTR
  327. ": recording successful operation and adding %zu units of capacity back to the bucket.",
  328. (void *)token,
  329. AWS_BYTE_CURSOR_PRI(impl->strategy_bucket->partition_id_cur),
  330. impl->last_retry_cost);
  331. size_t capacity_payback = impl->strategy_bucket->synced_data.current_capacity + impl->last_retry_cost;
  332. struct standard_strategy *standard_strategy = token->retry_strategy->impl;
  333. impl->strategy_bucket->synced_data.current_capacity =
  334. capacity_payback < standard_strategy->max_capacity ? capacity_payback : standard_strategy->max_capacity;
  335. impl->last_retry_cost = 0;
  336. AWS_LOGF_TRACE(
  337. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  338. "bucket_id=%p: partition=" PRInSTR " : new capacity is %zu.",
  339. (void *)token,
  340. AWS_BYTE_CURSOR_PRI(impl->strategy_bucket->partition_id_cur),
  341. impl->strategy_bucket->synced_data.current_capacity);
  342. AWS_FATAL_ASSERT(!aws_mutex_unlock(&impl->strategy_bucket->synced_data.partition_lock) && "mutex unlock failed");
  343. return AWS_OP_SUCCESS;
  344. }
  345. static void s_standard_retry_strategy_release_token(struct aws_retry_token *token) {
  346. if (token) {
  347. AWS_LOGF_TRACE(AWS_LS_IO_STANDARD_RETRY_STRATEGY, "id=%p: releasing token", (void *)token);
  348. struct retry_bucket_token *impl = token->impl;
  349. aws_retry_token_release(impl->exp_backoff_token);
  350. aws_retry_strategy_release(token->retry_strategy);
  351. aws_mem_release(token->allocator, impl);
  352. }
  353. }
  354. static struct aws_retry_strategy_vtable s_standard_retry_vtable = {
  355. .schedule_retry = s_standard_retry_strategy_schedule_retry,
  356. .acquire_token = s_standard_retry_acquire_token,
  357. .release_token = s_standard_retry_strategy_release_token,
  358. .destroy = s_standard_retry_destroy,
  359. .record_success = s_standard_retry_strategy_record_success,
  360. };
  361. struct aws_retry_strategy *aws_retry_strategy_new_standard(
  362. struct aws_allocator *allocator,
  363. const struct aws_standard_retry_options *config) {
  364. AWS_PRECONDITION(allocator);
  365. AWS_PRECONDITION(config);
  366. AWS_LOGF_INFO(AWS_LS_IO_STANDARD_RETRY_STRATEGY, "static: creating new standard retry strategy");
  367. struct standard_strategy *standard_strategy = aws_mem_calloc(allocator, 1, sizeof(struct standard_strategy));
  368. if (!standard_strategy) {
  369. AWS_LOGF_ERROR(AWS_LS_IO_STANDARD_RETRY_STRATEGY, "static: allocation of new standard retry strategy failed");
  370. return NULL;
  371. }
  372. aws_atomic_init_int(&standard_strategy->base.ref_count, 1);
  373. struct aws_exponential_backoff_retry_options config_cpy = config->backoff_retry_options;
  374. /* standard default is 3. */
  375. if (!config->backoff_retry_options.max_retries) {
  376. config_cpy.max_retries = 3;
  377. }
  378. AWS_LOGF_INFO(
  379. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  380. "id=%p: creating backing exponential backoff strategy with max_retries of %zu",
  381. (void *)&standard_strategy->base,
  382. config_cpy.max_retries);
  383. standard_strategy->exponential_backoff_retry_strategy =
  384. aws_retry_strategy_new_exponential_backoff(allocator, &config_cpy);
  385. if (!standard_strategy->exponential_backoff_retry_strategy) {
  386. AWS_LOGF_ERROR(
  387. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  388. "id=%p: allocation of new exponential backoff retry strategy failed: %s",
  389. (void *)&standard_strategy->base,
  390. aws_error_debug_str(aws_last_error()));
  391. goto error;
  392. }
  393. if (aws_hash_table_init(
  394. &standard_strategy->synced_data.token_buckets,
  395. allocator,
  396. 16u,
  397. s_hash_partition_id,
  398. s_partition_id_equals_byte_cur,
  399. NULL,
  400. s_destroy_standard_retry_bucket)) {
  401. AWS_LOGF_ERROR(
  402. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  403. "id=%p: token bucket table creation failed: %s",
  404. (void *)&standard_strategy->base,
  405. aws_error_debug_str(aws_last_error()));
  406. goto error;
  407. }
  408. standard_strategy->max_capacity =
  409. config->initial_bucket_capacity ? config->initial_bucket_capacity : s_initial_retry_bucket_capacity;
  410. AWS_LOGF_DEBUG(
  411. AWS_LS_IO_STANDARD_RETRY_STRATEGY,
  412. "id=%p: maximum bucket capacity set to %zu",
  413. (void *)&standard_strategy->base,
  414. standard_strategy->max_capacity);
  415. AWS_FATAL_ASSERT(!aws_mutex_init(&standard_strategy->synced_data.lock) && "mutex init failed");
  416. standard_strategy->base.allocator = allocator;
  417. standard_strategy->base.vtable = &s_standard_retry_vtable;
  418. standard_strategy->base.impl = standard_strategy;
  419. return &standard_strategy->base;
  420. error:
  421. if (standard_strategy->exponential_backoff_retry_strategy) {
  422. aws_retry_strategy_release(standard_strategy->exponential_backoff_retry_strategy);
  423. }
  424. aws_mem_release(allocator, standard_strategy);
  425. return NULL;
  426. }