Un pipeline puede terminar en verde y aun así dejar una pregunta incómoda: ¿se envió realmente el email de prueba, o solo terminó el job que lo solicitó? En equipos que ejecutan muchos tests en Kubernetes, esa diferencia convierte un fallo sencillo en una investigación larga.
La solución no es guardar más logs sin orden. Es tratar cada email de prueba como una evidencia con identidad, tiempo de vida y estado observable. El pod que inicia la prueba no debe ser dueño permanente del buzón ni de la entrega. Debe solicitar una ejecución, obtener un lease y publicar señales que otro componente pueda revisar.
El síntoma: CI verde, evidencia perdida
Un flujo típico crea un usuario, pide un enlace de verificación y espera un mensaje. Cuando el test corre en paralelo, aparecen síntomas conocidos:
- Dos pods leen el mismo mensaje y uno consume la evidencia del otro.
- Un job termina antes de que el buzón reciba el email.
- Un reintento crea una segunda dirección y hace imposible saber cual era la válida.
- El teardown elimina el recurso antes de conservar el asunto, el identificador y la hora de recepción.
La búsqueda temp mail mail puede llevar a soluciones rápidas, pero en CI importa más la frontera de responsabilidad. Una cuenta de correo desechable puede servir para una comprobación controlada; no sustituye un contrato de ejecución, una política de datos ni un registro de eventos.
Separar la ejecución del buzón
Primero separo tres objetos que a menudo se mezclan:
- Solicitud de prueba: contiene el test, el commit, el namespace y el destinatario lógico.
- Lease de ejecución: concede durante un periodo corto el derecho a consumir una evidencia.
- Mensaje observado: guarda un identificador mínimo, el asunto normalizado, la hora y el resultado de las aserciones.
El destinatario puede ser una dirección temporal, pero la ejecución debe tener un run_id propio. Así, si el proveedor tarda o el pod cae, un worker de recuperación puede retomar la misma intención. El estado del onboarding también merece un contrato explícito; este contrato de estados del onboarding es una referencia útil para pensar en transiciones y no solo en respuestas HTTP.
El run_id debe viajar en los logs estructurados, en las métricas y en los artefactos de CI. No conviene poner tokens completos en esos lugares. Para una investigación basta con un hash corto del destinatario, el commit y un identificador de ejecución.
Diseñar un lease para cada prueba
Un lease debe tener dueño, expiración y versión. En una tabla o almacén equivalente, el registro puede seguir este ciclo:
requested -> claimed -> waiting_for_message -> asserted -> released
\-> timed_out
El worker reclama una ejecución con una operación atómica. El filtro debe comprobar que no existe un lease vigente y que el intento anterior no superó su límite. Si el pod desaparece, la expiración vuelve a hacer elegible el registro. Esto es mas seguro que confiar en un finally del test, porque Kubernetes puede terminar un contenedor sin darle tiempo suficiente.
Una consulta conceptual tendría estas condiciones:
UPDATE email_test_runs
SET lease_owner = :worker,
lease_until = CURRENT_TIMESTAMP + INTERVAL '90 seconds',
attempt = attempt + 1,
status = 'claimed'
WHERE run_id = :run_id
AND (lease_until IS NULL OR lease_until < CURRENT_TIMESTAMP)
AND attempt < max_attempts
RETURNING run_id, lease_until, attempt;
En PostgreSQL real, la actualización debe estar protegida por una transacción y una condición que impida que un worker viejo complete el registro después de perder su lease. Si se usan colas, SKIP LOCKED ayuda a repartir trabajo sin que los workers esperen por filas bloqueadas.
Métricas y límites para SRE
La señal principal no es solo el porcentaje de tests verdes. Para una operación SRE conviene observar:
- tiempo entre
requestedymessage_received; - porcentaje de ejecuciones que expiran el lease;
- reintentos por proveedor, namespace y versión del test;
- mensajes encontrados pero no coincidentes con el
run_id; - edad de la cola y cantidad de ejecuciones en
timed_out.
Un presupuesto de reintentos evita que una API lenta congele todo el pipeline. Después de dos o tres intentos, es mejor mover la ejecución a una cola de mensajes fallidos y adjuntar el artefacto de diagnóstico. Un circuit breaker también evita que cientos de pods golpeen al mismo proveedor cuando ya hay señales de indisponibilidad.
La configuración debe incluir límites claros: TTL del buzón, tiempo máximo de espera, tamaño de lote y política de limpieza. Conviene probar la configuracion con un caso de terminación abrupta del pod, no solo con un test feliz. Una métrica sin un umbral operativo es una señal bonita, pero no una alerta util.
Preguntas frecuentes
¿Un lease garantiza que nunca se duplique un email?
No. Garantiza una propiedad de concurrencia durante su vigencia. Si el proveedor acepta el mensaje y el worker muere antes de registrar la respuesta, el reintento todavía puede duplicarlo. Para reducir el impacto, usa una clave idempotente cuando el proveedor la soporte y haz que el test acepte un conjunto limitado de mensajes equivalentes.
¿Debo crear un buzón por pod?
No necesariamente. Un buzón por ejecución suele ser más fácil de aislar, pero aumenta la limpieza y el coste operativo. La decisión depende del volumen, la retención y la posibilidad de filtrar por destinatario, asunto y run_id.
¿Qué debe conservar CI?
Conserva el run_id, el resultado de cada aserción, el timestamp de recepción y un resumen del mensaje. Evita guardar el cuerpo completo si contiene datos personales. Para búsquedas como fake e mail com, una nota en el contexto de la prueba puede ayudar a rastrear intención, pero no debe convertirse en un dato de producción.
Checklist de implementación
Antes de desplegar este patrón, revisa:
- Cada ejecución tiene un
run_idúnico y una política de expiración. - La reclamación es atómica y el worker verifica su lease al completar.
- Los reintentos tienen límite, backoff y clasificación de errores.
- Los artefactos contienen evidencia suficiente sin exponer tokens.
- Hay métricas para latencia, expiración, cola y mensajes huérfanos.
- Un job de limpieza elimina buzones y registros después de su TTL.
- Se simula la caída de un pod durante la espera y durante la aserción.
Los formularios que disparan estas pruebas también necesitan estados comprensibles para el usuario; los formularios inclusivos y rápidos muestran por qué esa señal no debería quedar escondida detrás de un spinner. Cuando la evidencia de email tiene dueño, expiración y contexto, el pipeline deja de ser una caja negra y la operacion se vuelve mucho más predecible.
Top comments (0)