En algún lugar de todo contrato de integración hay una frase como «cada pedido se transmitirá exactamente una vez». Nunca ha sido cierta, ni una sola vez. La red se particionó a mitad de la solicitud y el emisor reintentó; la cola reentregó el mensaje tras el fallo de un consumidor; un operario de almacén pulsó el botón de exportar dos veces. Los duplicados no son un modo de fallo - son el clima.
Claves de idempotencia, elegidas con cuidado
La solución es antigua y poco vistosa: cada mensaje lleva una clave, y el procesamiento no hace nada cuando esa clave ya se ha visto. Las decisiones interesantes están todas en la elección de la clave.
- Las claves naturales superan a las generadas. Una sincronización de pedidos con clave
order_id + statussobrevive a que el emisor regenere su outbox; un UUID aleatorio acuñado en el momento del envío no. - Ámbito de la clave al efecto, no al mensaje. Si un mensaje crea una factura y reserva stock, esos son dos efectos con dos claves - de lo contrario, un fallo parcial te deja sin poder reintentar con seguridad ninguno de los dos.
- Caduca las claves de forma deliberada. Un almacén de deduplicación que crece indefinidamente es una bomba de relojería; uno que caduca demasiado pronto reintroduce duplicados. Por defecto usamos 3 veces el horizonte máximo de reintento del emisor.
La prueba que importa
Toda integración que entregamos pasa lo que internamente llamamos la prueba del doble toque: reproducir todo el registro de mensajes de ayer contra el sistema de hoy, dos veces, y comparar el estado resultante. Si algo cambió en la segunda pasada, el pipeline no está terminado.
Es una prueba brutal y falla constantemente durante el desarrollo - por marcas de tiempo, por campos de auditoría «creado por», por contadores de secuencia. Cada fallo es un lugar donde una reentrega a las 3 de la madrugada habría corrompido datos silenciosamente. Sale mucho más barato detectarlo en desarrollo que en producción.