Cómo OpenAI Habitat evolucionó para soportar una escala de miles de millones
OpenAI Habitat evolucionó de una biblioteca cliente de Python a un servicio centralizado reescrito en Rust para afrontar los problemas de coordinación, latencia, memoria y concurrencia de una infraestructura a gran escala.
Herramientas relacionadas
Todo empieza con una pregunta sencilla
Casi todos los sistemas backend comienzan siguiendo un patrón similar: los servicios de negocio se conectan directamente a la base de datos.
Cuando hay pocos usuarios y pocos servicios, este modelo funciona muy bien. El cliente de la base de datos se encarga de gestionar las conexiones y el código de negocio se ocupa de las consultas. Es simple, directo y rápido de desarrollar.
El problema aparece cuando el sistema crece hasta cientos de millones de usuarios y decenas de servicios necesitan acceder al mismo sistema de almacenamiento.
En ese momento, la estructura de costes cambia por completo. El problema no es necesariamente que la base de datos se haya vuelto más lenta, sino que empiezan a empeorar simultáneamente tres aspectos: el coste de coordinación, la propagación de fallos y la previsibilidad del rendimiento.
La plataforma de almacenamiento Habitat de OpenAI ha pasado por toda esta evolución: primero fue una biblioteca cliente de Python integrada dentro de los servidores principales de ChatGPT; después se convirtió en un servicio independiente y finalmente fue reescrita en Rust.
Entender las decisiones técnicas detrás de esta evolución resulta mucho más interesante que limitarse a saber qué hace Habitat.
Por qué la conexión directa entre el cliente y la base de datos deja de funcionar a gran escala
El diseño inicial de Habitat tenía una idea muy clara: los ingenieros de producto no deberían tener que preocuparse por la gestión de la base de datos.
Habitat funcionaba como una biblioteca cliente de Python dentro de los servidores principales de ChatGPT y encapsulaba tareas como la resolución del schema, el enrutamiento, la autorización, el cifrado, la serialización, la transformación de solicitudes y la gestión del pool de conexiones.
El desarrollador solo tenía que utilizar la API de Habitat. No necesitaba saber dónde estaban almacenados los datos ni qué base de datos se utilizaba.
En las primeras etapas, este diseño era perfectamente razonable.
Aceleraba el desarrollo, simplificaba el uso y evitaba que cada equipo de producto tuviera que implementar por separado la misma infraestructura.
El problema apareció cuando aumentó el número de servicios.
Si decenas de servicios incorporan cada uno su propia copia de la biblioteca Habitat, cualquier cambio en la infraestructura subyacente requiere actualizar todos esos servicios.
El artículo describe un caso concreto: el equipo quería migrar determinados conjuntos de datos críticos a una base de datos distribuida por regiones. Para hacerlo, era necesario añadir la lógica de enrutamiento a la biblioteca cliente y después esperar a que todos los servicios hubieran sido desplegados antes de activar el feature flag.
El proceso tardó varios días y durante la migración incluso se produjo una interrupción porque uno de los equipos hizo rollback de su servicio.
Esto no es un problema exclusivo de OpenAI. Es una consecuencia general de la arquitectura distribuida:
cuando una capacidad se coloca en el cliente, el coste de coordinación crece linealmente con el número de clientes; cuando se concentra en un servicio, aparece un único punto de control y el coste de cambio pasa de N clientes a un solo servicio.
Por qué Habitat pasó de biblioteca cliente a servicio independiente
La transformación de Habitat consiste esencialmente en cambiar de un modelo de “cliente distribuido” a un modelo de “servicio centralizado”.
El servicio independiente modifica muchos aspectos de la arquitectura al mismo tiempo:
Despliegue centralizado
Enrutamiento centralizado
Gestión unificada de permisos
Auditoría centralizada
Monitorización
Control de tráfico
Gestión de fallos
Las políticas de seguridad y control de acceso dejan de depender de código distribuido entre múltiples clientes y pasan a imponerse directamente en la capa de servicio.
Además, el servicio puede actualizarse de forma independiente, sin coordinar el despliegue de decenas de clientes.
Pero este cambio tiene un coste evidente.
Ahora existe una capa adicional de comunicación de red. Eso implica más latencia, consumo de CPU y memoria, además del coste de serialización y deserialización.
Más importante todavía: el servicio independiente tiene que gestionar por sí mismo las conexiones y el tráfico en escenarios de alta concurrencia.
En el modelo anterior, cada servicio de negocio gestionaba sus propias conexiones. Era un modelo más distribuido: menos centralizado y potencialmente más difícil de administrar, pero también sin un único punto de presión.
El diseño arquitectónico no consiste simplemente en maximizar el rendimiento. Consiste en encontrar un equilibrio entre rendimiento, mantenibilidad, fiabilidad y velocidad de desarrollo.
Habitat aceptó cierto coste de rendimiento a cambio de obtener un punto de control centralizado. Cuando el sistema alcanza una escala de miles de millones de usuarios, este intercambio puede tener mucho sentido.
Por qué la latencia de cola importa más que la latencia media
Este es uno de los conceptos fundamentales para entender los problemas de rendimiento de Habitat.
Imagina que una solicitud de usuario necesita realizar 100 operaciones sobre la base de datos.
Cada operación tarda de media 10 ms. Pero si una de ellas tarda 500 ms, el tiempo percibido por el usuario estará determinado en gran medida por esa operación lenta, además del resto de llamadas.
En otras palabras, una sola llamada lenta puede determinar la experiencia completa de la solicitud.
En sistemas distribuidos, esto se conoce como fan-out amplification.
Si una solicitud se divide en N llamadas secundarias y cada una tiene un p99 de latencia del 1 %, existe una probabilidad del 1 % de que cada llamada sea lenta.
Cuando N = 100, la probabilidad de que al menos una llamada alcance esa cola de latencia es:
1 - (1 - 0,01)^100 ≈ 63 %
Eso significa que una llamada que individualmente solo “se ralentiza ocasionalmente” puede convertirse, debido al fan-out, en una experiencia habitual para los usuarios.
El escenario de Habitat es precisamente este tipo de sistema.
Una sola operación de usuario puede provocar cientos de búsquedas de datos. Incluso si Cosmos DB responde rápidamente, factores como la latencia del event loop de Habitat, el comportamiento del pool de conexiones y el coste de serialización pueden convertirse en fuentes de latencia de cola.
Por eso:
aunque la latencia media parezca normal, p99 y p99,9 suelen ser mucho más representativos de la experiencia real del usuario.
Por qué Python terminó convirtiéndose en un cuello de botella
Elegir Python para el servicio de Habitat era razonable en las primeras etapas. Los equipos de producto ya conocían el lenguaje y permitía desarrollar rápidamente.
Pero Python presenta algunas limitaciones estructurales cuando se utiliza como runtime de un servicio de red altamente concurrente.
La primera es el GIL (Global Interpreter Lock).
Incluso utilizando asyncio, en un proceso de Python solo puede ejecutarse un hilo de bytecode de Python a la vez. El event loop de asyncio utiliza un único hilo para programar las coroutines.
Cuando una coroutine realiza una operación intensiva en CPU, las demás no pueden ejecutarse realmente en paralelo dentro de ese mismo proceso.
La ventaja de asyncio aparece principalmente mientras el programa está esperando operaciones de I/O. Durante los cálculos de CPU, la ejecución sigue siendo esencialmente secuencial.
Y Habitat no se limita a reenviar solicitudes de red.
Tiene que realizar tareas como:
Cálculo de rutas
Compresión
Cifrado
Verificación y cálculo de checksums
Serialización
Comprobaciones de salud de servicios downstream
Muchas de estas operaciones consumen CPU.
Cuando una gran cantidad de solicitudes entra simultáneamente en el event loop, algunas tareas pueden ocupar el procesador durante demasiado tiempo y retrasar la ejecución de las demás coroutines.
El resultado es event loop latency.
La I/O asíncrona no significa que el procesamiento de CPU sea gratuito.
El propio event loop debe monitorizarse. Bajo una carga elevada, su retraso de planificación puede alcanzar cientos de milisegundos o incluso varios segundos, convirtiéndose directamente en latencia de cola perceptible para el usuario.
Cómo un pool de conexiones puede crear un bucle de retroalimentación con LIFO
Este es probablemente uno de los casos con mayor valor práctico de ingeniería descritos en el artículo.
Un pool de conexiones permite reutilizar conexiones de base de datos existentes.
Establecer una conexión TCP tiene un coste, por lo que reutilizar conexiones ya abiertas evita parte de ese trabajo.
La estrategia utilizada para extraer conexiones del pool determina el orden en el que se reutilizan.
LIFO (Last In, First Out) significa que la última conexión devuelta al pool es la primera que se vuelve a utilizar.
En un entorno de una sola máquina, esto puede ser beneficioso. La conexión utilizada recientemente puede seguir estando caliente y ser más eficiente de reutilizar.
Pero en un sistema distribuido, LIFO puede generar un bucle de retroalimentación positiva.
Supongamos que un servidor está sometido a una carga elevada y empieza a responder más lentamente.
Las conexiones utilizadas por ese servidor tardan más en regresar al pool. Como LIFO prioriza precisamente las conexiones devueltas más recientemente, esas conexiones pueden volver a utilizarse con mayor frecuencia y continuar enviando tráfico hacia el mismo servidor que ya está sobrecargado.
El ciclo sería:
servidor más ocupado → conexiones regresan más tarde → esas conexiones se reutilizan con mayor frecuencia → el tráfico se concentra aún más → servidor todavía más ocupado.
El sistema puede terminar entrando en un estado conocido como fallo metaestable: aparentemente el servicio sigue funcionando, pero la distribución de carga está gravemente desequilibrada y la latencia de cola continúa empeorando.
El equipo de Habitat cambió la estrategia del pool de LIFO a FIFO (First In, First Out).
De esta manera, las conexiones se reutilizan de forma más uniforme y se rompe el bucle de retroalimentación.
Este caso demuestra algo importante:
la elección de un algoritmo no solo afecta al rendimiento de una operación individual; también puede cambiar la dinámica de distribución de carga de todo un sistema distribuido.
Qué solucionó realmente la reescritura en Rust
Durante el segundo trimestre de 2026, dos ingenieros reescribieron el servicio Habitat en Rust con ayuda de Codex y GPT-5.5.
El nuevo servicio procesa actualmente el 95 % de las solicitudes de producción. Según los datos publicados, la versión en Rust ofrece una eficiencia de CPU 6 veces superior a la versión en Python y una eficiencia de memoria 15 veces superior. Tanto la latencia media como la latencia de cola también se redujeron considerablemente.
La mejora puede dividirse en varios niveles.
Gestión de memoria
Rust no utiliza un recolector de basura tradicional, por lo que la asignación y liberación de memoria son más deterministas.
En Python, la gestión automática de memoria y el comportamiento del GC pueden introducir pausas menos predecibles bajo cargas elevadas, y esas pausas pueden convertirse directamente en latencia de cola.
El modelo de ownership de Rust permite detectar muchos problemas de concurrencia durante la compilación y evita depender de pausas de garbage collection durante la ejecución.
Coste del runtime
Rust se compila a código máquina nativo y no necesita una capa de intérprete equivalente a la de Python.
El event loop de asyncio tiene que programar coroutines, gestionar colas de tareas y procesar callbacks. Bajo una carga elevada, estas operaciones pueden convertirse en un cuello de botella.
Los runtimes asíncronos de Rust, como Tokio, también tienen costes de planificación, pero el coste general del runtime puede ser considerablemente menor.
Modelo de concurrencia
Rust puede aprovechar realmente varios núcleos de CPU en paralelo.
El GIL de Python limita el paralelismo de determinadas operaciones intensivas en CPU. En Habitat, precisamente algunas tareas importantes —como cifrado, compresión y verificación— tienen este perfil.
El modelo de concurrencia de Rust permite ejecutar estas operaciones en múltiples núcleos sin las mismas restricciones del GIL.
Procesamiento de red
Las abstracciones de coste cero y el control más preciso de la memoria permiten que Rust gestione la I/O de red de forma eficiente.
Esto puede traducirse en menos copias de memoria, menos asignaciones innecesarias y estructuras de datos más compactas.
Sin embargo, hay que evitar una conclusión demasiado simplista:
Rust no es automáticamente la mejor opción para cualquier servicio backend.
La reescritura tiene sentido cuando existen cuellos de botella observables de CPU, memoria o latencia.
En el caso de Habitat, el cambio se produjo después de que el servicio Python ya hubiera mostrado problemas de rendimiento medibles.
Por qué la API NoSQL limita deliberadamente sus capacidades
Habitat no expone consultas SQL arbitrarias.
En su lugar, construye su API alrededor de objetos y relaciones definidas por el cliente.
A primera vista, SQL parece mucho más flexible. Un equipo de producto puede escribir cualquier consulta que necesite.
Pero en un sistema de escala extremadamente grande, las consultas arbitrarias introducen numerosos problemas:
Consumo de CPU impredecible
Patrones de acceso al disco impredecibles
Posibles escaneos de grandes cantidades de datos
Aumentos repentinos de latencia
Mayor dificultad para planificar capacidad
Riesgo de que una única consulta afecte a todo el sistema
Por eso:
limitar las capacidades de una API no significa necesariamente construir un sistema más débil. A veces significa construir un sistema más predecible.
La previsibilidad es una de las propiedades fundamentales de una infraestructura a gran escala.
Solo cuando el comportamiento del sistema es suficientemente predecible se pueden realizar correctamente la planificación de capacidad, la definición de SLO y el aislamiento de fallos.
Para los clientes que realmente necesitan consultas complejas, Habitat utiliza Change Data Capture (CDC) para sincronizar datos casi en tiempo real con instancias independientes de Rockset.
La base de datos online se encarga de las solicitudes de negocio estables y de baja latencia, mientras que Rockset se utiliza para consultas complejas, análisis y cargas intensivas de lectura.
Es un ejemplo clásico de separación entre cargas transaccionales online y cargas analíticas.
Principios de arquitectura que podemos extraer de este caso
La arquitectura debe evolucionar con la escala.
La solución óptima durante las primeras etapas de un producto no necesariamente sigue siendo la mejor cuando el sistema crece. Una biblioteca cliente puede ser muy eficiente cuando existen pocos servicios, mientras que un servicio independiente puede ser más adecuado cuando el número de clientes aumenta.
Un punto de control centralizado puede reducir el coste de coordinación.
Concentrar una capacidad importante en un servicio de infraestructura permite pasar de coordinar N clientes a gestionar un único punto de control.
La latencia de cola suele importar más que el rendimiento medio.
Cuando una solicitud depende de numerosas llamadas downstream, p99 y p99,9 son indicadores mucho más relevantes de la experiencia real del usuario.
Asíncrono no significa sin cuellos de botella de CPU.
El propio event loop debe monitorizarse y optimizarse. La ventaja de asyncio se encuentra principalmente durante la espera de I/O; el procesamiento intensivo de CPU continúa estando limitado por el modelo de ejecución.
Limitar capacidades puede mejorar la previsibilidad.
No todos los sistemas necesitan ofrecer la máxima flexibilidad posible. En una infraestructura a gran escala, un comportamiento predecible puede ser mucho más valioso que una API completamente abierta.
Las cargas online y analíticas deberían mantenerse separadas.
Una consulta analítica pesada no debería afectar a la latencia y estabilidad de las solicitudes críticas del sistema.
La optimización de rendimiento debe basarse en observabilidad.
El equipo de Habitat utilizó CPU profiling para descubrir que el análisis periódico de JSON relacionado con la configuración de feature flags de Statsig estaba provocando problemas de latencia de cola.
Este tipo de problema sería muy difícil de localizar únicamente mediante intuición.
La estrategia correcta es primero encontrar el verdadero cuello de botella y después decidir si merece la pena optimizar el componente existente o reescribirlo.
La deuda técnica puede asumirse deliberadamente, pero hay que saber cuándo pagarla.
Elegir Python en las primeras etapas para acelerar el desarrollo no fue necesariamente un error.
La cuestión importante es reconocer las señales que indican que la arquitectura ya no encaja con la escala actual.
Cuando la eficiencia de CPU se convierte en un cuello de botella, la latencia de cola continúa empeorando y el equipo empieza a dedicar una cantidad desproporcionada de tiempo a optimizar rendimiento en lugar de desarrollar nuevas funciones, puede haber llegado el momento de pagar esa deuda técnica.