At-Most-Once Delivery
Qué es
At-most-once delivery restringe la entrega de una unidad lógica a cero o una vez dentro de una frontera definida. No exige que se entregue: la ausencia de duplicados es compatible con pérdida.
Si dos publicaciones usan IDs distintos para la misma intención, cada una puede entregarse una vez y producir, juntas, un efecto duplicado. Delivery Guarantees separa identidad lógica, intentos y fronteras.
Qué problema resuelve
En algunos flujos repetir trabajo es más costoso que perderlo o detectarlo después. En otros, una muestra pierde utilidad tan rápido que recuperar una entrega vieja agrega latencia sin valor.
At-most-once permite aceptar explícitamente ese intercambio. Restringe la repetición y renuncia a garantizar progreso para cada unidad; no es una forma general de conseguir ejecución confiable.
Cómo funciona
Un transporte puede retirar una unidad de sus pendientes al enviarla, sin mantener la responsabilidad de reenviarla hasta obtener confirmación. Si se pierde durante la transferencia, no vuelve a intentarla. AMQP 1.0 describe una modalidad de at-most-once donde el emisor da la transferencia por terminada antes de enviarla y deja de conservar estado para retransmitir. OASIS AMQP 1.0: Transport, Message Delivery.
Otra política confirma la recepción antes de procesar. Puede evitar reentregas en la frontera broker-consumidor, pero un crash posterior pierde el trabajo:
recibir m → confirmar recepción → crash → efecto nunca aplicadoLa confirmación automática en RabbitMQ ilustra esa ventana: el broker da la entrega por realizada sin esperar que termine el handler. RabbitMQ: automatic acknowledgements.
No basta con desactivar reintentos en un cliente y asumir que desaparecieron todos los duplicados. Hay que revisar proxies, bibliotecas, broker y productores independientes. El contrato aplica a la frontera declarada.
En una capa de aplicación también puede buscarse at-most-once execution mediante una marca durable de «intento consumido» antes de ejecutar. Es otra frontera: puede suprimir intentos aunque el transporte reentregue. Si el proceso cae entre la marca y el efecto, no completa trabajo útil. El registro debe sobrevivir al crash y coordinar la concurrencia; una marca en memoria no basta.
Ejemplo
Un sensor publica una posición cada segundo. Se tolera perder una muestra y la siguiente la reemplaza. El consumidor descarta muestras vencidas en lugar de reconstruir un historial completo. At-most-once puede ser apropiado si el canal evita repetir la misma muestra en el punto de entrega y las pérdidas son aceptables.
Cambiar ese flujo a instrucciones «incrementar saldo» altera el requisito. Si se pierde una instrucción, la siguiente no corrige el saldo. Tener cero duplicados no hace correcta la política para ese dominio.
Cuándo usarlo
Puede servir para telemetría prescindible, estados transitorios o notificaciones cuya omisión tiene una consecuencia aceptada y una vía de recuperación independiente.
También puede ser una decisión conservadora ante una operación incierta: no reintentar hasta consultar o reconciliar el estado. Debe reconocerse que quizás la intención no se ejecutó; no es una garantía de éxito.
Cuándo NO usarlo
No es apropiado cuando todas las unidades aceptadas deben producir un efecto y no existe recuperación de pérdidas. Tampoco garantiza «un solo cobro por intención» si esa intención puede entrar con varias identidades o por otras vías.
Si se necesitan progreso y protección contra duplicados, conviene analizar at-least-once combinado con idempotency o deduplication, en lugar de sacrificar silenciosamente la entrega.
Trade-offs y alternativas
Evitar estado pendiente y reentregas puede reducir costos y latencia, pero la etiqueta por sí sola no asegura menor complejidad: una implementación con deduplicación durable sigue necesitando almacenamiento y coordinación.
Las ventanas de retención y los reinicios importan si la promesa depende de recordar que una unidad ya pasó. Al olvidar ese registro, una repetición tardía puede quedar fuera de la protección.
Errores comunes: equiparar envío con éxito, interpretar timeout como ausencia de efecto y usar un ack temprano como solución al Dual-Write Problem. El ack temprano solo cambia qué ventana de falla se acepta.
Relaciones
- at-least-once conserva reintentos a costa de posibles duplicados.
- delivery-guarantees permite declarar la frontera y la identidad de cada unidad.
- deduplication puede limitar intentos en una capa distinta de la entrega.
- idempotency permite repetir sin multiplicar el efecto relevante.
- exactly-once-semantics añade progreso y precisa qué se confirma exactamente una vez.