🧪 Problem-Driven Systems Lab
🪦 Caso 20 · Resiliencia

La dead letter queue olvidada

El pipeline muestra throughput normal y cero errores mientras pierde el 14% de los mensajes: los errores se fueron a una cola que nadie mira.

OPERATIVO 🐘 PHP🐍 Python🟢 Node.js☕ Java🔵 .NET🐹 Go🦀 Rust

📈 Que cambia para el negocio

Elimina la perdida silenciosa de datos y convierte 'se proceso' en una afirmacion sobre la que se puede construir: conciliaciones, garantias de entrega, SLA de procesamiento.

💼 Que demuestra tecnicamente

Muestra que un consumidor que captura el error, no se muere y sigue esta haciendo exactamente lo que se le pidio — y que sin la otra mitad eso es perdida de datos con buenos modales. El 71,39% de esa DLQ se recupera sin cambiar una linea de codigo.

✅ Evidencia que deja

  • Contrasta /consume-silent y /consume-observed: 13,87% de dead-letter contra 3,97%.
  • Drena la DLQ del consumidor silencioso y recupera el 71,39%: trabajo que nunca debio tirarse.
  • Expone by_error_class y dlq_oldest_msg_age_ms, que convierten un numero en un diagnostico.

👀 Que mirar al ejecutarlo

  • Si el error rate del servicio es cero mientras dead_letter_rate_pct esta en 13,87%.
  • Si by_error_class dice 'unclassified' en la variante silenciosa y desglosa cuatro clases en la otra.
  • Si el drenaje del silencioso recupera el 71,39% y el del observado recupera cero: ahi solo hay veneno.
Honestidad: La DLQ es una lista en memoria (un archivo JSON bajo flock en PHP), no SQS ni RabbitMQ. Lo que define el caso no es el broker: es que un mensaje que falla tiene que ir a algun lado, y que ese lado necesita profundidad, antiguedad, clasificacion y una salida. La clase de error de cada mensaje es deterministica para que los 7 stacks den resultados identicos y lo unico comparable sea que tan dificil hace cada lenguaje clasificar MAL. dlq_oldest_msg_age_ms sale en milisegundos por la escala del laboratorio; en produccion se mide en semanas y meses.

Como esta resuelto en cada stack

Cierra el arco del caso 15, donde la DLQ nace como politica de rechazo. Clasificacion transitorio vs venenoso con la primitiva de cada runtime: enum + match exhaustivo en Rust —una clase de error nueva NO COMPILA— y panic! como canal separado de Result, filtros catch(e) when(...) en .NET que deciden ANTES de desenrollar la pila, jerarquia sealed...permits en Java, errors.Is/errors.As sobre cadenas %w en Go, catch(A|B) con Throwable en PHP mas drenaje por cron, jerarquia de excepciones en Python con el peligro del except Exception, e instanceof fragil por diseño en Node. Mas DLQ observable (profundidad, antiguedad, desglose por clase, muestreo) y replay

StackHealth check localComposeDetalle
🐘 PHP 8.3http://localhost:8100/20/healthcompose.root.ymlREADME del stack
🐍 Python 3.12http://localhost:8200/20/healthcompose.python.ymlREADME del stack
🟢 Node.js 22http://localhost:8300/20/healthcompose.nodejs.ymlREADME del stack
☕ Java 21http://localhost:8400/20/healthcompose.java.ymlREADME del stack
🔵 .NET 8http://localhost:8500/20/healthcompose.dotnet.ymlREADME del stack
🐹 Go 1.23http://localhost:8600/20/healthcompose.go.ymlREADME del stack
🦀 Rust 1.83http://localhost:8700/20/healthcompose.rust.ymlREADME del stack

El expediente completo

El caso no empieza en el codigo: empieza en el sintoma y termina en el postmortem.

← Caso 19