Skip to content

Retry Policies

Istos provides built-in retry mechanisms with exponential backoff for fault-tolerant distributed systems.

Quick Usage

Add automatic retries with a simple integer:

from istos import Istos

istos = Istos()

# Retry up to 5 times with default exponential backoff
@istos.query("weather/forecast", retry=5)
def get_forecast(result):
    return result

# Subscriber with retries — if processing fails, it retries
@istos.subscribe("sensor/readings", retry=3)
def on_reading(data):
    save_to_database(data)

Advanced Configuration

For fine-grained control, use the RetryPolicy class:

from istos.retry import RetryPolicy

@istos.query("weather/forecast", retry=RetryPolicy(
    max_retries=10,
    delay=1.0,           # Initial delay in seconds
    backoff_factor=3.0,  # Multiply delay by this factor each retry
    on_failure=lambda e: print(f"Dead letter: {e}")
))
def get_forecast(result):
    return result

RetryPolicy Parameters

Parameter Type Default Description
max_retries int 0 Maximum number of retry attempts
delay float 0.5 Initial delay between retries (seconds)
backoff_factor float 2.0 Multiplier applied to delay after each retry
max_delay float 60.0 Cap on a single wait, in seconds
jitter float 0.1 Fractional spread (±) applied to each wait. 0 disables it
on_failure Callable None Called when retries are exhausted; the exception is still raised

Backoff Timeline Example

With delay=1.0 and backoff_factor=2.0:

Attempt 1: immediate
Attempt 2: wait 1.0s
Attempt 3: wait 2.0s
Attempt 4: wait 4.0s
Attempt 5: wait 8.0s
→ on_failure() called

A single wait never exceeds max_delay (default 60s). jitter (default 0.1) spreads each wait by ±10%.

Dead Letter Handling

The on_failure callback runs when the operation has permanently failed (a dead-letter hook). The original exception is still raised afterwards, so a failed exactly-once handler is not recorded as a successful None:

def handle_dead_letter(error):
    """Called when all retries are exhausted."""
    log.error(f"Operation permanently failed: {error}")
    alert_ops_team(error)

@istos.query("critical/service", retry=RetryPolicy(
    max_retries=5,
    on_failure=handle_dead_letter
))
def critical_query(result):
    return result

When to Use Retries

Retries are ideal for transient failures — network blips, temporary unavailability, or race conditions during startup. For permanent errors (bad data, missing endpoints), retries won't help.

Next Steps