Saltar al contenido principal

El bug que redujo a la mitad el throughput de un gateway sin un solo error

· 6 min de lectura
Rafael Fernandes
NLP Engineer & Tech Writer at WiLine
Share:
Gateways · AI NewsUn gateway perdiendo la mitad de su throughput sin reportar errores

Pfizer opera un gateway de IA — el único servicio con el que habla cada herramienta interna cuando quiere un modelo. Durante una verificación rutinaria de actualización, su throughput cayó a la mitad. Sin errores. Sin peticiones fallidas. Nada raro en ningún dashboard.

Publicaron lo que encontraron junto al equipo de LiteLLM, y la causa es lo bastante pequeña como para caber en un párrafo: alguien escribió una opción que decía no cifres esta conexión, y el sistema la cifró igual.

Qué salió mal exactamente​

El gateway mantiene una caché — un almacén lateral rápido llamado Redis que consulta antes de hacer trabajo costoso. La conexión a esa caché puede ir cifrada o no, y eso es una opción que escribes en un archivo de configuración.

El operador escribió la opción en off. Conexión plana, sin cifrado.

El código que leía esa opción comprobaba si la opción existía, no a qué estaba puesta. Escribir off crea la opción. La opción ahora existe. Así que la comprobación pasó, y el gateway abrió una conexión cifrada — contra una caché que no esperaba ninguna.

Aquí está la parte que lo vuelve peligroso y no solo incorrecto. Cuando un programa intenta iniciar una conversación cifrada con algo que no habla cifrado, no lo rechazan. Dice hola y espera una respuesta que nunca llega. No rechazado — ignorado.

Así que la caché no estaba caída. La caché estaba lenta. Y lo lento es mucho más difícil de ver.

Las cuatro líneas​

Para quien quiera mirarlo directamente, esto es get_redis_connection_pool tal como estaba en la versión afectada:

connection_class = async_redis.Connection
if "ssl" in redis_kwargs:
connection_class = async_redis.SSLConnection
redis_kwargs.pop("ssl", None)
redis_kwargs["connection_class"] = connection_class

"ssl" in redis_kwargs pregunta ¿existe esta clave?. Existe — la creaste tú al ponerla en false. La rama se ejecuta, y obtienes la conexión cifrada que acabas de desactivar explícitamente.

Omite la opción por completo y todo funciona. Escríbela y di que no, y se rompe. La configuración cuidadosa es la que está rota.

Por qué nadie recibió un error​

Cada capa por encima de la caché está construida para tolerar que la caché no responda, y ese diseño es correcto — si la caché no está disponible, haces el trabajo por la vía lenta y sigues adelante. Que es precisamente lo que convierte esto en silencio:

Redis timeout errors appeared in the proxy's internal logs, but the gateway still returned HTTP 200 to every caller

HTTP 200 significa éxito. Cada una de las peticiones reportó éxito, durante todo el episodio. El post es preciso sobre dónde cayó el daño:

Redis operations were stalling on TLS handshake timeouts in the async hot path, degrading throughput without surfacing HTTP-level errors

Las cifras reportadas: throughput de unas 300 peticiones por segundo a unas 156 — aproximadamente la mitad — con tiempo de respuesta mediano bajo carga en 4.200 ms, medido contra 750 usuarios concurrentes simulados durante una ejecución sostenida de 60 segundos.

Esas mediciones vienen de las pruebas internas de Pfizer y se reportan en el post, no son reproducibles de forma independiente. El código es otra cosa — puedes abrir el archivo en esa versión y leerlo tú mismo.

Su propio resumen en una línea es la mejor frase del artículo:

A throughput drop with a 0% HTTP error rate is the worst kind of regression to catch after the fact

El bug ya estaba ahí​

Quise saber si esto llegó con la actualización que lo expuso. No fue así. Comparé el archivo relevante entre la versión que rendía bien y la que no — son idénticos byte a byte. El post dice lo mismo:

The underlying Redis bug existed in older versions too, but surfaced during Pfizer's v1.89.2 upgrade validation under this workload

Ese es el detalle que vale la pena masticar. Esto no era una release mala que pudieras revertir. El fallo llevaba tiempo en el código de conexión, atravesando versiones que rendían perfectamente bien en los benchmarks. Necesitó una configuración concreta y una carga concreta antes de hacer daño visible — y entonces costó la mitad de la capacidad de un gateway en producción sin levantar nada.

Cómo lo encontraron en realidad​

No con monitoreo, y no rastreando cambios recientes de código. Apagando y encendiendo funcionalidades hasta que el número se movió:

Enabled Redis caching. Median latency jumped to 4,200ms. That isolated it to the Redis connection path.

Un equipo con tráfico real en producción, observabilidad real y línea directa con el proveedor encontró esto a mano. No había señal en la tasa de error, porque no había errores. Nada en los códigos de estado, porque todos decían éxito. El único lugar donde aparecía era el tiempo de respuesta bajo carga sostenida — que solo ves si vas a buscarlo a propósito.

Eso coincide con lo que me encontré a una escala mucho menor cuando hice un load test a un gateway para encontrar el atasco que la mediana esconde: en este tipo de infraestructura, los fallos que más te cuestan son los que nunca levantan un error. Un callback bloqueante y una conexión atascada esperando un handshake son bugs completamente distintos con la misma huella — el throughput se desploma, las peticiones más lentas se vuelven mucho más lentas, y la tasa de error nunca se mueve.

Debo ser honesto sobre los límites aquí: no opero nada a la escala de Pfizer, así que no puedo reproducir sus mediciones. Lo que sí puedo hacer es leer el código, y el código es público.

El arreglo​

Una línea, en la versión actual:

if redis_kwargs.pop("ssl", None):
redis_kwargs["connection_class"] = async_redis.SSLConnection

Esta versión lee el valor de la opción en lugar de limitarse a notar que existe. Off ahora significa off.

Está registrado como "Redis ssl handling — Presence check → value check" bajo LIT-4307 y PR #32590, publicado en v1.93.0. El título del pull request lo dice sin rodeos: honor ssl value instead of key presence when building async connection pool.

Qué llevarse de esto​

Dos cosas, si operas algo con una caché detrás.

Comprueba qué hizo tu sistema en realidad, no qué le dijiste que hiciera. Escribir una opción y que esa opción surta efecto son eventos distintos, y este es un ejemplo limpio de esa brecha. La conexión que obtuvo Pfizer era la contraria a la que pidieron, y nada en ninguna parte lo dijo.

Si tus pruebas miden promedios, no pueden ver esto. Tampoco las alertas construidas sobre tasas de error. Carga sostenida, tiempos de las peticiones más lentas, y throughput comparado contra una línea base conocida son lo que convierte un atasco silencioso en algo visible.

Los dashboards siguieron en verde durante todo el episodio. Eso no es una brecha de monitoreo que cierres añadiendo otra alerta sobre errores — no había ninguno sobre el que alertar.

Comments & questions

Hit an error, spotted a typo, or have a question? Leave a note below.