Garantías de entrega de webhooks
Webhooks - notificaciones asíncronas «del sistema al suscriptor» por HTTP (S). La red no es fiable: las respuestas se pierden, los paquetes vienen duplicados o fuera de orden. Por lo tanto, las garantías de entrega no se construyen «según TCP», sino a nivel de protocolo de webhooks y de idempotencia de dominio.
El objetivo clave es proporcionar al cliente una entrega en orden por llave (donde sea necesario), dar al suscriptor los materiales para el tratamiento idempotente y la herramienta reconcile para las restauraciones.
1) Niveles de garantía
Best-effort es un intento único, sin retraídas. Sólo es aceptable para eventos «sin importancia».
At-least-once (recomendado): es posible duplicar y salir del pedido, pero el evento se entregará siempre que el suscriptor esté disponible en un plazo razonable.
Effectively-exactly-once (a nivel de efecto): se consigue mediante una combinación de idempotencia y almacenamiento de dedup en el lado suscriptor/remitente. En el transporte HTTP «exactly-once» no es posible.
2) Contrato de webhook: mínimo necesario
Encabezados (ejemplo):
X-Webhook-Id: 5d1e6a1b-4f7d-4a3d-8b3a-6c2b2f0f3f21 # глобальный ID события
X-Delivery-Attempt: 3 # номер попытки
X-Event-Type: payment.authorized.v1 # тип/версия
X-Event-Time: 2025-10-31T12:34:56Z # ISO8601
X-Partition-Key: psp_tx_987654 # ключ порядка
X-Seq: 418 # монотонный номер по ключу
X-Signature-Alg: HMAC-SHA256
X-Signature: t=1730378096,v1=hex(hmac(secret, t body))
Content-Type: application/json
Cuerpo (ejemplo):
json
{
"id": "5d1e6a1b-4f7d-4a3d-8b3a-6c2b2f0f3f21",
"type": "payment.authorized.v1",
"occurred_at": "2025-10-31T12:34:56Z",
"partition_key": "psp_tx_987654",
"sequence": 418,
"data": {
"payment_id": "psp_tx_987654",
"amount": "10.00",
"currency": "EUR",
"status": "AUTHORIZED"
},
"schema_version": 1
}
Requerimiento para el destinatario: responder rápidamente '2xx' después del buffer y la validación de la firma, y hacer el procesamiento empresarial asíncrono.
3) Orden y causalidad
Orden por clave: la garantía «no saldrá» sólo dentro de una 'partition _ key' (por ejemplo, 'player _ id', 'wallet _ id', 'psp _ tx _ id').
El orden mundial no está garantizado.
En el lado del remitente hay una cola con serialización por clave (un consumidor/sharding), en el lado del destinatario está la entrada con '(source, event_id)' y opcionalmente la espera por falta 'seq'.
Si los pases son críticos - proporcionar pull-API 'GET/eventos? after = checkpoint 'para el estado de «ponerse al día y comprobar».
4) Idempotencia y deduplicación
Cada webhook lleva un 'X-Webhook-Id' estable.
El destinatario almacena 'inbox (event_id)': PK - 'source + event_id'; repeticiones → no-op.
Los efectos secundarios (registro en la DB/billetera) sólo se realizan una vez en la primera «visión» del evento.
Para comandos «con efecto», utilice Idempotency-Key y caché de resultados durante el tiempo que dure la ventana Retray.
5) Retraídas, backoff y ventanas
Política de retiro (referencia):- Retractar en '5xx/timeout/connection error/409-Conflict (retryable )/429'.
- No retraer sobre '4xx' excepto '409/423/429' (y solo con semántica coherente).
- Backoff exponencial + full jitter: 0. 5s, 1s, 2s, 4s, 8s, … hasta 'max = 10-15 min'; TTL ventanas de retrés: por ejemplo, 72 horas.
- Respetar 'Retry-After' en el destinatario.
- Tener un deadline común: «reconocer un evento no entregado» y traducirlo a DLQ.
yaml retry:
initial_ms: 500 multiplier: 2.0 jitter: full max_delay_ms: 900000 ttl: 72h retry_on: [TIMEOUT, 5xx, 429]
6) DLQ и redrive
DLQ es un «cementerio» de eventos venenosos o caducados por TTL con metainformación completa (paload, titulares, errores, intentos, hashes).
Consola Web/API para redrive (reenvío puntual) con edición opcional endpoint/secreta.
Rate-limited redrive y batch-redrive con prioridad.
7) Seguridad
mTLS (si es posible) o TLS 1. 2+.
Firma corporal (HMAC con el secreto per tenant/endpoint). Verificación:1. Extraiga la 't' (timestamp) del título, compruebe la ventana deslizante (por ejemplo, ± 5 min).
8) Cuotas, rate limits y equidad
Fair-Queue per tenant/subscriber: para que un suscriptor/tenant no marque la agrupación general.
Cuotas y límites de burst sobre el tráfico saliente y per-endpoint.
Reacción a '429': leer 'Retry-After', arrastrar el flujo; con un límite largo, degrade (enviar sólo tipos críticos de eventos).
9) Ciclo de vida de suscripción
Registro/Verify: Confirmación POST endpoint → challenge/response o out-of-band.
Lease (opcional): la firma es válida hasta 'valid _ to'; prolongación - explícita.
Secret rotation: `current_secret`, `next_secret` с `switch_at`.
Ping de prueba: un evento artificial para verificar la ruta antes de encender los tops principales.
Muestras de salud: HEAD/GET periódicos con verificación de perfil latencia y TLS.
10) Evolución de los esquemas (versiones de eventos)
Versionar el tipo de evento: 'payment. authorized. v1` → `…v2`.
Evolución - additive (nuevos campos → versión MINOR de la API), breaking → un nuevo tipo.
Registro de esquemas (JSON-Schema/Avro/Protobuf) + validación automática antes del envío.
El título 'X-Event-Type' y el campo 'schema _ version' en el cuerpo son obligatorios.
11) Observabilidad y SLO
Métricas (por tipo/tenante/suscriptor):- `deliveries_total`, `2xx/4xx/5xx_rate`, `timeout_rate`, `signature_fail_rate`.
- 'attempts _ avg', 'p50/p95/p99 _ delivery _ latency _ ms' (de publicación a 2xx).
- `dedup_rate`, `out_of_order_rate`, `dlq_rate`, `redrive_success_rate`.
- `queue_depth`, `oldest_in_queue_ms`, `throttle_events`.
- Porcentaje de envíos ≤ 60 c (p95) - 99. 5% para eventos críticos.
- DLQ ≤ 0. 1% por 24 horas
- Signature failures ≤ 0. 05%.
Логи/трейсинг: `event_id`, `partition_key`, `seq`, `attempt`, `endpoint`, `tenant_id`, `schema_version`, `trace_id`.
12) Algoritmo de referencia del remitente
1. Registrar un evento en una caja de salida transaccional.
2. Definir partition_key y seq; poner en cola.
3. El obrero toma la clave, forma la consulta, firma, envía con los temporizadores (connect/read).
4. Con '2xx' es reconocer a los entregados, fijar la latencia y el seq-checkpoint.
5. Con '429/5xx/timeout' es un retiro según la política.
6. Por TTL → DLQ y alerta.
13) Manejador de referencia (destinatario)
1. Aceptar solicitud, comprobar TLS/proto.
2. Validación de firma y ventana de tiempo.
3. ACK 2xx rápido (después de la grabación sincrónica en inbox/cola local).
4. El verificador asíncrono lee 'inbox', comprueba 'event _ id' (dedoup), si es necesario - ordena por 'seq' dentro de 'partition _ key'.
5. Realiza efectos, escribe «offset/seq checkpoint» para reconcile.
6. En caso de error, retraídas locales; tareas «venenosas» → DLQ local con alertas.
14) Reconcile (contorno de pull)
Para incidentes «impenetrables»:- `GET /events? partition_key=...&after_seq=...&limit=...' - dar todos los perdidos.
- Token-checkpoint: 'after = opaque _ token' en lugar de seq.
- Redelivery idempotente: el mismo 'event _ id', la misma firma según la nueva 't'.
15) Títulos y códigos útiles
2xx - aceptado (incluso si el procesamiento empresarial es posterior).
410 Gone - endpoint cerrado (el remitente detiene la entrega y marca la suscripción como «en el archivo»).
409/423 - bloqueo temporal del recurso → retray es razonable.
429 - con demasiada frecuencia → el TNT y el backoff.
400/401/403/404 - error de configuración; Detener los retiros, abrir el ticket.
16) Multi-tenant y regiones
Colas individuales y límites por tenant/endpoint.
Residencia de datos: envío desde la región de datos; titulares de extremo a extremo 'X-Tenant', 'X-Región'.
Aislamiento de fallos: la caída de un suscriptor no afecta al resto (separate pools).
17) Pruebas
Pruebas de contrato: ejemplos fijos de cuerpos/firmas, validación de validación.
Chaos: drop/duplicados, shuffle de orden, retardos de red, 'RST', 'TLS' errores.
Load: tormenta burst, p95/p99 medido.
Seguridad: anti-replay, timestamp obsoleto, secretos incorrectos, rotación.
DR/Replay: redrive masivo de DLQ en un stand aislado.
18) Playbooks (runbooks)
1. Crecimiento de 'signature _ fail _ rate'
Comprobar la deriva del reloj, caducada 'tolerance', la rotación de secretos; habilitar temporalmente «doble secreto».
2. La cola envejece ('oldest _ in _ queue _ ms' ↑)
Aumentar los workers, activar la priorización de topics críticos, reducir temporalmente la frecuencia de tipos «ruidosos».
3. La tormenta '429' por un suscriptor
Activar el trottling y las pausas entre intentos; mover tipos de eventos menos críticos.
4. Masiva '5xx'
Abrir circuit-breaker para un endpoint específico, poner en modo defer & batch; señal al suscriptor.
5. Rellenar DLQ
Detener la publicación no crítica, activar el redrive de batch con RPS bajo, levantar alertas a los propietarios de suscripciones.
19) Errores típicos
Procesamiento duro síncrono antes de la respuesta 2xx → retrés y duplicados.
No hay firma de cuerpo/ventana de tiempo → vulnerabilidad a la sustitución/réplica.
La ausencia de 'event _ id' y 'inbox' → es imposible hacer idempotencia.
El intento de «orden global» → bloqueos eternos de las colas.
Retraídas sin jitter/límites → aumento del incidente (thundering herd).
Un único grupo común en todos los suscriptores → «ruidoso» pone a todos.
20) Lista de verificación antes de la venta
- Contrato: 'event _ id', 'partition _ key', 'seq', 'event _ type. vN ', firma HMAC y timestamp.
- Remitente: outbox, serialización por clave, retrés con backoff + jitter, TTL, DLQ y redrive.
- Destinatario: entrada rápida en inbox + 2xx; Tratamiento idempotente; DLQ local.
- Seguridad: TLS, firmas, anti-replay, dual-secret, rotación.
- Cuotas/límites: fair-queue per tenant/endpoint, respeto a 'Retry-After'.
- Reconcile API y checkpoints; documentación para suscriptores.
- Observabilidad: p95/hilos/errores/DLQ, seguimiento por 'event _ id'.
- Versificación de eventos y política de evolución de esquemas.
- Playbooks de incidentes y «botón» de pausa/descongelación global.
Conclusión
Un webhooks confiable es un protocolo en la parte superior de HTTP en lugar de simplemente «POST con JSON». Un contrato claro (ID, clave de orden, firma), idempotencia, retraídas con jitter, cola justa y playbucks bien ajustados convierten el «best-case» en un mecanismo de entrega predecible y medible. Construya at-least-once + orden por clave + reconcile, y el sistema sobrevivirá tranquilamente a la red, picos de carga y errores humanos.