summaryrefslogtreecommitdiffstats
path: root/third_party/python/taskcluster/taskcluster/retry.py
blob: 59cf581e488ca17f85e8e44504ced95816cba2c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import logging
import time

from . import utils

log = logging.getLogger(__name__)


def retry(maxRetries, tryFn):
    """
    Retry `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)
            time.sleep(utils.calculateSleepTime(retry))

        retriableException = None

        def retryFor(exc):
            nonlocal retriableException
            retriableException = exc

        res = tryFn(retryFor)

        if not retriableException:
            return res

        if retry < maxRetries:
            log.warning(f'Retrying because of: {retriableException}')
            continue

        raise retriableException