Tema representativo de entrevista

Entrevista de diseño de sistemas: ¿Cómo construirías una entrega de webhooks protegida contra ataques de reproducción (replay-safe)?

Diseño de sistemasDifícil
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

Diseña una plataforma de webhooks que entregue eventos de pago a miles de endpoints de clientes. Debe tolerar tiempos de espera agotados (timeouts), entregas duplicadas, ataques de reproducción (replay attacks) y un suscriptor que esté fuera de línea durante horas.

Planteamiento y contexto

Un productor de eventos emite eventos de negocio hacia endpoints HTTP externos. La plataforma debe preservar los eventos de forma duradera, entregarlos al menos una vez (at-least-once), hacer que los duplicados sean seguros y proporcionar a cada inquilino controles claros de reintento y reproducción (replay).

Qué evalúa el entrevistador

  • Elegir una garantía de entrega explícita en lugar de prometer "exactamente una vez" (exactly-once) a través de HTTP.
  • Separar la ingesta, la programación, los intentos de entrega y los efectos secundarios del lado del suscriptor.
  • Combinar IDs de eventos, marcas de tiempo en las firmas, políticas de reintento, manejo de colas de mensajes no entregados (dead-letter) y equidad (fairness).

Preguntas aclaratorias antes de responder

  • ¿Cuál es el volumen de eventos, el tamaño de la carga útil (payload), la cantidad de endpoints y el retraso máximo de entrega permitido?
  • ¿Se requiere ordenamiento por inquilino, por endpoint o no se requiere en absoluto?
  • ¿Pueden los consumidores procesar un evento de forma idempotente y cuánto tiempo debe conservarse el estado de deduplicación?
  • ¿Qué requisitos de reproducción, eliminación, privacidad y auditoría aplican a las cargas útiles?

Estructura de respuesta en 30 segundos

Persistiría un evento inmutable con un ID estable, pondría en cola los intentos de entrega y respondería rápidamente desde la ruta de recepción. Los workers firman la carga útil sin procesar (raw payload) con una marca de tiempo, aplican un retroceso exponencial (exponential backoff) con variación aleatoria (jitter) y clasifican las respuestas en fallas reintentables y terminales. Los consumidores deduplican por ID de evento antes de aplicar efectos secundarios. Un programador por inquilino, un disyuntor (circuit breaker) y una cola de mensajes no entregados (dead-letter queue) evitan que un endpoint fuera de línea afecte a los demás; la reproducción genera un nuevo intento sin cambiar la identidad original del evento.

Análisis detallado paso a paso

1. Hacer que el evento sea duradero primero

Escribe el evento de negocio y su registro de entrega de forma transaccional o mediante un outbox confiable. El registro de entrega almacena el inquilino, el endpoint, el ID del evento, el conteo de intentos, la hora del próximo intento y el estado. Se espera que ocurra una caída del sistema después del envío pero antes de registrar el éxito; esto produce otro intento, por lo que los consumidores deben tolerar duplicados.

2. Verificar y firmar de forma segura

Firma los bytes exactos sin procesar junto con una marca de tiempo, e incluye el ID del evento en el mensaje firmado. Stripe documenta firmas con marca de tiempo para limitar los ataques de reproducción. Los consumidores verifican la firma antes de parsear, rechazan marcas de tiempo fuera de una tolerancia configurada y rotan secretos sin invalidar inesperadamente un evento que ya esté en cola.

3. Clasificar respuestas y reintentar

Trata los tiempos de espera de red, las fallas de conexión y ciertas respuestas 5xx como reintentables. Trata la autenticación malformada, las versiones de eventos no compatibles y la mayoría de las respuestas 4xx como terminales o sujetas a revisión por un operador. Utiliza retroceso exponencial con jitter, una ventana máxima de intentos y un estado de dead-letter. No reintentes indefinidamente contra un endpoint que falla de forma permanente.

4. Hacer explícitos los duplicados y las reproducciones

El consumidor almacena los IDs de eventos procesados con una restricción de unicidad duradera y confirma ese registro junto con el efecto secundario de negocio cuando sea posible. Una reproducción manual reutiliza la carga útil inmutable del evento y registra un nuevo intento de entrega, mientras que los paneles de control distinguen entre entrega original, reintento automático y reproducción por operador. Los efectos secundarios de tipo exactly-once requieren una transacción en el consumidor; la red por sí misma solo proporciona entrega at-least-once.

5. Escalar sin inanición (starvation) entre inquilinos

Particiona las colas por inquilino o endpoint, aplica límites de concurrencia y de tasa por inquilino, y reserva capacidad para inquilinos saludables. Un circuit breaker pausa un endpoint después de fallas repetidas. Las métricas deben incluir la antigüedad del evento pendiente más antiguo, la latencia de éxito, la distribución de intentos, la tasa de duplicados, las fallas de firma y el volumen de dead-letter.

Respuesta de ejemplo de alta calidad

“Persistiría cada evento con un ID estable antes de programar la entrega y prometería una semántica at-least-once. Cada intento firma la carga útil sin procesar con un ID de evento y una marca de tiempo. Los workers clasifican los timeouts y las respuestas 5xx para reintentos con jitter, mientras que las respuestas 4xx terminales se mueven a dead letter. Los consumidores deduplican los IDs de eventos dentro de su transacción de efectos secundarios. Las colas por inquilino, los circuit breakers, las alertas basadas en antigüedad y un flujo de reproducción evitan que un suscriptor fuera de línea prive de recursos a otros y hacen que la recuperación sea auditable.”

Errores comunes

  • Prometer exactly-once sobre HTTP → las caídas del sistema crean resultados ambiguos → establecer at-least-once y requerir consumidores idempotentes.
  • Firmar JSON parseado en lugar de bytes sin procesar → formatos equivalentes pueden fallar la verificación → firmar y verificar los bytes exactos de la carga útil.
  • Reintentar cada 4xx indefinidamente → los fallos permanentes consumen capacidad → clasificar errores y enviar a dead-letter los casos terminales.
  • Usar una única cola global → un solo inquilino puede dejar sin recursos a todos → particionar, aplicar rate limiting y reservar capacidad por inquilino.

Preguntas de seguimiento y respuestas

¿Cuánto tiempo debe conservarse el estado de deduplicación?

Al menos tanto como duren los reintentos automáticos y la ventana de reproducción admitida, más un margen de seguridad. Si la reproducción puede ocurrir meses después, mantén un registro de eventos compacto o exige que los consumidores elijan explícitamente una identidad de reproducción y una política de retención.

¿Debe una reproducción recibir un nuevo ID de evento?

Por lo general, no: la identidad del evento de negocio se mantiene estable, mientras que el intento de entrega obtiene su propio ID y registro de auditoría. Esto permite que los consumidores reconozcan la reproducción como el mismo evento y evita efectos de negocio duplicados.

¿Qué sucede si un consumidor devuelve 200 antes de confirmar su efecto secundario?

El contrato del consumidor se rompe; la plataforma no puede inferir el éxito a partir de la respuesta. Los consumidores deben enviar la confirmación tras una aceptación duradera, utilizar una bandeja de entrada (inbox) o un registro de deduplicación transaccional y exponer mecanismos de conciliación para resultados ambiguos.

Fuentes públicas

Preguntas relacionadas

Herramienta de entrevista relacionada

Usa Resolver para una respuesta de diseño de sistemas

Aclara primero los requisitos y luego avanza a través de la escala, la arquitectura, la elección de componentes y las compensaciones (trade-offs).

Ver la herramienta