summaryrefslogtreecommitdiff
path: root/lib/ansible/module_utils
diff options
context:
space:
mode:
authorSloane Hertel <19572925+s-hertel@users.noreply.github.com>2021-05-17 05:06:53 -0400
committerGitHub <noreply@github.com>2021-05-17 04:06:53 -0500
commit162973d1a7a09932408a2744eb5f770b9fc5afb0 (patch)
treef4d6f4038e56665207902883951aaced08104440 /lib/ansible/module_utils
parentfc84de8faa9399246ba619ab4a9983e2c0800f07 (diff)
downloadansible-162973d1a7a09932408a2744eb5f770b9fc5afb0.tar.gz
ansible-galaxy - increase page size and add retry decorator for throttling (#74240) (#74648)
* Get available collection versions with page_size=100 for v2 and limit=100 for v3 * Update unit tests for larger page sizes * Add a generic retry decorator in module_utils/api.py that accepts an Iterable of delays and a callable to determine if an exception inheriting from Exception should be retried * Use the new decorator to handle Galaxy API rate limiting * Add unit tests for new retry decorator * Preserve the decorated function's metadata with functools.wraps Co-authored-by: Matt Martz <matt@sivel.net> Co-authored-by: Sviatoslav Sydorenko <wk.cvs.github@sydorenko.org.ua> (cherry picked from commit ee725846f070fc6b0dd79b5e8c5199ec652faf87)
Diffstat (limited to 'lib/ansible/module_utils')
-rw-r--r--lib/ansible/module_utils/api.py50
1 files changed, 50 insertions, 0 deletions
diff --git a/lib/ansible/module_utils/api.py b/lib/ansible/module_utils/api.py
index 46a036d374..e780ec6b50 100644
--- a/lib/ansible/module_utils/api.py
+++ b/lib/ansible/module_utils/api.py
@@ -26,6 +26,8 @@ The 'api' module provides the following common argument specs:
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
+import functools
+import random
import sys
import time
@@ -114,3 +116,51 @@ def retry(retries=None, retry_pause=1):
return retried
return wrapper
+
+
+def generate_jittered_backoff(retries=10, delay_base=3, delay_threshold=60):
+ """The "Full Jitter" backoff strategy.
+
+ Ref: https://www.awsarchitectureblog.com/2015/03/backoff.html
+
+ :param retries: The number of delays to generate.
+ :param delay_base: The base time in seconds used to calculate the exponential backoff.
+ :param delay_threshold: The maximum time in seconds for any delay.
+ """
+ for retry in range(0, retries):
+ yield random.randint(0, min(delay_threshold, delay_base * 2 ** retry))
+
+
+def retry_never(exception_or_result):
+ return False
+
+
+def retry_with_delays_and_condition(backoff_iterator, should_retry_error=None):
+ """Generic retry decorator.
+
+ :param backoff_iterator: An iterable of delays in seconds.
+ :param should_retry_error: A callable that takes an exception of the decorated function and decides whether to retry or not (returns a bool).
+ """
+ if should_retry_error is None:
+ should_retry_error = retry_never
+
+ def function_wrapper(function):
+ @functools.wraps(function)
+ def run_function(*args, **kwargs):
+ """This assumes the function has not already been called.
+ If backoff_iterator is empty, we should still run the function a single time with no delay.
+ """
+ call_retryable_function = functools.partial(function, *args, **kwargs)
+
+ for delay in backoff_iterator:
+ try:
+ return call_retryable_function()
+ except Exception as e:
+ if not should_retry_error(e):
+ raise
+ time.sleep(delay)
+
+ # Only or final attempt
+ return call_retryable_function()
+ return run_function
+ return function_wrapper