Skip to content

Retry & jitter

@retry

A decorator for exponential-backoff retries. It detects whether the wrapped function is sync or async and picks the right implementation automatically.

from vayu import retry

@retry(ConnectionError, tries=4, delay=1, backoff=2)
async def call_api():
    ...
  • exception_to_check — exception class or tuple of classes to catch. Others propagate.
  • tries — total attempts (not retries). tries=4 means up to 4 calls.
  • delay — initial sleep between attempts, seconds.
  • backoff — multiplier applied to delay after each failure.
  • logger — if provided, the retry message goes to logger.warning. Otherwise print.

Example effective delays with delay=1, backoff=2, tries=4: 1 s, 2 s, 4 s, then give up and re-raise the last exception.

Multiple exception classes

@retry((ConnectionError, TimeoutError, OSError), tries=3)
def fetch():
    ...

Why the noqa?

The class is lowercase (class retry) on purpose — it reads like a decorator at call sites. The # noqa suppresses the class-naming lint.

add_jitter

Randomly perturb a value by ±N%:

from vayu.common import add_jitter

sleep_for = add_jitter(30)                 # 24.0 - 36.0 seconds
sleep_for = add_jitter(30, jitter_percentage=10)   # 27.0 - 33.0

Pairs well with @retry when you want to spread retry storms across instances — though note that @retry itself doesn't apply jitter. If you want jittered backoff, apply it in the wrapped function's sleep:

import asyncio
from vayu.common import add_jitter

async def poll_once():
    try:
        return await fetch()
    except ConnectionError:
        await asyncio.sleep(add_jitter(5))
        raise

Other helpers in common

group(iterable, key)

from vayu.common import group

users = [{"name": "A", "team": 1}, {"name": "B", "team": 1}, {"name": "C", "team": 2}]
by_team = group(users, key=lambda u: u["team"])
# {1: [{"name": "A", ...}, {"name": "B", ...}], 2: [{"name": "C", ...}]}

camel_or_space_to_snake

from vayu import camel_or_space_to_snake

camel_or_space_to_snake("HelloWorld")      # "hello_world"
camel_or_space_to_snake("hello world")     # "hello_world"

See also