Saltar a contenido

Message Expiration

1. Nombre del Patrón

  • Nombre oficial: Message Expiration
  • Categoría: Message Construction (Construcción de Mensajes)
  • Traducción contextual: Expiración de Mensaje / Tiempo de Vida del Mensaje (TTL)

2. Resumen Ejecutivo

Message Expiration es un patrón de construcción de mensajes que permite establecer un tiempo de vida (TTL — Time To Live) en un mensaje, de modo que si no es consumido y procesado dentro de ese período, el sistema de mensajería lo descarta automáticamente o lo redirige a un canal de mensajes muertos (Dead Letter Channel).

El patrón existe porque en sistemas distribuidos, la información tiene una ventana de validez temporal. Un precio de cotización bursátil tiene validez de milisegundos. Una reserva de hotel pendiente de confirmación tiene validez de 15 minutos. Una alerta de inventario bajo tiene validez de horas. Procesar un mensaje fuera de su ventana de validez no solo es inútil sino potencialmente dañino: puede ejecutar una orden a un precio obsoleto, confirmar una reserva ya cancelada o generar una alerta sobre un problema ya resuelto.

Aparece en todo sistema de mensajería en producción. TTL es un parámetro de configuración estándar en RabbitMQ (per-message TTL y per-queue TTL), Azure Service Bus (TimeToLive), Amazon SQS (MessageRetentionPeriod y VisibilityTimeout), y aunque Kafka no implementa TTL per-message, el concepto de expiración se gestiona mediante retention.ms a nivel de topic y timestamps de records. En la práctica, configurar TTL en colas y mensajes es una actividad operacional que se realiza en toda implementación enterprise de mensajería.


3. Definición Detallada

Propósito

El propósito de Message Expiration es garantizar que los mensajes que no son procesados dentro de su ventana de validez temporal sean automáticamente eliminados del sistema de mensajería, evitando que consumidores procesen información obsoleta y que mensajes antiguos acumulen en colas consumiendo recursos indefinidamente.

Lógica Arquitectónica

Message Expiration introduce una restricción temporal en el ciclo de vida del mensaje:

  • El productor establece un TTL al crear el mensaje, basado en la semántica de negocio del dato (cuánto tiempo es válida esta información).
  • El broker verifica el TTL de cada mensaje antes de entregarlo al consumidor. Si el mensaje ha expirado, lo descarta o lo mueve a una Dead Letter Queue.
  • El consumidor tiene la garantía de que los mensajes que recibe están dentro de su ventana de validez (si el broker implementa TTL correctamente) o puede verificar el timestamp del mensaje y decidir si procesarlo.

El TTL se puede establecer de múltiples formas:

  • Absolute expiration time: timestamp absoluto después del cual el mensaje es inválido (ej. "2026-04-07T14:30:00Z").
  • Relative TTL: duración relativa desde la creación del mensaje (ej. "30 segundos").
  • Per-queue TTL: todos los mensajes en una cola heredan el mismo TTL configurado en la cola.
  • Per-message TTL: cada mensaje tiene su propio TTL, independiente de la cola.

Principio de Diseño Subyacente

El principio fundamental es información temporalmente acotada. No toda la información tiene la misma vida útil. Un mensaje que representa "el precio actual de AAPL es $187.42" tiene una vida útil de milisegundos en un sistema de high-frequency trading. Un mensaje que representa "el cliente X cambió su dirección de facturación" tiene una vida útil de días o semanas. El TTL codifica esta semántica temporal directamente en el mensaje, permitiendo que el sistema de mensajería actúe automáticamente sobre ella.

Problema Estructural que Resuelve

Sin Message Expiration, los mensajes permanecen en las colas indefinidamente hasta que un consumidor los procese. Esto genera:

  • Procesamiento de datos obsoletos: un consumidor que se recupera de un fallo puede procesar mensajes de horas o días de antigüedad, ejecutando acciones sobre información que ya no es válida.
  • Acumulación de mensajes: si un consumidor se detiene o se atrasa, los mensajes se acumulan en la cola sin límite, consumiendo memoria y disco del broker.
  • Poison messages temporales: mensajes que ya no tienen valor de negocio pero siguen circulando por el sistema, consumiendo recursos de procesamiento.

Contexto en el que Emerge

Message Expiration emerge en todo sistema de mensajería donde:

  • La información transmitida tiene una ventana de validez finita.
  • Los consumidores pueden experimentar períodos de indisponibilidad o atraso.
  • El procesamiento de datos obsoletos tiene consecuencias negativas (financieras, operacionales o de experiencia de usuario).
  • Las colas pueden acumular mensajes más rápido de lo que se consumen durante picos de carga.

Por Qué No Es Trivial

Las decisiones alrededor de Message Expiration tienen consecuencias significativas:

  • Determinar el TTL correcto: un TTL demasiado corto descarta mensajes que aún son procesables y útiles. Un TTL demasiado largo no cumple su propósito de proteger contra datos obsoletos. El TTL correcto depende de la semántica del negocio, la latencia esperada del sistema y la tolerancia al procesamiento tardío.
  • Qué hacer con mensajes expirados: ¿descartarlos silenciosamente? ¿Moverlos a Dead Letter Queue para inspección? ¿Generar una alerta? ¿Notificar al productor? La estrategia de manejo de mensajes expirados afecta la observabilidad y la capacidad de diagnóstico.
  • Interacción con retries: si un mensaje se reintenta múltiples veces y cada retry consume parte del TTL, el mensaje puede expirar durante un retry legítimo. ¿El TTL debe reiniciarse en cada retry o es absoluto desde la creación?
  • TTL del productor vs. TTL del broker: ¿quién decide el TTL? Si el productor establece un TTL de 5 segundos pero la cola tiene un TTL de 1 hora, ¿cuál prevalece? La semántica de precedencia varía entre brokers.
  • Clock skew: en sistemas distribuidos, los relojes del productor y del broker pueden no estar sincronizados. Un TTL basado en timestamp absoluto puede comportarse incorrectamente si hay clock skew significativo.

Relación con Sistemas Distribuidos y Mensajería

En sistemas distribuidos, la noción de "tiempo" es inherentemente compleja (CAP theorem, relojes lógicos, vector clocks). Message Expiration usa el tiempo físico (wall clock) para tomar decisiones de descarte, lo cual introduce una dependencia en la sincronización de relojes. En la práctica, NTP mantiene los relojes suficientemente sincronizados para TTLs de segundos o minutos, pero para TTLs de milisegundos en sistemas de alta frecuencia, el clock skew puede ser significativo.


4. Problema que Resuelve

El Problema Antes del Patrón

Sin Message Expiration, cuando un consumidor se detiene durante 2 horas y luego se reinicia, encuentra en la cola todos los mensajes acumulados durante esas 2 horas. Los procesa todos, incluyendo aquellos cuya información ya es obsoleta. En un sistema de trading, esto significa ejecutar órdenes a precios de hace 2 horas. En un sistema de alertas, significa generar alertas sobre situaciones que ya se resolvieron. En un sistema de reservas, significa intentar confirmar reservas que ya expiraron.

Síntomas del Problema

  • Consumidores que procesan mensajes antiguos después de un reinicio o recovery, causando acciones incorrectas.
  • Colas que crecen indefinidamente cuando los consumidores no pueden mantener el ritmo, consumiendo toda la memoria disponible del broker.
  • Alertas duplicadas o anacróncias: el sistema genera alertas sobre problemas que ya se resolvieron porque los mensajes de alerta no expiraron.
  • Ejecución de acciones sobre datos obsoletos: confirmaciones de precios caducados, envío de notificaciones sobre eventos ya pasados, procesamiento de solicitudes ya canceladas.
  • Métricas de latencia engañosas: el tiempo "en cola" de mensajes viejos distorsiona las métricas de latencia promedio del sistema.

Impacto Operativo y Arquitectónico

Sin expiración de mensajes:

  • Los brokers necesitan dimensionarse para el peor caso de acumulación: si un consumidor puede estar caído 24 horas, la cola debe poder almacenar 24 horas de mensajes. Esto incrementa significativamente los costos de infraestructura.
  • Los consumidores necesitan implementar su propia lógica de verificación de antigüedad del mensaje, lo cual es error-prone y no estándar.
  • Los operadores no tienen un mecanismo automático para proteger al broker de overflow de colas.
  • La recuperación de fallos se complica: un consumidor que se reinicia debe procesar un backlog de mensajes obsoletos antes de alcanzar los mensajes actuales.

Riesgos Si No Se Implementa Correctamente

  • TTL demasiado corto: mensajes legítimos descartados durante picos de carga normales. El consumidor pierde datos que debería haber procesado.
  • TTL demasiado largo: mensajes obsoletos procesados después de un fallo, causando acciones sobre datos inválidos.
  • Expiración silenciosa sin DLQ: mensajes descartados sin registro ni auditoría. Si un mensaje contenía una orden financiera que expiró, ¿quién se entera? ¿Cómo se concilia?
  • Clock skew: si el reloj del productor está adelantado, los mensajes pueden expirar prematuramente en el broker. Si está atrasado, los mensajes viven más de lo esperado.
  • Interacción con retries: un mensaje que falla en el primer intento y se reencola para retry puede expirar durante el retry, perdiendo el mensaje cuando era recuperable.

Ejemplos Reales

  • Trading / Mercados Financieros: una cotización de mercado (market data quote) para un par de divisas EUR/USD tiene una validez de 200 milisegundos. Si el consumidor (motor de pricing) no procesa la cotización en ese tiempo, debe descartarla porque el mercado ya se movió. El sistema de market data publica cotizaciones con TTL de 200ms; el broker descarta las no consumidas.
  • E-commerce: una oferta flash de "envío gratis en los próximos 30 minutos" genera mensajes de notificación push con TTL de 30 minutos. Si el servicio de notificaciones está saturado y no entrega la notificación en 30 minutos, no tiene sentido entregarla porque la oferta ya expiró.
  • Healthcare: un resultado crítico de laboratorio (valor de glucosa peligrosamente alto) genera una alerta con TTL de 15 minutos. Si el sistema de alertas no la entrega al médico en 15 minutos, el protocolo de escalación manual ya se habrá activado y la alerta electrónica sería redundante y confusa.
  • IoT / Telemetría: una lectura de sensor de temperatura tiene validez de 60 segundos. Lecturas anteriores son obsoletas porque la siguiente lectura ya las reemplazó.

5. Contexto de Aplicación

Cuándo Usarlo

  • Cuando la información del mensaje tiene una ventana de validez temporal finita y procesarla fuera de esa ventana es inútil o dañino.
  • Cuando se necesita proteger al broker de acumulación ilimitada de mensajes en caso de consumidores lentos o caídos.
  • Cuando los consumidores no deben procesar un backlog de mensajes obsoletos después de un reinicio.
  • Cuando el dominio de negocio tiene requisitos temporales explícitos: precios con validez, ofertas con deadline, alertas con escalación temporal, SLAs de procesamiento.
  • En todo sistema de producción como mecanismo defensivo básico: incluso cuando no hay un requisito de negocio explícito, un TTL razonable (horas o días) protege contra acumulación infinita.

Cuándo No Usarlo

  • Cuando cada mensaje debe procesarse independientemente de su antigüedad (ej. eventos de auditoría, transacciones financieras que requieren procesamiento completo sin pérdida).
  • Cuando el sistema implementa event sourcing y cada evento es parte del log inmutable que debe preservarse.
  • Cuando la semántica del mensaje es atemporal (ej. un comando de "crear usuario" es válido independientemente de cuándo se procese).

Precondiciones

  • El broker soporta TTL per-message o per-queue.
  • Los relojes del productor y del broker están razonablemente sincronizados (NTP).
  • Existe una estrategia definida para mensajes expirados (discard, DLQ, alerta).
  • El TTL de cada tipo de mensaje está definido como parte del contrato de mensajería.

Restricciones

  • El TTL mínimo práctico depende de la resolución del timer del broker y del clock skew del sistema.
  • Per-message TTL en algunos brokers (RabbitMQ) solo se verifica al momento de entrega al consumer, no proactivamente. Esto significa que mensajes expirados pueden permanecer en la cola hasta que llegan al head.
  • TTL y retries pueden entrar en conflicto: un mensaje con TTL corto puede no tener oportunidad de reintentarse.

Dependencias

  • Sincronización de relojes (NTP) entre productor y broker.
  • Dead Letter Queue configurada si se desea preservar mensajes expirados para inspección.
  • Monitoreo de tasa de expiración para detectar problemas de rendimiento del consumidor.

Supuestos Arquitectónicos

  • El procesamiento tardío de un mensaje es peor que no procesarlo (o al menos no significativamente mejor).
  • El productor conoce la ventana de validez de la información que transmite.
  • El sistema puede tolerar la pérdida de mensajes expirados (o los redirige a DLQ para procesamiento alternativo).

Tipo de Sistemas Donde Aparece con Más Frecuencia

  • Sistemas de trading y market data en tiempo real.
  • Sistemas de notificaciones push y alertas con ventana temporal.
  • Sistemas IoT con telemetría de alta frecuencia.
  • Sistemas de reservas y booking con timeouts de confirmación.
  • Sistemas de pricing dinámico con cotizaciones temporales.
  • Cualquier sistema de mensajería en producción (como mecanismo defensivo de protección del broker).

6. Fuerzas Arquitectónicas

Frescura vs. Completitud

La tensión fundamental de Message Expiration es entre procesar solo datos frescos (relevantes) y procesar todos los datos (completitud). Un TTL estricto garantiza que el consumidor solo ve datos actuales, pero acepta la pérdida de mensajes que no se procesaron a tiempo. Un TTL relajado preserva más mensajes pero acepta que algunos pueden estar obsoletos.

Protección del Broker vs. Pérdida de Datos

TTL protege al broker de acumulación infinita, pero cada mensaje expirado es un dato no procesado. En dominios donde cada mensaje es crítico (transacciones financieras), la pérdida por TTL es inaceptable y se necesita un DLQ con procesamiento alternativo. En dominios donde los datos son reemplazables (telemetría de sensores), la pérdida por TTL es aceptable y esperada.

TTL del Productor vs. TTL del Broker/Infraestructura

¿Quién decide el TTL? El productor conoce la semántica de negocio y sabe cuánto tiempo es válida la información. Pero el operador del broker conoce la capacidad de la infraestructura y necesita protegerla de overflow. En la práctica, ambos coexisten: el productor establece un TTL semántico y el broker tiene un TTL máximo de infraestructura. El TTL efectivo es el menor de los dos.

Granularidad: Per-Message vs. Per-Queue

Per-message TTL permite que cada mensaje tenga su propia ventana de validez, lo cual es más preciso pero más complejo de gestionar. Per-queue TTL aplica el mismo TTL a todos los mensajes de la cola, lo cual es más simple pero menos flexible. En RabbitMQ, per-queue TTL es más eficiente porque el broker puede descartar mensajes del head de la cola sin inspeccionar el TTL individual.

Latencia del Consumidor vs. Pérdida de Mensajes

Si el consumidor tiene picos de latencia (GC pauses, degradación de base de datos downstream), un TTL estricto puede causar pérdida de mensajes durante esos picos. Un TTL más relajado tolera los picos pero acepta procesamiento tardío. La calibración del TTL debe considerar la latencia del peor caso del consumidor, no solo la latencia promedio.

Observabilidad vs. Volumen de DLQ

Enviar todos los mensajes expirados a DLQ proporciona máxima observabilidad (se puede analizar qué se perdió y por qué), pero puede generar un volumen enorme en la DLQ si la tasa de expiración es alta. Descartar silenciosamente reduce el volumen pero pierde visibilidad. Un enfoque intermedio es descartar con métrica (conteo de expirados) sin preservar el contenido.


7. Estructura Conceptual del Patrón

Actores o Componentes Involucrados

  1. Productor: crea el mensaje y establece el TTL basado en la semántica de negocio del dato.
  2. Broker / Cola: almacena el mensaje y verifica el TTL antes de entregar al consumidor. Si el mensaje ha expirado, ejecuta la política configurada (discard, DLQ).
  3. Consumidor: recibe solo mensajes no expirados (si el broker verifica TTL) o verifica el timestamp del mensaje y decide si procesarlo (si el broker no verifica TTL activamente).
  4. Dead Letter Queue (opcional): recibe mensajes expirados para inspección, auditoría o procesamiento alternativo.
  5. Sistema de Monitoreo: rastrea la tasa de expiración como indicador de salud del sistema.

Flujo Lógico

flowchart TD
    A([Productor]) --> B[Crear mensaje M\ncon payload P]
    B --> C[Establecer TTL y\ntimestamp de creación]
    C --> D[(Canal / Cola)]
    D --> E[Broker recibe y\nalmacena M con TTL]
    E --> F{Consumidor solicita\nsiguiente mensaje}
    F --> G{M ha expirado?\nnow > creation_time + TTL}
    G -- No expirado --> H[Entregar M al consumidor]
    H --> I[Consumidor procesa P]
    I --> J[Verificar timestamp\ncomo segunda validación]
    J --> K([Fin])
    G -- Expirado --> L{Política de\nexpiración?}
    L -- Descartar --> M[Descartar mensaje]
    L -- DLQ --> N[(Dead Letter Queue)]
    M --> O[Intentar siguiente mensaje]
    N --> O
    O --> F

Responsabilidades

Componente Responsabilidad
Productor Determinar y establecer el TTL correcto basado en la semántica de negocio
Broker Verificar expiración y ejecutar la política (discard o DLQ)
Consumidor Procesar mensajes no expirados; opcionalmente verificar timestamp como segunda validación
DLQ Almacenar mensajes expirados para inspección y auditoría
Monitoreo Rastrear tasa de expiración como indicador de problemas

Interacciones

  • Productor → Broker: envío de mensaje con TTL en headers.
  • Broker → Consumidor: entrega de mensajes no expirados.
  • Broker → DLQ: redirección de mensajes expirados.
  • Monitoreo → Broker: consulta de métricas de expiración.

Contratos Implícitos

  • Formato del TTL: relativo (duración en ms/s) o absoluto (timestamp de expiración).
  • Política de expiración: discard silencioso o DLQ.
  • Precedencia: si hay TTL per-message y per-queue, cuál prevalece.
  • Semántica: el TTL mide desde la creación del mensaje, no desde su encolamiento (la diferencia importa cuando hay latencia de publicación).

Decisiones de Diseño Clave

  1. TTL absoluto vs. relativo: absoluto es más preciso pero sensible a clock skew. Relativo es más simple pero requiere que el broker calcule la expiración.
  2. Per-message vs. per-queue: per-message es más flexible; per-queue es más eficiente y simple de operar.
  3. Política de expiración: discard (simple, sin overhead) vs. DLQ (observable, con overhead de storage).
  4. Verificación activa vs. lazy: ¿el broker verifica TTL proactivamente (timer) o solo al intentar entregar al consumidor (lazy)? La verificación lazy es más eficiente pero los mensajes expirados permanecen en la cola consumiendo espacio.
  5. TTL y retries: ¿el TTL se reinicia en cada retry o es absoluto desde la creación original? Generalmente absoluto, lo cual implica que retries consumen tiempo del TTL.

8. Ejemplo Arquitectónico Detallado

Dominio: Trading / Mercados Financieros — Market Data Quotes

Contexto del Negocio

Una firma de trading electrónico opera un sistema de market data que distribuye cotizaciones de precios de acciones, bonos, divisas y commodities a múltiples consumidores internos: motores de pricing, algoritmos de trading, sistemas de riesgo en tiempo real y dashboards de trading para operadores humanos. La firma procesa cotizaciones de 15 exchanges globales, con un volumen combinado de 2 millones de cotizaciones por segundo en horario de mercado.

Necesidad de Integración

Las cotizaciones de mercado son datos intrínsecamente perecederos. El precio de una acción se mueve continuamente, y una cotización de hace 500 milisegundos ya es potencialmente obsoleta. Los consumidores no deben operar con precios obsoletos: un motor de pricing que usa una cotización de hace 2 segundos puede calcular un precio de derivado incorrecto, y un algoritmo de trading que ejecuta una orden basándose en un precio obsoleto puede incurrir en pérdidas significativas (stale price risk).

Sistemas Involucrados

  1. Market Data Feed Handlers: procesos que reciben datos crudos de cada exchange (FIX protocol, ITCH, OPRA) y los normalizan a un formato interno.
  2. RabbitMQ Cluster (Market Data Bus): broker de mensajería de baja latencia que distribuye cotizaciones normalizadas a los consumidores.
  3. Pricing Engine: servicio que calcula precios de derivados basándose en cotizaciones de subyacentes. Requiere cotizaciones frescas (TTL: 500ms).
  4. Trading Algorithm Engine: servicio que ejecuta estrategias de trading cuantitativo. Requiere cotizaciones muy frescas (TTL: 200ms).
  5. Risk Engine: servicio que calcula exposición de riesgo en tiempo real. Tolera cotizaciones de hasta 5 segundos de antigüedad.
  6. Trading Dashboard: interfaz web para traders humanos. Tolera cotizaciones de hasta 2 segundos de antigüedad.
  7. Dead Letter Queue + Metrics: almacenamiento de cotizaciones expiradas y métricas de expiración para monitoreo de salud del sistema.

Restricciones Técnicas

  • Latencia end-to-end (desde exchange hasta consumidor) debe ser <10 ms en el percentil 99.
  • 2 millones de cotizaciones/segundo en horario pico.
  • Los consumidores no deben procesar cotizaciones más antiguas que su TTL específico.
  • El broker debe manejar la expiración sin degradar throughput.
  • La pérdida de cotizaciones por expiración es aceptable (la siguiente cotización reemplaza a la anterior) pero debe monitorearse como indicador de salud.

Flujos de Datos

Exchange Feed → [Feed Handler] → Normalización
  → [RabbitMQ: exchange "market-data" (topic)]
    → queue "pricing-engine" (TTL: 500ms) → Pricing Engine
    → queue "trading-algo" (TTL: 200ms) → Trading Algo Engine
    → queue "risk-engine" (TTL: 5000ms) → Risk Engine
    → queue "trading-dashboard" (TTL: 2000ms) → Trading Dashboard

Eventos o Mensajes

Cada cotización se publica como un mensaje con los siguientes headers:

Header Tipo Ejemplo
message-id String (UUID) q-2026-04-07-14:30:00.123-AAPL
timestamp Long (epoch ms) 1744034400123
expiration String (ms) 500 (per-message TTL de RabbitMQ)
content-type String application/json
x-exchange-source String NASDAQ
x-instrument String AAPL

El body contiene la cotización normalizada:

{
  "instrument": "AAPL",
  "bid": 187.42,
  "ask": 187.44,
  "bidSize": 500,
  "askSize": 300,
  "exchangeTimestamp": 1744034400120,
  "sequenceNumber": 48293847
}

Decisiones Arquitectónicas

  1. TTL diferenciado por consumidor: en lugar de un TTL per-message (que sería el mismo para todos los consumidores), se usa TTL per-queue. Cada cola tiene el TTL apropiado para su consumidor. El mensaje se publica sin TTL per-message al exchange, y cada cola aplica su propio TTL. Esto permite que el mismo mensaje tenga TTL de 200ms para el trading algo y 5000ms para el risk engine.
  2. Política de expiración: discard con métrica: los mensajes expirados se descartan (no se envían a DLQ) porque el volumen sería enorme (millones de cotizaciones expiradas). En su lugar, se monitorea el conteo de mensajes expirados por cola como métrica de salud.
  3. Exchange type: topic: el exchange de RabbitMQ es de tipo topic, permitiendo routing por instrumento (routing key: market-data.NASDAQ.AAPL). Los consumidores se suscriben a los instrumentos que necesitan.
  4. Verificación doble en consumidor: además del TTL del broker, cada consumidor verifica el timestamp del mensaje contra su reloj local y descarta mensajes que superan su umbral de frescura. Esto protege contra clock skew entre el productor y el broker.
  5. Prefetch limitado: los consumidores configuran prefetch=1 para no acumular mensajes en el buffer del client, garantizando que procesan el mensaje más reciente disponible.

Riesgos y Mitigaciones

Riesgo Mitigación
Tasa de expiración alta indica consumidor lento Alerta cuando la tasa de expiración supera el 5% en cualquier cola
Clock skew entre feed handler y broker NTP sincronizado cada 60 segundos; tolerancia de 50ms en verificación de timestamp
Broker saturado por volumen de cotizaciones Cluster de 3 nodos con queue mirroring; monitoring de throughput y latencia
Consumidor procesa cotización just-before-expiry Verificación doble en consumidor con margen de seguridad (TTL - 50ms)
Feed handler genera cotización con timestamp futuro (clock skew) Validación de timestamp: si exchangeTimestamp > now() + 100ms, descartar

9. Desarrollo Paso a Paso del Ejemplo

Paso 1: Configuración de la Infraestructura

El equipo de infraestructura configura el cluster RabbitMQ:

  1. Exchange market-data de tipo topic, durable, con alternador de exchange para mensajes no ruteados.
  2. Queue pricing-engine con TTL de 500ms:
    x-message-ttl: 500
    x-dead-letter-exchange: "dlx-market-data"
    x-max-length: 100000
    
  3. Queue trading-algo con TTL de 200ms:
    x-message-ttl: 200
    x-max-length: 50000
    
  4. Queue risk-engine con TTL de 5000ms:
    x-message-ttl: 5000
    x-max-length: 500000
    
  5. Queue trading-dashboard con TTL de 2000ms:
    x-message-ttl: 2000
    x-max-length: 200000
    

Los x-max-length actúan como segunda línea de defensa: si la cola crece más allá del límite (porque el TTL no se aplica lo suficientemente rápido), los mensajes más antiguos se descartan.

Paso 2: Publicación de Cotizaciones

El Feed Handler de NASDAQ recibe una actualización de precio para AAPL:

  1. Recibe el mensaje FIX del exchange con el precio actualizado.
  2. Normaliza al formato interno JSON.
  3. Añade timestamp: now() (epoch milliseconds del reloj local del feed handler).
  4. Publica al exchange market-data con routing key market-data.NASDAQ.AAPL.
  5. No establece per-message TTL (el TTL viene de la cola).
  6. Usa publisher-confirms para verificar que el broker recibió el mensaje.

Tasa de publicación: aproximadamente 2,000 cotizaciones/segundo para AAPL en horario activo.

Paso 3: Distribución y Expiración

RabbitMQ recibe el mensaje y lo copia a las 4 colas (binding por routing key pattern):

  1. El mensaje entra en pricing-engine queue. Su TTL es de 500ms.
  2. El mismo mensaje entra en trading-algo queue. Su TTL es de 200ms.
  3. El mismo mensaje entra en risk-engine queue. Su TTL es de 5000ms.
  4. El mismo mensaje entra en trading-dashboard queue. Su TTL es de 2000ms.

Si el trading-algo consumer está procesando un cálculo de 300ms, el mensaje expira en su cola antes de ser consumido. RabbitMQ lo descarta (no DLQ para esta cola, solo métrica). El consumer, al terminar el cálculo, recibe el siguiente mensaje no expirado, que es la cotización más reciente disponible.

Paso 4: Consumo con Verificación de Frescura

El Pricing Engine recibe una cotización:

  1. Lee el timestamp del mensaje: 1744034400123.
  2. Calcula la antigüedad: now() - timestamp = 180ms.
  3. Verifica contra su umbral interno: 180ms < 450ms (TTL - margen de seguridad). La cotización es fresca.
  4. Usa la cotización para recalcular el precio del derivado AAPL.
  5. Hace ACK del mensaje.

Si la antigüedad fuera 480ms (dentro del TTL del broker pero fuera del margen de seguridad del consumidor), el Pricing Engine descarta el mensaje y solicita el siguiente.

Paso 5: Monitoreo de Salud

El sistema de monitoreo (Prometheus + Grafana) rastrea:

  1. Tasa de expiración por cola: mensajes expirados/segundo. Normal: <1% del volumen. Alerta: >5%.
  2. Queue depth: mensajes en cola. Normal: <1000. Alerta: >10,000 (indica consumer lento).
  3. Consumer lag: diferencia entre timestamp del mensaje más reciente publicado y timestamp del último mensaje consumido. Normal: <50ms. Alerta: >200ms.
  4. Throughput de cotizaciones: mensajes/segundo publicados vs. consumidos por cola.

En operación normal, la tasa de expiración es cercana a cero: los consumidores procesan los mensajes antes de que expiren. Un incremento en la tasa de expiración es el primer indicador de que un consumidor está degradado.

Paso 6: Escenario de Fallo

A las 14:35 UTC, el Pricing Engine experimenta un GC pause de 3 segundos (Java):

  1. Durante 3 segundos, el consumer no procesa mensajes.
  2. Los mensajes se acumulan en la cola pricing-engine. Los que tienen más de 500ms expiran.
  3. Después del GC pause, el consumer reanuda y recibe solo mensajes no expirados (los publicados en los últimos 500ms).
  4. El sistema de monitoreo registra un spike de expiración de ~6,000 mensajes (3 segundos * 2,000 cotizaciones/segundo).
  5. El equipo de operaciones investiga el spike, identifica el GC pause en los logs del JVM y ajusta la configuración de GC.
  6. Impacto de negocio: durante 3 segundos, los precios de derivados no se actualizaron. Al reanudarse, el Pricing Engine operó inmediatamente con cotizaciones frescas, sin procesar ninguna cotización obsoleta.

Manejo de Errores

  • Consumer down prolongado: si un consumidor está caído más de su TTL, todos los mensajes expirarán. Al reiniciarse, la cola estará vacía y comenzará a recibir mensajes nuevos. No hay backlog de datos obsoletos que procesar.
  • Broker node failure: con queue mirroring, los mensajes y sus TTLs se replican. El failover al nodo mirror preserva el comportamiento de expiración.
  • Feed handler failure: no se publican cotizaciones. Los consumidores ven que la cola está vacía (no que hay cotizaciones viejas). El monitoreo detecta la ausencia de publicación.

10. Diagrama Técnico del Patrón

Código Python con diagrams

Diagrama General

Diagrama AWS

Diagrama Azure

Ver / Copiar código de los diagramas
from diagrams import Diagram, Cluster, Edge
from diagrams.onprem.queue import RabbitMQ
from diagrams.onprem.compute import Server
from diagrams.onprem.monitoring import Prometheus, Grafana
from diagrams.generic.network import Firewall

with Diagram("Message Expiration - Market Data Quotes", show=False, direction="LR"):

    with Cluster("Market Data Sources"):
        nasdaq = Server("Feed Handler\nNASDAQ")
        nyse = Server("Feed Handler\nNYSE")
        cme = Server("Feed Handler\nCME")

    with Cluster("RabbitMQ Cluster"):
        exchange = RabbitMQ("Exchange\nmarket-data\n(topic)")

        with Cluster("Queues with TTL"):
            q_pricing = RabbitMQ("Queue: pricing\nTTL: 500ms")
            q_algo = RabbitMQ("Queue: trading-algo\nTTL: 200ms")
            q_risk = RabbitMQ("Queue: risk\nTTL: 5000ms")
            q_dash = RabbitMQ("Queue: dashboard\nTTL: 2000ms")

    with Cluster("Consumers"):
        pricing = Server("Pricing\nEngine")
        algo = Server("Trading\nAlgorithm")
        risk = Server("Risk\nEngine")
        dash = Server("Trading\nDashboard")

    with Cluster("Monitoring"):
        prom = Prometheus("Prometheus")
        graf = Grafana("Grafana\nExpiration Metrics")

    expired = Firewall("Expired\nMessages\n(discarded)")

    # Source to exchange
    nasdaq >> Edge(label="quotes") >> exchange
    nyse >> Edge(label="quotes") >> exchange
    cme >> Edge(label="quotes") >> exchange

    # Exchange to queues
    exchange >> q_pricing
    exchange >> q_algo
    exchange >> q_risk
    exchange >> q_dash

    # Queues to consumers
    q_pricing >> pricing
    q_algo >> algo
    q_risk >> risk
    q_dash >> dash

    # Expiration
    q_pricing >> Edge(style="dashed", label="expired", color="red") >> expired
    q_algo >> Edge(style="dashed", label="expired", color="red") >> expired

    # Monitoring
    q_pricing >> Edge(style="dotted") >> prom
    q_algo >> Edge(style="dotted") >> prom
    prom >> graf
from diagrams import Diagram, Cluster, Edge
from diagrams.generic.network import Firewall
from diagrams.aws.compute import Lambda, ECS
from diagrams.aws.analytics import KinesisDataStreams
from diagrams.aws.integration import SNS, SQS
from diagrams.aws.management import Cloudwatch


with Diagram("Message Expiration - Market Data Quotes (AWS)", show=False, direction="LR"):

    with Cluster("Market Data Sources"):
        nasdaq = ECS("Feed Handler\nNASDAQ")
        nyse = ECS("Feed Handler\nNYSE")
        cme = ECS("Feed Handler\nCME")

    with Cluster("SNS Fan-Out"):
        exchange = SNS("SNS Topic\nmarket-data")

        with Cluster("SQS Queues with Message Timer"):
            q_pricing = SQS("Queue: pricing\nTimer: 500ms")
            q_algo = SQS("Queue: trading-algo\nTimer: 200ms")
            q_risk = SQS("Queue: risk\nTimer: 5000ms")
            q_dash = SQS("Queue: dashboard\nTimer: 2000ms")

    with Cluster("Consumers"):
        pricing = ECS("Pricing\nEngine")
        algo = ECS("Trading\nAlgorithm")
        risk = Lambda("Risk\nEngine")
        dash = Lambda("Trading\nDashboard")

    with Cluster("Monitoring"):
        cw_metrics = Cloudwatch("CloudWatch\nMetrics")
        cw_dash = Cloudwatch("CloudWatch\nExpiration Dashboard")

    expired = Firewall("Expired\nMessages\n(discarded)")

    # Source to SNS topic
    nasdaq >> Edge(label="quotes") >> exchange
    nyse >> Edge(label="quotes") >> exchange
    cme >> Edge(label="quotes") >> exchange

    # SNS to SQS subscriptions
    exchange >> q_pricing
    exchange >> q_algo
    exchange >> q_risk
    exchange >> q_dash

    # Queues to consumers
    q_pricing >> pricing
    q_algo >> algo
    q_risk >> risk
    q_dash >> dash

    # Expiration
    q_pricing >> Edge(style="dashed", label="expired", color="red") >> expired
    q_algo >> Edge(style="dashed", label="expired", color="red") >> expired

    # Monitoring
    q_pricing >> Edge(style="dotted") >> cw_metrics
    q_algo >> Edge(style="dotted") >> cw_metrics
    cw_metrics >> cw_dash
from diagrams import Diagram, Cluster, Edge
from diagrams.generic.network import Firewall
from diagrams.azure.compute import FunctionApps
from diagrams.azure.devops import ApplicationInsights
from diagrams.azure.integration import ServiceBus


with Diagram("Message Expiration - Market Data Quotes (Azure)", show=False, direction="LR"):

    with Cluster("Market Data Sources"):
        nasdaq = FunctionApps("Feed Handler\nNASDAQ")
        nyse = FunctionApps("Feed Handler\nNYSE")
        cme = FunctionApps("Feed Handler\nCME")

    with Cluster("Service Bus Topic (TimeToLive)"):
        exchange = ServiceBus("market-data\n(Topic)")

        with Cluster("Subscriptions with TTL"):
            q_pricing = ServiceBus("pricing-sub\nTimeToLive: 500ms")
            q_algo = ServiceBus("trading-algo-sub\nTimeToLive: 200ms")
            q_risk = ServiceBus("risk-sub\nTimeToLive: 5000ms")
            q_dash = ServiceBus("dashboard-sub\nTimeToLive: 2000ms")

    with Cluster("Consumers"):
        pricing = FunctionApps("Pricing\nEngine")
        algo = FunctionApps("Trading\nAlgorithm")
        risk = FunctionApps("Risk\nEngine")
        dash = FunctionApps("Trading\nDashboard")

    with Cluster("Monitoring"):
        app_insights = ApplicationInsights("Application\nInsights")
        monitor = ApplicationInsights("Azure Monitor\nExpiration Metrics")

    expired = Firewall("Expired\nMessages\n(discarded)")

    # Source to topic
    nasdaq >> Edge(label="quotes") >> exchange
    nyse >> Edge(label="quotes") >> exchange
    cme >> Edge(label="quotes") >> exchange

    # Topic to subscriptions
    exchange >> q_pricing
    exchange >> q_algo
    exchange >> q_risk
    exchange >> q_dash

    # Subscriptions to consumers
    q_pricing >> pricing
    q_algo >> algo
    q_risk >> risk
    q_dash >> dash

    # Expiration
    q_pricing >> Edge(style="dashed", label="TTL expired", color="red") >> expired
    q_algo >> Edge(style="dashed", label="TTL expired", color="red") >> expired

    # Monitoring
    q_pricing >> Edge(style="dotted") >> app_insights
    q_algo >> Edge(style="dotted") >> app_insights
    app_insights >> monitor

Explicación del Diagrama

El diagrama representa el flujo completo de Message Expiration para la distribución de cotizaciones de mercado:

  1. Market Data Sources (izquierda): Feed Handlers de múltiples exchanges (NASDAQ, NYSE, CME) publican cotizaciones normalizadas al exchange de RabbitMQ.
  2. RabbitMQ Cluster: el exchange market-data de tipo topic distribuye cada cotización a las 4 colas. Cada cola tiene un TTL diferente según las necesidades de frescura de su consumidor.
  3. Consumers (derecha): cada consumidor procesa cotizaciones de su cola. Solo recibe cotizaciones no expiradas.
  4. Expiration: los mensajes que no se procesan dentro de su TTL se descartan (representados por el componente "Expired Messages").
  5. Monitoring: Prometheus recopila métricas de expiración de cada cola; Grafana las visualiza para el equipo de operaciones.

Correspondencia Patrón ↔ Diagrama

Concepto del Patrón Componente del Diagrama
Productor con TTL Feed Handlers (NASDAQ, NYSE, CME)
Mensaje con expiración Quote message publicado al exchange
Broker con verificación de TTL RabbitMQ Queues con x-message-ttl
Consumidor protegido Pricing Engine, Trading Algorithm, Risk Engine, Dashboard
Mensajes expirados Expired Messages (discarded)
Monitoreo de expiración Prometheus + Grafana

11. Beneficios

Impacto Técnico

  • Protección contra datos obsoletos: los consumidores nunca procesan información fuera de su ventana de validez, evitando acciones basadas en datos caducados.
  • Protección del broker: TTL previene la acumulación infinita de mensajes en colas, protegiendo la memoria y el disco del broker.
  • Recovery limpio: después de un fallo del consumidor, la cola contiene solo mensajes dentro del TTL. El consumidor no necesita procesar un backlog de datos obsoletos antes de alcanzar los datos actuales.
  • Backpressure temporal: TTL actúa como un mecanismo natural de backpressure — cuando el consumidor no puede mantener el ritmo, los mensajes obsoletos se descartan automáticamente, permitiendo que el consumidor procese solo los datos relevantes.

Impacto Organizacional

  • SLA explícito de frescura: el TTL formaliza el requisito temporal de la información, convirtiendo un requisito implícito ("los precios deben ser recientes") en una configuración explícita y medible.
  • Contrato temporal entre equipos: el equipo productor establece "esta información es válida por X tiempo" y el equipo consumidor confía en esa garantía.

Impacto Operacional

  • Dimensionamiento predecible: con TTL, el tamaño máximo de una cola está acotado (rate * TTL), lo cual permite dimensionar el broker de forma predecible.
  • Indicador de salud: la tasa de expiración es un indicador directo de problemas de rendimiento del consumidor. Un spike de expiración señala degradación antes de que se produzcan efectos visibles para el usuario.
  • Autolimpieza: las colas se autolimpian de mensajes obsoletos, eliminando la necesidad de purgas manuales.

Beneficios de Mantenibilidad y Evolución

  • Configuración, no código: el TTL es un parámetro de configuración de la cola o del mensaje, no una lógica de negocio en el código del consumidor. Se puede ajustar sin redesplegar.
  • Independencia de escenarios de fallo: el sistema se comporta correctamente ante fallos temporales del consumidor sin necesidad de lógica de recuperación especial.

12. Desventajas y Riesgos

Complejidad Añadida

  • Determinación del TTL correcto: requiere comprender la semántica temporal de cada tipo de mensaje, la latencia esperada del sistema y la tolerancia al procesamiento tardío. No hay un valor universalmente correcto.
  • Interacción con retries: un mensaje que falla y se reencola para retry consume parte de su TTL. Si el retry tarda más que el TTL restante, el mensaje se pierde definitivamente. Diseñar la interacción entre TTL y retries es no trivial.
  • Clock skew: en sistemas distribuidos, la diferencia entre relojes puede causar que mensajes expiren prematuramente o demasiado tarde.

Riesgos de Mal Uso

  • TTL demasiado agresivo durante picos de carga: un TTL calibrado para operación normal puede ser demasiado corto durante picos de carga legítimos, descartando mensajes que el consumidor habría procesado segundos después. Esto convierte un problema de latencia temporal en pérdida de datos.
  • TTL como sustituto de scaling: usar TTL para "resolver" el problema de un consumidor lento en lugar de escalar el consumidor. El TTL enmascara el problema pero no lo resuelve — los datos se pierden en lugar de procesarse con retraso.
  • Expiración silenciosa sin monitoreo: si los mensajes expiran y nadie monitorea la tasa de expiración, se pierden datos sin que nadie se entere. Toda implementación de TTL debe acompañarse de monitoreo de tasa de expiración.

Sobreingeniería

  • TTL per-message cuando per-queue es suficiente: si todos los mensajes de una cola tienen la misma ventana de validez, per-queue TTL es más simple y eficiente. Per-message TTL añade overhead de header y verificación individual.
  • DLQ para mensajes expirados de alta frecuencia: enviar millones de cotizaciones expiradas a DLQ genera más problemas (storage, limpieza) que soluciones. Para datos de alta frecuencia y reemplazables, el discard con métrica es más apropiado.

Costos de Operación

  • Calibración continua: el TTL óptimo puede cambiar con la evolución del sistema (más consumidores, más carga, infraestructura diferente). Requiere revisión periódica.
  • Debugging de mensajes perdidos: investigar por qué un mensaje "se perdió" cuando en realidad expiró requiere correlacionar timestamps de publicación, expiración y estado del consumidor.

Anti-Patterns Relacionados

  • TTL Zero: establecer un TTL tan corto que los mensajes expiran antes de que cualquier consumidor tenga oportunidad de procesarlos. Esto no es TTL, es descarte.
  • Infinite TTL: no configurar TTL, permitiendo acumulación ilimitada. Esto anula el propósito del patrón.
  • TTL as Rate Limiter: usar TTL para "limitar" la tasa de procesamiento en lugar de usar mecanismos apropiados de rate limiting. El TTL descarta datos; el rate limiting los almacena para procesamiento posterior.

13. Relación con Otros Patrones

Patrones Complementarios

  • Dead Letter Channel: es el destino natural de los mensajes expirados cuando se quiere preservarlos para inspección. La configuración típica es: TTL en la cola principal + DLQ como dead-letter exchange. Los mensajes que expiran se mueven automáticamente a la DLQ.
  • Message Timestamp: prerequisito implícito de Message Expiration. Sin un timestamp de creación confiable, no se puede calcular si el mensaje ha expirado. En la práctica, el productor establece el timestamp y el broker calcula la expiración.
  • Competing Consumers: escalar el número de consumidores reduce la latencia de procesamiento y, por tanto, la tasa de expiración. Competing Consumers es la respuesta arquitectónica cuando la tasa de expiración indica que el consumidor no puede mantener el ritmo.

Patrones que Suelen Aparecer Antes o Después

  • Message Channel: el TTL se configura en el canal (cola). La configuración del canal incluye el TTL como uno de sus parámetros operacionales.
  • Content-Based Router: puede enrutar mensajes a colas con diferentes TTLs según su contenido (ej. cotizaciones high-priority a cola con TTL largo, low-priority a cola con TTL corto).
  • Wire Tap: puede copiar mensajes a un canal de auditoría antes de que expiren, preservando un registro sin afectar el TTL del canal principal.

Combinaciones Comunes

  • Message Expiration + Dead Letter Channel + Alerting: la combinación más frecuente en producción. Los mensajes que expiran van a DLQ, y la tasa de llegada a DLQ dispara alertas.
  • Message Expiration + Competing Consumers: TTL protege contra datos obsoletos; Competing Consumers escala el procesamiento para minimizar la necesidad de que mensajes expiren.
  • Message Expiration + Message Sequence: cada fragmento de una secuencia puede tener un TTL para evitar que fragmentos huérfanos permanezcan indefinidamente.

Diferencias con Patrones Similares

  • vs. Message Timestamp: Timestamp es metadata informativa; Expiration es una directiva operacional. Timestamp dice "cuándo se creó"; Expiration dice "cuándo debe descartarse". Ambos usan tiempo pero con semántica diferente.
  • vs. Polling Consumer: Polling Consumer determina cuándo el consumidor busca mensajes. Message Expiration determina cuándo los mensajes dejan de ser válidos. Son ortogonales.

Encaje en un Flujo Mayor de Integración

Message Expiration es un patrón transversal que se aplica a cualquier flujo de integración donde la información tiene temporalidad. No es un paso en el flujo sino una propiedad del mensaje que afecta cómo el broker y los consumidores manejan el mensaje a lo largo de todo su ciclo de vida.


14. Relevancia Actual del Patrón

Evaluación: Relevancia Alta

Argumentación

Message Expiration es uno de los patrones de construcción de mensajes más relevantes en la práctica moderna. TTL es un parámetro de configuración estándar en todo sistema de mensajería en producción, y su correcta configuración es una práctica operacional fundamental.

A favor de la vigencia:

  • TTL está en toda plataforma: RabbitMQ (x-message-ttl, per-message expiration), Azure Service Bus (TimeToLive), Amazon SQS (MessageRetentionPeriod, VisibilityTimeout), Google Cloud Pub/Sub (ackDeadline, message retention), Apache Pulsar (TTL policies). No es un concepto teórico; es un parámetro que se configura en toda implementación.
  • Protección operacional obligatoria: en producción, toda cola debe tener un TTL o un max-length como protección contra acumulación infinita. Los equipos de operaciones incluyen TTL como parte del checklist de configuración de colas.
  • Microservicios y event-driven: en arquitecturas de microservicios con cientos de colas, TTL es esencial para evitar que la caída de un servicio genere acumulación en las colas de sus dependientes.
  • Cloud cost optimization: en servicios cloud donde el storage de mensajería se factura por GB retenido (Azure Service Bus, Amazon SQS), TTL reduce costos al eliminar mensajes obsoletos.

Matices de implementación moderna:

  • Kafka no tiene per-message TTL: Kafka implementa retención a nivel de topic (retention.ms), no per-message. Esto es conceptualmente diferente: Kafka retiene todos los mensajes por un período y luego los elimina por segmento, no evalúa cada mensaje individualmente. Los consumers de Kafka son responsables de verificar timestamps y decidir si procesan un mensaje antiguo.
  • Event Sourcing: en arquitecturas de event sourcing, los eventos son inmutables y se retienen indefinidamente. TTL no aplica a eventos del log. Sin embargo, los projections y views derivadas sí pueden tener TTL.

Contexto Moderno Donde Se Utiliza

  • Toda cola de mensajería en producción (como protección operacional básica).
  • Sistemas de market data y trading en tiempo real.
  • Notificaciones push y alertas con ventana temporal.
  • IoT y telemetría de alta frecuencia.
  • Sistemas de reservas, cotizaciones y ofertas temporales.
  • Colas de procesamiento asíncrono donde el procesamiento tardío no aporta valor.

Cómo Se Implementa Hoy

  • RabbitMQ: x-message-ttl en la declaración de la cola (per-queue) o expiration en las properties del mensaje (per-message). Mensajes expirados se descartan o se mueven a DLX (Dead Letter Exchange).
  • Azure Service Bus: TimeToLive en el ServiceBusMessage. Mensajes expirados se mueven automáticamente a la Dead Letter Queue del subscription si está habilitada.
  • Amazon SQS: MessageRetentionPeriod (per-queue, 1 minuto a 14 días). No hay per-message TTL nativo, pero la aplicación puede usar SentTimestamp attribute para verificar antigüedad.
  • Kafka: retention.ms a nivel de topic elimina segmentos completos después de la retención. Per-record TTL no existe; los consumers verifican record.timestamp().
  • Google Cloud Pub/Sub: message_retention_duration en el topic; ack_deadline_seconds en la subscription.

Qué Parte Sigue Siendo Esencial

  • El concepto de que la información tiene temporalidad finita es universal y atemporal.
  • La configuración de TTL como protección operacional es una práctica obligatoria en producción.
  • El monitoreo de tasa de expiración como indicador de salud del consumidor es una best practice operacional.

15. Implementación en Arquitecturas Modernas

Apache Kafka

Kafka no implementa per-message TTL, pero proporciona mecanismos relacionados:

  • retention.ms (topic-level): tiempo que Kafka retiene los segmentos del log. Después de este período, los segmentos se eliminan. Default: 7 días. Esto no es Message Expiration (no evalúa cada mensaje) sino retención del log.
  • retention.bytes (topic-level): tamaño máximo del log por partición. Cuando se excede, los segmentos más antiguos se eliminan.
  • Record timestamp: cada record tiene un timestamp (creación o enqueue). Los consumers pueden leer record.timestamp() y decidir si procesar basándose en la antigüedad.
  • Kafka Streams/ksqlDB: permiten filtrar por timestamp en procesamiento stream, descartando records obsoletos.
  • Recomendación Kafka: implementar verificación de timestamp en el consumer como equivalente funcional de Message Expiration. Ejemplo: if (System.currentTimeMillis() - record.timestamp() > maxAge) { skip; }.

RabbitMQ

RabbitMQ tiene la implementación más rica de Message Expiration:

  • Per-queue TTL: x-message-ttl en la declaración de la cola. Se aplica a todos los mensajes. Eficiente porque RabbitMQ puede descartar mensajes del head de la cola por expiración.
  • Per-message TTL: header expiration en las message properties. Se verifica al momento de entrega (lazy evaluation). Menos eficiente porque mensajes expirados pueden permanecer en la cola si no están en el head.
  • Precedencia: si ambos están configurados, el TTL efectivo es el menor de los dos.
  • Dead Letter Exchange: configurable con x-dead-letter-exchange y x-dead-letter-routing-key para redirigir mensajes expirados.

Azure Service Bus

  • TimeToLive: propiedad del mensaje (ServiceBusMessage.TimeToLive). Default: depende de la configuración del queue/subscription.
  • ExpiresAt: propiedad calculada (EnqueuedTimeUtc + TimeToLive).
  • Auto-forwarding a Dead Letter: mensajes expirados se mueven automáticamente a la Dead Letter Queue del queue/subscription.
  • DefaultMessageTimeToLive: configuración a nivel de queue/subscription que actúa como TTL default si el mensaje no especifica uno.

Amazon SQS

  • MessageRetentionPeriod: configurable per-queue (60 segundos a 14 días). Default: 4 días. Los mensajes que exceden este período se eliminan automáticamente.
  • VisibilityTimeout: no es TTL sino el tiempo que un mensaje es invisible para otros consumers después de ser leído. Si el consumer no hace delete antes del timeout, el mensaje vuelve a ser visible.
  • DelaySeconds: retrasa la disponibilidad del mensaje (inverso del TTL: en lugar de cuándo expira, cuándo empieza a estar disponible).
  • SentTimestamp: atributo del mensaje que el consumer puede usar para verificar antigüedad.

Google Cloud Pub/Sub

  • message_retention_duration: retención a nivel de topic (10 minutos a 31 días).
  • ack_deadline_seconds: tiempo que el subscriber tiene para hacer ACK. Si no lo hace, el mensaje se redelivers.
  • Dead letter topics: mensajes que no se procesan después de N intentos se redirigen a un dead letter topic.

MuleSoft / Apache Camel

  • MuleSoft: el conector JMS de MuleSoft soporta timeToLive en el publisher. En flujos de procesamiento, se puede implementar verificación de timestamp con DataWeave.
  • Apache Camel: soporta TTL en componentes JMS (jms:queue:myQueue?timeToLive=30000). El componente timer y expresiones en headers permiten implementar verificación de expiración custom.

16. Consideraciones de Gobierno y Operación

Observabilidad

  • Métricas clave: mensajes expirados/segundo por cola, porcentaje de mensajes expirados vs. procesados, profundidad de cola (que indica acumulación pre-expiración), latencia de procesamiento por cola.
  • Health checks: verificar que el TTL está configurado en todas las colas de producción (colas sin TTL son un riesgo operacional).
  • Alertas: tasa de expiración superior al umbral (indica consumer lento), cola creciendo a pesar de TTL (indica que el rate de publicación excede drasticamente al de consumo), TTL misconfigured (ej. TTL en milisegundos cuando debería ser en segundos).

Tracing

  • Cuando un mensaje expira, el broker debe emitir un evento trazable que incluya el message ID, el TTL configurado, el timestamp de creación y el timestamp de expiración. Esto permite correlacionar mensajes expirados con sus productores y diagnósticar la causa.
  • En Dead Letter Queues, los mensajes expirados deben conservar todos sus headers originales más el motivo de dead-lettering (x-death en RabbitMQ).

Monitoreo

  • Dashboard operacional: para cada cola: TTL configurado, tasa de expiración (últimos 5 minutos, 1 hora, 24 horas), porcentaje de expiración, profundidad de cola.
  • Alertas de negocio: si el tipo de mensaje tiene implicaciones de negocio (ej. cotizaciones de trading), la tasa de expiración debe reportarse al equipo de negocio como indicador de calidad de datos.
  • Trend analysis: una tendencia creciente en la tasa de expiración indica degradación progresiva del consumidor.

Versionado

  • El TTL es un parámetro operacional, no parte del schema del mensaje. Se puede cambiar sin afectar el formato del mensaje.
  • Sin embargo, cambios en el TTL deben coordinarse con los equipos de productor y consumidor: reducir el TTL puede causar pérdida inesperada de mensajes; aumentarlo puede causar procesamiento de datos más antiguos de lo esperado.

Seguridad

  • Los mensajes expirados que se mueven a DLQ contienen el mismo payload que los mensajes originales. Si el payload contiene datos sensibles, la DLQ debe tener las mismas protecciones de seguridad que la cola principal.
  • TTL es un mecanismo de higiene de datos, no de seguridad. No sustituye cifrado ni control de acceso.

Manejo de Errores

  • Mensaje expirado durante retry: si un mensaje falla en el primer intento y se reencola para retry, puede expirar durante el período de espera del retry. La estrategia debe definirse: ¿se acepta la pérdida? ¿Se envía a DLQ con indicador de "expirado durante retry"?
  • TTL misconfigured: un TTL de 500 (que el operador pretendía como 500 segundos) interpretado como 500 milisegundos causa que todos los mensajes expiren inmediatamente. Validación de configuración (sanity checks) ayuda a prevenir esto.

Retries

  • El TTL original del mensaje aplica incluso durante retries. Un mensaje creado a las 14:30:00 con TTL de 30 segundos expira a las 14:30:30 independientemente de cuántas veces se reintente.
  • Si el retry strategy requiere más tiempo que el TTL, considerar enviar a DLQ en lugar de reintentar.

Dead-Lettering

  • Los mensajes expirados que se mueven a DLQ deben incluir el motivo de dead-lettering (TTL expiry) para distinguirlos de mensajes que fallaron por error de procesamiento.
  • La DLQ debe tener su propio TTL (típicamente más largo, ej. 7 días) para no acumular mensajes de diagnóstico indefinidamente.
  • En RabbitMQ, el header x-death incluye el motivo (expired), la cola de origen, el timestamp de muerte y el conteo de muertes.

Idempotencia

  • Message Expiration no afecta la idempotencia del procesamiento. El consumidor debe ser idempotente independientemente del TTL.
  • Sin embargo, la combinación de TTL + retry puede generar situaciones donde un mensaje se procesa parcialmente, expira antes del retry y no se completa. El consumidor debe manejar este escenario.

Auditoría

  • Para mensajes con implicaciones de negocio (transacciones financieras, órdenes), la expiración debe auditarse: qué mensaje expiró, cuándo, por qué (TTL configurado, latencia del consumidor).
  • La DLQ con mensajes expirados es una fuente de auditoría, pero solo si está habilitada.

Performance

  • Per-queue TTL es eficiente en RabbitMQ porque el broker verifica expiración solo en el head de la cola (FIFO order implica que el head es siempre el más antiguo).
  • Per-message TTL es menos eficiente porque mensajes con TTL diferentes pueden estar intercalados en la cola, requiriendo verificación individual.
  • Impacto en el broker: la verificación de TTL tiene costo de CPU negligible comparado con la persistencia y transmisión de mensajes.

Escalabilidad

  • TTL es un mecanismo local a cada cola/partición. No introduce coordinación global ni contención.
  • El efecto de escalabilidad más importante es indirecto: TTL previene el crecimiento ilimitado de colas, lo cual protege la estabilidad del broker bajo alta carga.

17. Errores Comunes

No Configurar TTL en Colas de Producción

El error más frecuente y más peligroso es desplegar colas en producción sin TTL ni max-length. Cuando un consumidor se detiene (por deployment, fallo o degradación), la cola crece indefinidamente hasta que el broker se queda sin memoria o disco. Toda cola en producción debe tener al menos un mecanismo de limitación: TTL, max-length o ambos.

TTL Calibrado para Operación Normal, No para Picos

Calibrar el TTL basándose solo en la latencia promedio del consumidor, sin considerar picos de carga, GC pauses, degradación de servicios downstream o deployment rolling updates. El TTL debe calibrarse para el percentil 99 de latencia del consumidor, con un margen de seguridad adicional.

Usar TTL como Mecanismo de Purga en Lugar de Correcta Arquitectura

Configurar un TTL de 1 hora en una cola que "a veces acumula mensajes" es tratar el síntoma, no la causa. Si los mensajes se acumulan porque el consumidor es lento, la solución es escalar el consumidor (Competing Consumers), no descartar mensajes con TTL. TTL es para información perecedera, no para limitar la profundidad de colas de datos perdurables.

Expiración Silenciosa sin Monitoreo ni Alerting

Configurar TTL y olvidarse. Sin monitoreo de la tasa de expiración, los mensajes se pierden sin que nadie se entere. Un equipo descubre semanas después que su consumidor ha estado descartando el 30% de los mensajes por expiración, y los datos perdidos no son recuperables. Toda configuración de TTL debe acompañarse de una alerta en la tasa de expiración.

Confundir TTL con Visibility Timeout

En SQS, VisibilityTimeout y MessageRetentionPeriod son conceptos diferentes. VisibilityTimeout controla cuánto tiempo un mensaje es invisible después de ser leído (para permitir procesamiento). MessageRetentionPeriod controla cuánto tiempo SQS retiene un mensaje no eliminado. Confundir ambos conceptos lleva a comportamientos inesperados.

No Considerar Clock Skew en TTL Absoluto

Si el productor establece un timestamp absoluto de expiración basado en su reloj, y el reloj del broker o del consumidor está desfasado, los mensajes pueden expirar prematuramente o demasiado tarde. Para TTLs de segundos, un clock skew de 100ms puede ser significativo. Usar TTL relativo (duración) en lugar de absoluto (timestamp) mitiga este problema.

DLQ sin TTL Propio

Enviar mensajes expirados a una DLQ que no tiene su propio TTL ni política de retención. La DLQ crece indefinidamente, consumiendo recursos y eventualmente se convierte en el mismo problema que se intentaba resolver con TTL en la cola principal.


18. Conclusión Técnica

Message Expiration es uno de los patrones de construcción de mensajes más pragmáticos y operacionalmente relevantes. A diferencia de patrones que resuelven problemas de diseño abstractos, TTL resuelve un problema concreto y universal: la información tiene una vida útil finita y procesarla fuera de esa vida útil puede ser inútil o dañino.

Para un arquitecto moderno, Message Expiration no es opcional — es un parámetro de configuración que se establece en toda cola de producción:

  • Cuándo aporta valor máximo: en dominios donde la información es intrínsecamente perecedera (market data, cotizaciones, alertas en tiempo real, telemetría). El TTL es corto (milisegundos a minutos) y la pérdida por expiración es esperada y aceptable.
  • Cuándo aporta valor como protección operacional: en toda cola de producción, incluso para datos no perecederos. Un TTL largo (horas o días) protege al broker de acumulación infinita sin descartar mensajes en operación normal.
  • Cuándo no aplica: en event sourcing (los eventos son inmutables y permanentes), en colas de auditoría que deben retener todo, y en escenarios donde la pérdida de cualquier mensaje es inaceptable y no hay mecanismo de recuperación.

Recomendación para arquitectos: establezca TTL en toda cola de producción como parte del checklist de configuración estándar. Para datos perecederos, calibre el TTL basándose en el percentil 99 de latencia del consumidor con un margen de seguridad. Para datos no perecederos, establezca un TTL largo (24-72 horas) como protección contra acumulación infinita. Configure Dead Letter Queue para mensajes expirados que tienen implicaciones de negocio (transacciones, órdenes). Monitoree la tasa de expiración como indicador de salud del consumidor — un spike en la tasa de expiración es frecuentemente el primer síntoma de degradación del sistema. Y nunca use TTL como sustituto de escalado del consumidor: si los mensajes expiran regularmente porque el consumidor no puede mantener el ritmo, la solución es escalar el consumidor, no reducir el TTL.