Con una cuenta desbloqueas el cuestionario, el foro de dudas y el tutor con IA de esta lección, además del registro de progreso y el certificado verificable al terminar «Sistemas Distribuidos para Misiones Espaciales».
At-least-once es la realidad, exactly-once es un mito práctico
La red falla. Los consumidores se caen a mitad de procesar un evento. Un webhook puede entregarse, ser procesado con éxito por el consumidor, y aun así el hub nunca reciba la confirmación (la respuesta HTTP se pierde en la red) — así que el hub, razonablemente, reintenta. El resultado inevitable es que un sistema distribuido honesto entrega eventos al menos una vez (at-least-once), nunca exactamente una vez. Prometer exactly-once de extremo a extremo es, en la práctica, un mito: lo que sí se puede lograr es que procesar el mismo evento dos veces tenga el mismo efecto que procesarlo una sola vez. A eso se le llama idempotencia, y es la herramienta real (no la promesa imposible) que resuelve el problema.
Idempotencia: procesar dos veces = procesar una
Un consumidor idempotente lleva un registro de los id (UUID) de los eventos que ya procesó, y si recibe el mismo id de nuevo, simplemente lo descarta sin duplicar ningún efecto (una fila en la base de datos, una notificación enviada, una reserva creada). La deduplicación por UUID funciona bien cuando el productor genera un identificador nuevo por evento — pero hay un caso más sutil: cuando el mismo hecho del mundo real podría, por un bug o una condición de carrera, generar dos eventos con dos UUIDs aleatorios distintos. Para ese caso se usa un id derivado determinista, típicamente un UUID versión 5 (basado en un hash, no en aleatoriedad): si la clave de entrada (por ejemplo, norad_id + inicio_utc del pase) es la misma, el uuid5 calculado es siempre el mismo, sin importar cuántas veces o desde qué proceso se calcule. Esto garantiza que el mismo hecho produzca el mismo id, incluso si dos publicaciones independientes lo generaron por error.
Reintentos con backoff exponencial determinista
Cuando la entrega de un webhook falla, el hub no reintenta inmediatamente (eso solo agravaría un problema transitorio de carga) ni espera un tiempo aleatorio (eso dificulta depurar). Usa backoff exponencial determinista: el intento número n espera base · 2^(n-1) segundos, hasta un tope máximo de intentos. Con una base de 30 segundos, la tabla de espera es:
Intento
Fórmula
Espera
1
30⋅20
30 s
2
30⋅21
60 s
3
30⋅22
120 s
4
30⋅23
240 s
5
30⋅24
480 s (≈ 8 min)
6
30⋅25
960 s (≈ 16 min)
Que la fórmula sea determinista (no aleatoria, ni siquiera con "jitter") importa para la depuración: al ver un log de reintentos, un ingeniero puede calcular exactamente cuándo debería haber ocurrido el siguiente intento, y una desviación es una señal real de un problema, no ruido esperado del propio mecanismo de reintento.
Dead-letter: lo que nunca pudo entregarse no se pierde
Tras agotar el número máximo de intentos (seis, en la tabla anterior), el evento no se descarta: se mueve a una cola de dead-letter, donde queda apartado, visible y disponible para un reintento manual una vez que se resuelve la causa raíz (el consumidor estaba caído, tenía un bug, cambió de URL sin avisar). La cola de dead-letter es la diferencia entre "perdimos un evento sin darnos cuenta" y "sabemos exactamente qué evento no pudo entregarse, por qué, y podemos decidir reintentarlo cuando corresponda".
El cursor es responsabilidad del consumidor
En el modo de polling (lección 3), el cursor (seq hasta donde el consumidor ya procesó) no lo gestiona el hub por el consumidor: es responsabilidad explícita de cada consumidor persistirlo y reanudar desde ahí tras un reinicio. Si un consumidor pierde su cursor y vuelve a empezar desde cero, reprocesará eventos ya vistos — razón de más para que ese mismo consumidor sea idempotente. Idempotencia y gestión del cursor son, en la práctica, dos mitades de la misma disciplina de resiliencia.
Ejemplo: doble publicación resuelta por uuid5
Supongamos que, por una condición de carrera en la estación terrena, el mismo pase de norad_id=25544 a las 2026-07-29T14:00:00Z se publica dos veces por error, cada vez con un UUID aleatorio distinto generado por el productor. Si el consumidor deduplicara solo por ese UUID (versión 4, aleatorio), procesaría el pase dos veces — dos filas, dos notificaciones. Si en cambio el sistema deriva el id del evento como uuid5(namespace, "25544|2026-07-29T14:00:00Z"), ambas publicaciones producen el mismo id derivado, y el consumidor idempotente descarta la segunda automáticamente: el mismo hecho, sin importar cuántas veces se anunció, se procesa una sola vez.
Repasa el vocabulario de esta lección:
1 de 5
¿Por qué un sistema distribuido honesto garantiza entrega "at-least-once" y no "exactly-once"?
El laboratorio de esta lección simula un flujo de entregas con probabilidad de fallo ajustable: observa la barra de backoff exponencial en acción, mueve un evento a dead-letter tras agotar los seis intentos, y compara un consumidor idempotente contra uno que no lo es cuando el mismo evento se publica dos veces.
Laboratorio: Simulador de reintentos, backoff y dead-letter
8. Custodia de datos: un backup no probado no es backup