Saltar al contenido principal

Idempotency

Las fallas de red ocurren. La forma segura de reintentar una llamada a la API sin riesgo de duplicados es usar el header Idempotency-Key — DVS lo reconoce y deduplica dentro de una ventana de 24 horas.

Cómo funciona

  1. Generar un UUID del lado del cliente por operación lógica (una classification, una validation).
  2. Enviarlo en el header Idempotency-Key en cada retry de esa operación.
  3. Si se reintenta y DVS ya procesó el original, DVS retorna la response original sin reprocesar.
curl -X POST https://dvs-ingestion-api.sandbox.osigu.com/v1/classification-requests \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: f47ac10b-58cc-4372-a567-0e02b2c3d479" \
...

Cuándo deduplica DVS

EscenarioComportamiento de DVS
Misma Idempotency-Key, mismo body, dentro de 24hRetorna la response en caché. Sin nuevo procesamiento. Sin nuevos cargos.
Misma Idempotency-Key, body diferente, dentro de 24hRetorna 409 071-409-idempotency-mismatch.
Misma Idempotency-Key, después de 24hTratado como un nuevo request. La key se reutiliza.
Idempotency-Key distinta, mismo bodyCrea un nuevo request.

Mejores prácticas

Generar la key en el límite de la operación

Generarla una sola vez cuando se decide "voy a llamar a DVS para clasificar este documento". Reutilizarla en cualquier retry de esa misma llamada. No regenerarla por retry — eso anula el propósito.

UUID v4 es suficiente

No hay que complicarse — uuid.uuid4() o crypto.randomUUID() es suficiente. Evitar IDs secuenciales (predecibles).

Persistir la key junto con la operación

Almacenar la key en la base de datos junto con la operación que representa. En recuperación tras un crash, es posible reintentar con seguridad leyendo la key persistida.

Una key por operación, no por request

Si la aplicación envía 1000 classifications, generar 1000 keys. No compartir keys entre documentos distintos.

Condiciones de carrera

Si dos requests paralelos llegan a DVS con la misma Idempotency-Key, solo uno se procesa; el otro espera a que el primero termine (hasta 5 segundos) y retorna la misma response. Esto es seguro pero agrega latencia — es recomendable serializar los retries del lado del cliente.

Ejemplo: bucle de retry seguro

import uuid, time, httpx

def classify_with_retry(token, body, max_attempts=3):
idempotency_key = str(uuid.uuid4()) # UNA key por operación lógica
for attempt in range(max_attempts):
try:
return httpx.post(
"https://dvs-ingestion-api.sandbox.osigu.com/v1/classification-requests",
headers={
"Authorization": f"Bearer {token}",
"Idempotency-Key": idempotency_key, # reutilizada en cada retry
},
json=body,
timeout=30,
).raise_for_status().json()
except httpx.HTTPError:
if attempt == max_attempts - 1:
raise
time.sleep(2 ** attempt)