def exponential_backoff()

in lm_human_preferences/utils/gcs.py [0:0]


def exponential_backoff(
        retry_on=lambda e: True, *, init_delay_s=1, max_delay_s=600, max_tries=30, factor=2.0,
        jitter=0.2, log_errors=True):
    """
    Returns a decorator which retries the wrapped function as long as retry_on returns True for the exception.
    :param init_delay_s: How long to wait to do the first retry (in seconds).
    :param max_delay_s: At what duration to cap the retry interval at (in seconds).
    :param max_tries: How many total attempts to perform.
    :param factor: How much to multiply the delay interval by after each attempt (until it reaches max_delay_s).
    :param jitter: How much to jitter by (between 0 and 1) -- each delay will be multiplied by a random value between (1-jitter) and (1+jitter).
    :param log_errors: Whether to print tracebacks on every retry.
    :param retry_on: A predicate which takes an exception and indicates whether to retry after that exception.
    """
    def decorate(f):
        @wraps(f)
        def f_retry(*args, **kwargs):
            delay_s = float(init_delay_s)
            for i in range(max_tries):
                try:
                    return f(*args, **kwargs)
                except Exception as e:
                    if not retry_on(e) or i == max_tries-1:
                        raise
                    if log_errors:
                        print(f"Retrying after try {i+1}/{max_tries} failed:")
                        traceback.print_exc()
                    jittered_delay = random.uniform(delay_s*(1-jitter), delay_s*(1+jitter))
                    time.sleep(jittered_delay)
                    delay_s = min(delay_s * factor, max_delay_s)
        return f_retry
    return decorate