Inbox Pattern

Qué es

Inbox Pattern conserva un registro durable de mensajes recibidos y su procesamiento en la frontera del consumidor. En su caso fuerte, la identidad del mensaje y el efecto local se confirman en la misma transacción. Así, una repetición puede reconocerse sin aplicar otra vez ese efecto.

Aquí «inbox» designa ese registro de procesamiento. Algunas implementaciones también guardan el payload recibido para procesarlo posteriormente. Recibido, pendiente y procesado son estados distintos: persistir un mensaje no demuestra que su intención esté cumplida.

El patrón materializa una estrategia de deduplication para un consumidor. No sustituye el contrato de idempotency, la identidad del productor ni la recuperación de efectos fuera del store.

Qué problema resuelve

Con at-least-once, el consumidor puede confirmar un cambio y caer antes de que el broker registre su acknowledgement. La reentrega es correcta desde el transporte, pero repetir un decremento de stock puede ser incorrecto para el negocio.

Guardar «visto» antes del efecto evita repeticiones solo al precio de poder perder ese efecto. Guardarlo después permite repetir un efecto ya confirmado. El inbox debe coordinar ambos hechos para no elegir entre esas dos ventanas de fallo.

Caso fuerte: una transacción local

BEGIN LOCAL TRANSACTION
  insert unique (consumer-scope, message-id)
  modify local business state
COMMIT
acknowledge broker delivery

La inserción y el cambio deben abortar juntos si algo falla. Si la identidad ya corresponde a un procesamiento confirmado, la entrega puede reconocerse como repetida sin ejecutar otra vez el efecto. Un error de almacenamiento o una transacción concurrente todavía no resuelta no deben confundirse con esa evidencia.

Richardson documenta esta técnica con una clave compuesta por subscriber e ID de mensaje en una tabla de procesados. El nombre de la tabla no aporta la garantía: la unicidad y la transacción conjunta sí. Idempotent Consumer: processed-message transaction.

Uniqueness y concurrency

Consultar primero y luego insertar no coordina dos workers. Ambos pueden leer «no existe» y ejecutar el efecto. Una restricción de unicidad o mecanismo equivalente del store debe arbitrar la misma intención entre instancias.

query inbox
execute effect
save inbox

Si estas acciones se confirman separadamente, también hay una ventana entre ejecutar y guardar. Incluso con unicidad al final, el efecto ya podría haberse repetido. El conflicto debe bloquear o abortar el intento antes de confirmar otro efecto, dentro de la frontera común.

La identidad usa un ámbito de consumidor lógico, no el nombre de la máquina. Todas las instancias que realizan la misma función comparten esa protección; dos consumidores con efectos diferentes pueden procesar legítimamente el mismo evento. Puede incluirse un namespace de productor cuando los IDs no son únicos entre productores.

Message identity y ejemplo

Order Service publica evt-42-created, con pedido 42 y una unidad. Inventory Consumer usa el ámbito inventory-reservation. Inserta esa combinación y reserva la unidad en Inventory DB. Si se confirma, la reentrega encuentra la identidad y no reserva otra unidad.

El ID del pedido no basta para todos sus mensajes: creación y cancelación son intenciones distintas. La identidad de aplicación debe conservarse en republicaciones y replay. Si el mismo ID aparece con contenido incompatible, tratarlo como duplicado sin más puede ocultar un error; el contrato debe detectar o resolver esa incompatibilidad.

FalloEstado durable esperado
Crash antes del commit localNi procesamiento registrado ni efecto confirmado.
Falla el cambio de negocioLa marca del inbox también aborta.
Commit correcto, acknowledgement perdidoInbox y efecto existen; se reconoce la reentrega.
Commit con respuesta local inciertaConsultar o repetir de forma coordinada para resolver el resultado.

Inbox recibido y procesamiento diferido

Guardar payload y confirmar la recepción permite transferir responsabilidad desde el broker a almacenamiento local, si el contrato admite esa transferencia. Después, un worker procesa los pendientes y registra efecto más estado final atómicamente. Ese worker necesita progreso durable, recuperación de claims y vigilancia de backlog.

Reconocer un mensaje como ya recibido no autoriza a descartar su tarea pendiente. El acknowledgement temprano solo es válido si la aplicación mantiene una obligación durable equivalente. MassTransit distingue inbox de seguimiento de recibidos y outbox de mensajes producidos; el diseño conceptual debe conservar ambas responsabilidades. Consumer Outbox.

Retención, replay y cleanup

Borrar una identidad de procesamiento habilita un nuevo efecto si el mensaje reaparece. El horizonte debe considerar retención del broker, republicaciones del relay, DLQ, restauraciones y replay operacional. No existe un plazo universal seguro si se permite volver a enviar mensajes de cualquier edad.

Un replay para recuperar fallos mantiene la identidad y respeta los procesamientos existentes. Reconstruir otra proyección puede necesitar un consumidor o ámbito de versión diferente y almacenamiento separado. Borrar todas las marcas para «hacer funcionar» el replay del mismo inventario puede repetir reservas reales.

Limpiar payloads y conservar identidades compactas puede reducir costo, siempre que permanezca la evidencia necesaria. Medir conflictos, pendientes antiguos y fallos de procesamiento ayuda a distinguir duplicados esperados de un consumidor detenido. Algunas implementaciones tienen una ventana configurable de detección y borran después sus registros; esa ventana limita la protección. MassTransit: inbox retention.

Frontera transaccional y límites

Una API de pagos, un correo o una escritura en otro store no participan automáticamente. Confirmar el inbox y después enviar un correo puede perder el envío; enviarlo antes puede duplicarlo. Para producir otro mensaje, transactional-outbox permite conservar su intención en el mismo commit local. El efecto externo final sigue necesitando cooperación o recuperación adicional.

Usar inbox cuando los efectos locales necesitan protección durable ante duplicados y pueden coordinarse con su identidad. No es obligatorio para un idempotent-consumer cuya operación ya converge por semántica. Tampoco garantiza orden, entrega o exactly-once general: garantiza, bajo los supuestos descritos y mientras se retiene la identidad, que una repetición no confirma de nuevo el efecto local protegido.