summaryrefslogtreecommitdiffstats
path: root/third_party/python/taskcluster/taskcluster/aio/retry.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/python/taskcluster/taskcluster/aio/retry.py41
1 files changed, 41 insertions, 0 deletions
diff --git a/third_party/python/taskcluster/taskcluster/aio/retry.py b/third_party/python/taskcluster/taskcluster/aio/retry.py
new file mode 100644
index 0000000000..d4f743f2d5
--- /dev/null
+++ b/third_party/python/taskcluster/taskcluster/aio/retry.py
@@ -0,0 +1,41 @@
+import logging
+import asyncio
+
+from .. import utils
+
+log = logging.getLogger(__name__)
+
+
+async def retry(maxRetries, tryFn):
+ """
+ Retry async `tryFn` based on `maxRetries`. Each call to `tryFn` will pass a callable
+ which should be called with the exception object when an exception can be retried.
+ Exceptions raised from `tryFn` are treated as fatal.
+ """
+
+ retry = -1 # we plus first in the loop, and attempt 1 is retry 0
+ while True:
+ retry += 1
+
+ # if this isn't the first retry then we sleep
+ if retry > 0:
+ snooze = float(retry * retry) / 10.0
+ log.info('Sleeping %0.2f seconds for exponential backoff', snooze)
+ await asyncio.sleep(utils.calculateSleepTime(retry))
+
+ retriableException = None
+
+ def retryFor(exc):
+ nonlocal retriableException
+ retriableException = exc
+
+ res = await tryFn(retryFor)
+
+ if not retriableException:
+ return res
+
+ if retry < maxRetries:
+ log.warning(f'Retrying because of: {retriableException}')
+ continue
+
+ raise retriableException