Un pipeline de CI puede estar verde y aun así esconder un problema serio: los emails de prueba no llegan, el worker reintenta sin límite y las alertas se convierten en ruido. El equipo ve muchos pods ocupados, pero no sabe si el fallo está en la aplicación, en la red o en el buzón.
En una guardia aprendí a tratar el correo de prueba como una dependencia con presupuesto de fallos. No es un SLO perfecto ni una promesa de entrega instantánea. Es un límite operativo que ayuda a decidir cuándo reintentar, cuándo aislar el caso y cuándo parar para conservar evidencia.
El incidente: el pipeline que reintenta hasta empeorar la señal
Imagina una suite que crea una cuenta, espera un mensaje de verificación y confirma el enlace. El mensaje tarda unos segundos mas de lo normal. El test hace polling, el job vence su timeout y Kubernetes lo reinicia. El nuevo pod vuelve a crear la cuenta y abre otra espera.
Después de unos minutos tenemos varias consecuencias:
- muchos mensajes para la misma ejecución;
- logs mezclados entre pods y ramas;
- más carga sobre el servicio de email;
- un reporte que dice “timeout”, pero no muestra el último evento conocido.
El problema no es solo la latencia. Es que el sistema no distingue una entrega lenta de un intento duplicado. Buscar términos como tempmailso o temp mail so puede llevar al equipo a probar buzones distintos, pero no arregla la ausencia de una identidad de ejecución.
Qué significa presupuesto de fallos para un email
Un presupuesto sencillo puede expresarse con tres límites:
- Tiempo total: cuánto puede esperar un test por el mensaje, por ejemplo 90 segundos.
- Número de consultas: cuántas veces se consulta el buzón, con backoff y jitter.
- Intentos de envío: cuántas solicitudes de envío se permiten para una misma ejecución.
Estos valores dependen del sistema. Lo importante es que estén en el contrato del job y no escondidos en un while true. Si se consume el presupuesto, el worker debe terminar con un diagnóstico: delivery_budget_exhausted, el run_id, la última consulta y la edad del mensaje esperado.
También conviene separar un fallo de producto de uno de infraestructura. Si el endpoint aceptó el envío, pero el buzón no recibió nada dentro del tiempo acordado, la evidencia es distinta a una respuesta HTTP 500 o a un error de autenticación. Esa diferencia ahorra una investigación bastante larga.
Un contrato simple para el worker de Kubernetes
El pod necesita límites explícitos y un resultado que otro sistema pueda leer. Un fragmento de configuración podría verse así:
apiVersion: batch/v1
kind: Job
metadata:
generateName: email-check-
labels:
workload: ci-email-check
spec:
backoffLimit: 1
activeDeadlineSeconds: 120
ttlSecondsAfterFinished: 3600
template:
spec:
restartPolicy: Never
containers:
- name: checker
image: registry.example.com/ci-email-check:stable
env:
- name: DELIVERY_BUDGET_SECONDS
value: "90"
- name: MAX_INBOX_POLLS
value: "12"
backoffLimit protege al Job de reinicios infinitos, mientras activeDeadlineSeconds marca un límite de vida. No sustituyen el timeout del cliente: son capas distintas. El cliente debe cortar la espera de forma limpia y escribir su recibo antes de salir.
Para una implementación real, usaría esperas crecientes, por ejemplo 2, 4, 8 y 12 segundos, con un pequeño jitter. El polling rapido puede crear más tráfico sin hacer que el mensaje llegue antes. Y si cada prueba comparte un buzón, un resultado válido puede pertenecer a otra rama. Un buzón aislado por ejecución, o al menos un asunto con run_id, es mucho más facil de depurar.
Si el equipo está diseñando el endpoint que crea ese buzón, puede complementar este enfoque con los contratos para correos temporales en CI. El contrato de la API debería decir qué identifica al mensaje y cómo se informa una expiración, no solo devolver una dirección.
Alertas y evidencia para no perseguir fantasmas
No alertaría por cada timeout individual. Primero registraría métricas agrupadas por servicio, rama y causa:
- porcentaje de jobs que consumen más del 80% del presupuesto;
- tiempo desde el envío hasta el primer mensaje visible;
- cantidad de mensajes descartados por
run_idincorrecto; - reinicios del Job y razón de terminación;
- diferencia entre “envío aceptado” y “mensaje observado”.
Una alerta útil podría decir: “el 15% de los checks de email agotaron su presupuesto en 10 minutos”. Eso permite comparar con una ventana anterior sin culpar al primer pod que falló. El número es solo un ejemplo de umbral; cada equipo debe medir su comportamiento normal antes de fijarlo.
Para flujos con aprobaciones manuales o agentes, también sirve guardar una evidencia mínima del evento. Este patrón de aprobaciones por email con contexto mínimo recuerda una regla útil: el siguiente paso debe saber por qué está actuando, no solo que “llegó algo”.
En documentación de pruebas aparecen a veces variantes como tamp mail com. No las usaría para identificar el sistema ni para construir una alerta; son texto de búsqueda y pueden contaminar dashboards si no se normalizan.
Checklist de operación
- ¿Cada envío tiene un
run_idúnico y visible en logs? - ¿El cliente tiene límite de tiempo, consultas y envíos?
- ¿El Job de Kubernetes tiene
backoffLimity deadline? - ¿Un reinicio puede duplicar una cuenta o un mensaje?
- ¿El resultado distingue entrega lenta, error HTTP y buzón equivocado?
- ¿La alerta agrupa fallos en vez de notificar cada intento?
- ¿Se conserva el recibo suficiente para reproducir la investigación?
La idea no es hacer que el email parezca más confiable de lo que es. Es convertir una dependencia externa en una parte observable del sistema. Con un presupuesto claro, el pipeline falla antes, pero explica mejor por qué.
Preguntas rápidas
¿Debo reintentar siempre?
No. Reintenta solo errores transitorios y dentro del presupuesto. Un error de autenticación o un run_id desconocido necesita detenerse, no más polling.
¿Dónde encaja un generador de correos falsos?
Un generador de correos falsos puede ser útil para crear buzones de prueba en un entorno controlado. No reemplaza el contrato de identidad, los límites ni la observabilidad del pipeline.
¿Qué hago cuando se agota el presupuesto?
Marca el job como fallido, guarda la última evidencia y evita reabrir automáticamente la misma ejecución. Así la siguiente investigación empieza con datos, no con otra cascada de retries.
Top comments (0)