Cloudflare pasó seis semanas rastreando una condición de carrera en la biblioteca HTTP hyper que truncaba respuestas de imagen en el edge—silenciosamente, con estado 200 y sin registros de error—antes de corregirla en cuatro líneas de código. El análisis post-mortem, publicado el 22 de junio de 2026 por los ingenieros Deanna Lam, Diretnan Domnan y Matt Lewis, muestra cómo los cambios en la ruta de infraestructura exponen bugs de timing latentes.

El servicio Images está escrito en Rust, se ejecuta en Workers y se despliega en todas las máquinas de la red edge global de Cloudflare. Utiliza hyper, la biblioteca HTTP Rust de código abierto, para gestionar conexiones. En diciembre de 2025, el equipo rediseñó el binding: la ruta original enrutaba solicitudes a través de FL, un intermediario interno que manejaba seguridad y enrutamiento sobre sockets de red estándar. La nueva ruta reemplazó FL con un binding de worker interno co-localizado en la misma máquina, comunicándose sobre Unix domain sockets. El objetivo era reducir la latencia y desacoplar el ciclo de release de Images del de FL.

En cuestión de días, los reportes de clientes llegaron. Las solicitudes de transformación fallaron intermitentemente para imágenes más grandes. Las respuestas devolvieron HTTP 200 sin error en ningún lugar de la pila. Una respuesta de 2 MB podría llegar como unos pocos cientos de kilobytes—los datos de imagen se detuvieron. Sin pánico, sin timeout, sin 5xx.

El primer reporte confirmado vino de un cliente ejecutando dos pipelines anidados: un binding Images interno que componía un fondo JPEG grande y overlays PNG del R2, alimentando un pipeline URL-interface externo para escalado y conversión de formato. El único error visible surgió un nivel arriba: `end of file before message`. El pipeline interno devolvió un cuerpo truncado con un 200 limpio.

La condición de carrera vivía en la secuencia de shutdown de hyper. Cuando el servicio Images codifica un resultado, entrega el bloque completo en memoria a hyper, que lo almacena internamente antes de hacer flush al búfer de salida del socket. Si el lector se mantiene al día, hyper hace flush en una sola pasada e emite shutdown para señalar que la conexión ha terminado. Si el lector es más lento, el búfer de salida se llena e hyper espera espacio. La carrera: hyper podría emitir shutdown antes de que el flush se completara, cerrando la conexión antes de que se entregaran todos los bytes. La ruta anterior FL + socket de red introducía latencia suficiente para enmascarar la carrera. La ruta Unix socket—misma máquina, overhead cercano a cero—cambió el envelope de timing lo suficiente para dispararla consistentemente.

La corrección tocó cuatro líneas en hyper: asegurando que el flush se complete antes de que se emita shutdown.

Para arquitectos, este modo de fallo es severo: ninguna alerta se dispara, el código de estado miente, y el truncamiento es proporcional al tamaño de la imagen, haciéndolo invisible en pruebas de payload pequeño. El disparo—cambiar de sockets de red a Unix domain sockets—es exactamente lo que muchos equipos hacen al co-localizar servicios: patrones sidecar, rutas locales de service mesh, bindings estilo Workers. Los cambios de transporte de menor latencia alteran las suposiciones de timing que los autores de biblioteca pueden haber probado solo contra rutas más lentas. Cualquier biblioteca HTTP que gestione secuencias flush-then-shutdown es candidata para el mismo bug. Audita tu propia pila antes de que el buscapersonas lo haga.

Escrito y editado por agentes de IA · Methodology