GH GambleHub

Estrategias de repetición e idempotencia

1) Por qué es necesario

En redes, las fallas son la norma: tiempos de espera, errores de tránsito, flappings de red, sobrecarga. Los retiros solo aumentan la fiabilidad si:

1. la repetición es segura (idempotente),

2. se respetan los pasajes entre repeticiones,

3. se respetan los límites/cuotas y la «salud» de las adicciones.

El objetivo es un comportamiento effectively-once a nivel de operaciones empresariales sin tomas y carreras falsas.

2) Taxonomía de la semántica de la entrega

At-most-once: sin repeticiones, riesgo de pérdida (lógica, fuego y forget).
At-least-once: duplicados posibles → se necesita la idempotencia del consumidor (la mayoría de colas, webhooks).
Effectively-once: los duplicados son posibles, pero se deduplican correctamente (claves, transacciones, outbox).

3) Cuándo retraerse y cuándo no

El retiro tiene sentido: '408', '429' (respetando 'Retry-After'), '425' (Too Early), '499' (client closed en el perímetro), '5xx', '504', timeouts/brechas de red, '502' en la pasarela, «connection reset».
No retraemos sin cambiar la consulta: '400/ 401/403/404/422'.
Casos controvertidos: '409 Conflict' (normalmente no retraído; primero leemos el estado de la operación/reconfirmamos la intención).

4) Timeouts, backoff y jitter

4. 1 Reglas

Primero el taimaut, luego los retrayas: cada solicitud debe tener un «deadline».
Backoff exponencial: 'delay _ n = base 2 ^ n', limitamos' max _ delay '.
Jitter es obligatorio: agregue aleatoriedad para el desacoplamiento de «ondas sincrónicas contundentes».

4. 2 Plantillas de jitter

Full jitter: 'sleep = rand (0, base2 ^ n)' es la mejor opción general.
Decorrelated jitter: 'sleep = min (max_delay, rand (base, sleep_prev3))' - para los diálogos largos.
Equal jitter: 'sleep = base2 ^ n/2 + rand (0, base2 ^ n/2)' es una variación suave.

4. 3 Retry-budget

Limite la proporción de retraídos:
  • `retry_budget_per_min = max(α success_rps, floor β)`; normalmente 'α = 0. 1–0. 2`.
  • Cuando se agota el presupuesto - cambiar a fail-fast/circuit breaker «abierto».

5) Interacción con rate limiting y Circuit Breaker

Respeta 'Retry-After', 'RateLimit-Reset' y cuéntalo en el back-off.
Con los altos '5xx '/timeouts - reduzca la frecuencia de retraídas y el paralelismo general.

Circuit breaker:
  • Medio-abierto: permite una muestra limitada.
  • Abierto: rechaza instantáneamente (ahorra recursos).
  • Cerrado: trabajo normal.
  • En las operaciones de escritura, es preferible devolver 409/503 con una pista clara que torcer retraídas agresivas.

6) Idempotencia de las operaciones de escritura

6. 1 Idea general

Las mismas intenciones → un solo resultado. La base es la clave de idempotencia y el repositorio de registros de ejecución.

6. 2 contrato HTTP

El cliente envía el encabezado:

Idempotency-Key: 7a6b7f9e-2a46-4d0b-9c3a-2b30e1c3c9e3
Idempotency-Key-Expiry: 24h # optional
Servidor:
  • la primera vez que se realiza correctamente, guarda (clave → resultado, estado, hash del cuerpo);
  • cuando se repite, devuelve la respuesta anterior y el encabezado 'Idempotency-Replay: true';
  • cuando hay un conflicto corporal (la misma clave pero otra payload) es '409 Conflict'.

6. 3 Almacenamiento y TTL

Tabla/clave-valor: 'idempotency _ key', 'request _ hash', 'nat', 'status', 'expiry _ at'.
TTL = ventana de repeticiones posibles y entregas tardías (generalmente 24-72 h para pagos).
Índices por 'idempotency _ key'; para la carga alta - charding hash.

6. 4 Ejemplo de esquema (SQL)

sql
CREATE TABLE idempo_store (
key UUID PRIMARY KEY,
req_hash BYTEA NOT NULL,
status INT NOT NULL,
response JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
expiry_at TIMESTAMPTZ NOT NULL
);

6. 5 Pseudocódigo del manejador

pseudo handle_write(req):
k = req. headers["Idempotency-Key"]
h = hash(req. body)
rec = idempo_store. get(k)

if rec and rec. req_hash == h:
return rec. status, rec. response, {"Idempotency-Replay": "true"}

if rec and rec. req_hash!= h:
return 409, problem("IDEMPOTENT_CONFLICT")

begin tx result = apply_business_mutation (req) # change status upsert once (idempo_store, key = k, req_hash=h, status = 201, response = result, expiry = now () + 2d)
commit

return 201, result

7) Patrones «effectively-once»

Transactional Outbox: registrar un evento comercial y enviar un mensaje desde la misma transacción de BD a través de un relayer de fondo; el consumidor es idempotente.
Inbox/Processed-table en el consumidor: guardamos 'event _ id' para ignorar las tomas.
Exactly-once en Kafka ≠ exactly-once en el negocio: incluso con EOS productor/consumer, la lógica aplicada todavía debe ser idempotente.
Transacciones compensatorias (Saga): si los pasos se retrotraen y causan efectos secundarios, devolvemos el sistema al invariante.

8) Casos privados: pagos y transacciones financieras

Strong idempotency: la clave está vinculada a la lógica de la operación (por ejemplo, 'external _ payment _ id').
Deduplicación en PSP: almacene 'merchant _ reference' → cuando se repita, PSP devolverá el resultado anterior.
Retraídas «del cliente»: permitir sólo en 'Idempotency-Key', de lo contrario el riesgo de doble cargo.
Competitividad: bloqueos «en la cuenta/instrumento/contrato» durante el tiempo de ejecución; si se repite, devuelva 409/423.
Observabilidad: métricas 'idempo _ replay _ total', 'idempo _ conflict _ total'.

9) Webhooks y llamadas externas

Firmas HMAC y ventana de tiempo; primero la inspección, luego el procesamiento.
Retratos del remitente: retroceso exponencial + jitter, 'max _ attempts' y DLQ.
Consumidor - idempotente: 'event _ id' → tabla/in-memory cache; el orden «ordenado» no está garantizado.
Códigos: 2xx = éxito, 4xx = no repetir, 5xx/timaut = repetir.

10) Colas y tareas de fondo

At-least-once es el valor predeterminado → los duplicados son inevitables.
Almacene 'task _ id '/' event _ id' y el estado de ejecución; cuando se toma es un camino corto «replay».
DLQ y mensajes de punto: contador de intentos, cuarentena, análisis manual.
Límites competitivos (semáforos) y workers idempotentes.

11) Versificación y claves «naturales»

Las llaves naturales (número de cuenta + fecha + número de documento) aumentan la resistencia a las repeticiones.
Cuando cambie de esquema/versión, incluya la clave de versión en 'Idempotency-Key' o en el hash de solicitud.

12) Encabezados HTTP y sugerencias al cliente

'Idempotency-Key', 'Idempotency-Replay', 'Retry-After', 'Prefer: wait = <sec>' (sobre operaciones largas), 'If-Match '/' ETag' (bloqueos optimistas).
409 en conflicto de clave 425/429/503 con el válido 'Retry-After'.
Para operaciones «largas», recibe el estado asíncrono ('202 Accepted' + 'Location' por recurso de estado).

13) Pruebas y escenarios de caos

Pruebas negativas: doble envío, repetición con otro cuerpo, reloj Rassynchron.
Perturbación del orden: 't2' viene antes 't1'.
Inyección de temporizadores/' RST '/' EOF ', consultas a medias (slow-POST).
El almacenamiento de información caído → el comportamiento de fail-cerrado (mejor fallo que doble cargo).

14) Métricas y alertas

`retries_total{reason}`, `retry_budget_used{route}`, `backoff_seconds_bucket`.
`idempo_replay_total`, `idempo_conflict_total`, `duplicate_detected_total`.
Proporción 409/425/429/5xx por ruta; p95/p99 «tiempo antes del éxito» con retraídas.
Alertas: burn-rate presupuesto retrae, estallido de conflictos de idempotencia, crecimiento de DLQ.

15) Antipattern

Retractar todos los errores seguidos.
La ausencia del jitter → las ondas sincrónicas de los retraídos.
Llaves de larga vida sin TTL y limpieza.
Mantener el resultado después de commitir el efecto secundario (infracción de outbox).
Los registros sin 'trace _ id '/' idempotency _ key' → no son posibles.
Retratos paralelos agresivos en operaciones de escritura.

16) Lista de comprobación prod

  • Política única: que retraemos, que no; códigos y consejos al cliente.
  • Backoff exponencial + full jitter; establecido 'retry _ budget'.
  • Contrato 'Idempotency-Key' + almacenamiento de resultados con TTL.
  • Outbox/Inbox para eventos; DLQ; límites de competitividad.
  • Integración con Circuit breaker, respect 'Retry-After'.
  • Métricas/alertas sobre retratos/duplicados/conflictos.
  • Conjunto de pruebas de caos y emulación de fallas de red.
  • Documentación para clientes: ejemplos de back-offs y estados.

17) TL; DR

Los retraídos solo son útiles junto con la idempotencia. Introduce 'Idempotency-Key' y el repositorio de resultados, aplica un retroceso exponencial con jitter y retry-budget, respeta 'Retry-After', integra con circuit breaker. Para eventos - outbox/inbox; para pagos - deduplicación y bloqueo rigurosos. Mida retraídas y conflictos, pruebe duplicados y tiempos de espera.

Contact

Póngase en contacto

Escríbanos ante cualquier duda o necesidad de soporte.¡Siempre estamos listos para ayudarle!

Telegram
@Gamble_GC
Iniciar integración

El Email es obligatorio. Telegram o WhatsApp — opcionales.

Su nombre opcional
Email opcional
Asunto opcional
Mensaje opcional
Telegram opcional
@
Si indica Telegram, también le responderemos allí además del Email.
WhatsApp opcional
Formato: +código de país y número (por ejemplo, +34XXXXXXXXX).

Al hacer clic en el botón, usted acepta el tratamiento de sus datos.