diff --git a/README.rst b/README.rst index 2bdcae5..723b210 100644 --- a/README.rst +++ b/README.rst @@ -95,6 +95,20 @@ gets a non-falsey result could be defined like like this:: def poll_for_message(queue) return queue.get() + +@backoff.factor +--------------- + +The ``factor`` decorator is used to ajust other wait generator. + + @backoff.on_predicate(backoff.factor(backoff.constant, 60), interval=1) + def poll_for_message(queue) + return queue.get() + +Here `backoff.factor(backoff.constant, 60)` make backoff to sleep 60 times +longer than original wait generator. + + Jitter ------ diff --git a/backoff.py b/backoff.py index c46137d..74a86c1 100644 --- a/backoff.py +++ b/backoff.py @@ -85,6 +85,22 @@ def constant(interval=1): yield interval +def factor(wait_gen, f): + """Wrapped decay by factor. + + Args: + wait_gen: A generator yielding successive wait times in + seconds. + factor: Factor to multiply the wait_gen by. + """ + @functools.wraps(wait_gen) + def deco(*args, **kwargs): + wait = wait_gen(*args, **kwargs) + while True: + yield next(wait) * f + return deco + + def random_jitter(value): """Jitter the value a random number of milliseconds. diff --git a/backoff_tests.py b/backoff_tests.py index aac213c..042a053 100644 --- a/backoff_tests.py +++ b/backoff_tests.py @@ -218,6 +218,35 @@ def succeeder(*args, **kwargs): assert details['wait'] >= 0.5 * 2 ** i +def test_decay_factor(monkeypatch): + monkeypatch.setattr('time.sleep', lambda x: None) + + log, log_success, log_backoff, log_giveup = _log_hdlrs() + + @backoff.on_exception(backoff.factor(backoff.expo, 0.5), + Exception, + on_success=log_success, + on_backoff=log_backoff, + on_giveup=log_giveup, + jitter=backoff.random_jitter) + @_save_target + def succeeder(*args, **kwargs): + # succeed after we've backed off twice + if len(log['backoff']) < 2: + raise ValueError("catch me") + + succeeder(1, 2, 3, foo=1, bar=2) + + # we try 3 times, backing off twice before succeeding + assert len(log['success']) == 1 + assert len(log['backoff']) == 2 + assert len(log['giveup']) == 0 + + for i in range(2): + details = log['backoff'][i] + assert details['wait'] >= 0.5 * 2 ** i + + def test_on_exception_success_full_jitter(monkeypatch): monkeypatch.setattr('time.sleep', lambda x: None)