---
name: retry_with_backoff
kind: function
lang: py
domain: core
version: "1.0.0"
purity: impure
signature: "def retry_with_backoff(max_retries: int = 3, initial_delay: float = 1.0, max_delay: float = 30.0, backoff_factor: float = 2.0, jitter: bool = True, exceptions: tuple[type[Exception], ...] = (Exception,), on_retry: Callable[[Exception, int], None] | None = None) -> Callable"
description: "Decorador que reintenta una funcion sincrona con exponential backoff cuando lanza excepciones del tipo especificado. Soporta jitter, callback on_retry y filtrado por tipo de excepcion."
tags: [retry, backoff, exponential, decorator, sync, resilience, jitter]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [functools, time, random, asyncio, collections.abc, typing]
params:
  - name: max_retries
    desc: "Número máximo de reintentos"
  - name: initial_delay
    desc: "Delay inicial en segundos"
  - name: max_delay
    desc: "Delay máximo en segundos"
  - name: backoff_factor
    desc: "Factor multiplicativo para el delay exponencial"
  - name: jitter
    desc: "Si True, añade variabilidad aleatoria al delay"
  - name: exceptions
    desc: "Tupla de tipos de excepción a reintentar"
  - name: on_retry
    desc: "Callback opcional llamado cuando se reintenta"
output: "Decorador que envuelve la función con lógica de retry"
tested: true
tests:
  - "funcion que falla dos veces y luego exito"
  - "funcion que siempre falla agota retries y lanza"
  - "on_retry callback se llama"
  - "jitter produce delays variables"
  - "solo reintenta excepciones especificadas"
test_file_path: "python/functions/core/retry_with_backoff_test.py"
file_path: "python/functions/core/retry_with_backoff.py"
---

## Ejemplo

```python
from retry_with_backoff import retry_with_backoff

@retry_with_backoff(max_retries=3, exceptions=(ConnectionError, TimeoutError))
def call_api():
    response = requests.get("https://api.example.com/data")
    response.raise_for_status()
    return response.json()

# Con callback para logging
def log_retry(exc: Exception, attempt: int) -> None:
    print(f"Attempt {attempt} failed: {exc}. Retrying...")

@retry_with_backoff(max_retries=5, on_retry=log_retry)
def fetch_resource():
    ...
```

## Notas

Impure: hace `time.sleep` entre reintentos. El delay entre intentos se calcula como `min(delay, max_delay)`, opcionalmente multiplicado por un factor aleatorio en `[0.5, 1.0]` si `jitter=True`. Tras cada intento fallido, `delay *= backoff_factor`. El parametro `max_retries` es el numero de reintentos despues del primer intento (total de llamadas = max_retries + 1). Solo reintenta excepciones que sean instancias de los tipos en `exceptions`. Implementado en el mismo archivo que `retry_with_backoff_async`.
