Escalar rápido el almacenamiento en línea para más de 1 000 millones de usuarios de ChatGPT
Cómo adaptamos en Python nuestra plataforma de almacenamiento de aplicaciones, Habitat, para gestionar un crecimiento sin precedentes.
Por Jon Lee, Chaomin Yu y Ben Ries, integrantes del personal técnico
Todos los productos de OpenAI dependen de un acceso rápido y confiable a los datos, ya sea cuando alguien inicia sesión, consulta su configuración de Codex o comienza una nueva conversación en ChatGPT. Cada una de esas acciones puede requerir muchas consultas de datos independientes antes de que el producto pueda responder. Si esas solicitudes son lentas, el producto se siente lento. Si esas solicitudes fallan, el producto deja de funcionar por completo.
Habitat es la plataforma de almacenamiento en línea que creamos para que los productos de OpenAI accedan de forma rápida y confiable a la información que necesitan. Habitat ya gestiona más de 70 millones de solicitudes por segundo y respalda productos que más de 1 000 millones de personas usan cada semana en casi 40 regiones geográficas. Habitat se lanzó originalmente para respaldar los GPT en DevDay 2023 como una sencilla biblioteca de Python del lado del cliente conectada a una sola base de datos. Hoy es un sistema distribuido complejo que gestiona más de 500 petabytes de datos.
Figura 01 · ¿Qué es Habitat?
Plataforma de almacenamiento en línea
Habitat es la plataforma de almacenamiento en línea que creamos para que los productos de OpenAI accedan de forma rápida y confiable a la información que necesitan.
- Solicitud
- Respuesta
- Cambios (CDC)
Construir y operar infraestructura a esta escala no es tarea fácil, aunque tampoco resulta especialmente difícil. Lo singular de nuestra situación fue la velocidad sin precedentes con la que tuvimos que escalar para responder al asombroso crecimiento de usuarios y de la demanda de productos, mientras construíamos una plataforma madura. A menudo, los ingenieros de sistemas diseñan para una escala 10 veces mayor y esperan que sea suficiente durante algunos años mientras se preparan para el siguiente aumento de 10 veces. En nuestro caso, crecimos más de 10 veces interanualmente durante los últimos tres años. Como resultado, construir y operar Habitat ha exigido una serie de decisiones tácticas y una secuencia cuidadosa: comprender cada componente al nivel más profundo para exprimir al máximo nuestra arquitectura existente, mientras sorteábamos limitaciones de almacenamiento y capacidad de cómputo para ganar tiempo destinado a inversiones fundamentales.
- 70 M+
de solicitudes por segundo
- 1 000 M+
de personas cada semana
- 500 PB+
de datos
A medida que OpenAI crecía, Habitat debía crecer a la par: primero, debía ser lo bastante confiable para el tráfico esencial de los productos; luego, lo bastante rápido para usuarios de todo el mundo; y, finalmente, debía operar con agilidad a una escala masiva. Esta publicación es la primera de una serie de dos partes sobre cómo escalamos el almacenamiento en línea. Aquí explicaremos cómo evolucionó Habitat, por qué lo convertimos de una biblioteca en un servicio y cómo llevamos un servicio escrito en un lenguaje poco habitual para este fin —Python— hasta convertirlo en una capa confiable de plataforma de almacenamiento.
En una futura publicación, detallaremos cómo logramos la confiabilidad multitenencia a escala, nuestra estrategia por capas para optimizar el rendimiento de lectura y cómo ampliamos nuestra colaboración con Azure Cosmos DB para gestionar con confiabilidad una demanda sin precedentes.
Habitat nació de una idea sencilla: los ingenieros de producto no deberían tener que pensar en administrar bases de datos. Habitat se lanzó originalmente para respaldar los GPT en DevDay 2023 como una pequeña biblioteca de Python que interactuaba con el servidor principal de ChatGPT. Admitía un pequeño conjunto de operaciones que internamente se asignaban a la aplicación de base de datos Azure Cosmos DB.
La función de la biblioteca era ofrecer a los equipos de producto una forma sencilla de almacenar y recuperar datos sin tener que dominar los detalles subyacentes. Habitat se ocupaba del trabajo necesario: determinar qué tipo de datos estaban involucrados, de dónde debían provenir —o adónde debían ir—, si la solicitud estaba permitida, etcétera.
Los ingenieros de producto no tenían que preocuparse por consultar esquemas, enrutar, autorizar, cifrar, serializar, dar forma a solicitudes ni agrupar conexiones. Ni siquiera tenían que considerar de dónde provenían los datos: Azure Cosmos DB, cachés u otros tipos de almacenamiento.
Figura 02 · Servicio Habitat
Flujo simplificado de solicitudes de Habitat
Al separar la lógica de almacenamiento en un servicio independiente, establecimos un único punto de control para implementaciones, observabilidad y mejoras de la plataforma.
- Solicitud
- Respuesta
Esta biblioteca de Python funcionó bien y Habitat tuvo una rápida adopción entre los ingenieros de producto de OpenAI, pese a que no hubo una iniciativa centralizada para abandonar el uso de Postgres y Azure Cosmos DB de autoservicio.
A medida que evolucionaban las necesidades de los productos, a sus desarrolladores incluso les resultaba fácil añadir a la biblioteca compartida funciones como almacenamiento en caché, compresión o cifrado del lado del cliente.
A mediados de 2025, Habitat había alcanzado sus límites como implementación del lado del cliente. A medida que la capa de Habitat se volvía más compleja y aumentaba la cantidad de servicios de OpenAI, los cambios de protocolo compatibles con versiones anteriores dejaron de ser viables.
En una ocasión, quisimos reducir el alcance del impacto de una interrupción en una sola región para nuestros conjuntos de datos más críticos, migrándolos a varias cuentas de Azure Cosmos DB distribuidas regionalmente. Este cambio exigía añadir lógica de enrutamiento al cliente, desactivarla mediante un indicador de función, implementarla en todos los clientes y, luego, activar el indicador.
Coordinar implementaciones en decenas de servicios y colaborar con cada equipo para desplegarlas tomó días. Antes de habilitarla, concluimos que debíamos añadir replicación oculta para verificar que la lógica de fragmentación fuera correcta. Implementarlo tomó otro par de días. ¿Corregir un error que descubrimos? Otro par de días. Finalmente, estábamos listos para activar el indicador, pero uno de los equipos revirtió su servicio por motivos ajenos a una versión anterior y defectuosa del cliente, lo que provocó precisamente la interrupción que tanto nos habíamos esforzado por evitar.
Los cambios en la biblioteca cliente exigían una coordinación compleja entre decenas de servicios, un proceso cada vez más frágil, ineficiente y propenso a fallas operativas. Para reducir esta dispersión operativa en futuras implementaciones, decidimos convertir Habitat en un servicio independiente.
Al separar la lógica de almacenamiento en un servicio independiente, establecimos un único punto de control para implementaciones, observabilidad y mejoras de la plataforma. En lugar de administrar actualizaciones fragmentadas, podíamos implementar mejoras de forma centralizada y beneficiar de inmediato a todos los productos de OpenAI.
Un servicio centralizado también nos ofrece un único punto de control para aplicar las protecciones más sólidas de seguridad y privacidad de datos. El servicio Habitat nos permite aplicar de forma centralizada políticas de control de acceso, registrar auditorías y limitar el acceso a recursos de almacenamiento subyacentes como Azure Cosmos DB. Habitat desempeña un papel fundamental en la protección de los datos de los usuarios y en la prevención del acceso no autorizado por parte de actores externos, internos y agentes.
Sabíamos que necesitábamos un servicio, pero aún no queríamos migrar de Python, pese a la sobrecarga adicional que implicaba usarlo como servicio. Usar Python para un servicio de alto rendimiento aumentó la latencia de red y añadió costos considerables de escalamiento de CPU y memoria frente a la ejecución como biblioteca local. Además, sabíamos que las ineficiencias de Python serían inaceptables a una escala 100 veces mayor, por lo que una futura reescritura era casi inevitable.
Sin embargo, lo consideramos una incorporación estratégica de deuda técnica. Nuestro objetivo principal no era optimizar costos o recursos, sino eliminar los obstáculos para los desarrolladores de producto y estabilizar la plataforma. Al aceptar a corto plazo las desventajas de rendimiento de un servicio de Python, pudimos priorizar desafíos más inmediatos, establecer nuestras API principales y construir una infraestructura sólida.
También apostamos de forma calculada a que el rápido avance de nuestros propios modelos de programación simplificaría el camino técnico en el futuro. Apostamos a que, cuando fuera necesario migrar por completo de Python, Codex y GPT harían viable la migración. Con el tiempo, esa apuesta resultó acertada.
Ejecutar Habitat como servicio de Python no sería lo óptimo en términos de rendimiento, pero era necesario. Python nos permite avanzar rápido, pero eso no significaba que pudiéramos ignorar los riesgos y aceptar latencias mucho peores. Cuando una solicitud promedio de un usuario genera cientos de llamadas a bases de datos, el usuario percibe la más lenta. Descubrimos que el principal desafío de ejecutar un servicio de Python a esta escala es gestionar estas latencias de cola.
Asyncio ayuda a Python a ejecutar simultáneamente cargas limitadas por E/S, pero no permite sortear el GIL de Python ni ofrece paralelismo de CPU. Además de reenviar solicitudes con mucha E/S, Habitat gestiona numerosas responsabilidades y tareas en segundo plano que consumen mucha CPU: enrutamiento, compresión, cifrado, sumas de comprobación, comprobación del estado de servicios posteriores, replicación oculta de solicitudes y cobertura especulativa.
Con tantas cargas intensivas de CPU y tareas en segundo plano en nuestro servicio, el retraso de planificación de asyncio puede dominar fácilmente la latencia de cola de las solicitudes. Antes de optimizar el lanzamiento inicial, observamos en las trazas de solicitudes con latencia p99 o superior que, aunque el almacenamiento posterior respondía rápido, las solicitudes se detenían con frecuencia mientras esperaban que se reprogramara la corrutina responsable de analizar la respuesta.
Figura 03 · Medir el retraso de asyncio
La concurrencia no es paralelismo de CPU
Asyncio de Python permite procesar solicitudes simultáneamente, pero solo se ejecuta una solicitud a la vez en el hilo de CPU. Esto afecta considerablemente las latencias de las solicitudes cuando hay mucho trabajo de CPU.
Trabajo reducido de CPU
Pasos breves de Python; las esperas de E/S se superponenTrabajo elevado de CPU
Los pasos largos de Python mantienen esperando las respuestas listasEn los servicios de Python de OpenAI, además de medir las métricas habituales de uso y saturación de memoria, CPU, red y disco, es fundamental supervisar el bucle de asyncio y su nivel de actividad, y ajustar el sistema en consecuencia.
Al programar periódicamente tareas en segundo plano y registrar la diferencia entre el tiempo de ejecución previsto y el real, podemos medir empíricamente y en tiempo real el retraso de planificación del bucle de eventos. Con un uso elevado y muchas tareas costosas, incluso una cantidad moderada de solicitudes simultáneas por proceso basta para generar variaciones considerables en la planificación, de hasta cientos de milisegundos y, en casos extremos, varios segundos.
Por eso, hacemos que cada proceso atienda pocas solicitudes simultáneas y, en cambio, escalamos de forma masiva la cantidad de procesos de trabajo de Python.
Durante el lanzamiento inicial, el perfilado de CPU del servicio en vivo reveló una causa raíz del gran retraso de asyncio y de las altas latencias de cola resultantes: el análisis periódico del JSON de nuestras configuraciones de indicadores de funciones mediante Statsig, una herramienta para administrar estos indicadores, ejecutar pruebas A/B y mucho más.
De forma predeterminada, Statsig consultaba configuraciones actualizadas cada minuto, sin variación aleatoria, y la configuración incluía todas las reglas de producción de todos los servicios. Por otra parte, se decidió ejecutar hasta 8 procesos de Python por pod para aumentar el uso de CPU y reducir las latencias. En conjunto, esto significaba que, cada minuto, todos los trabajadores de cada pod se detenían en algún momento, dejaban de procesar las solicitudes en curso y dedicaban sus ciclos de CPU a analizar un enorme archivo de configuración.
La solución fue sencilla una vez que el perfilado de CPU nos ayudó a identificar la causa raíz: implementar una configuración más pequeña y específica, ampliar el intervalo de actualización y añadir cierta variación aleatoria a este tipo de tareas en segundo plano.
Para mantener bajo el retraso de asyncio, también es fundamental balancear bien las solicitudes entre los procesos de servidor; sin una optimización adecuada, la agrupación de conexiones puede terminar actuando en sentido contrario.
Con la agrupación de conexiones del lado del cliente, un solo proceso cliente que realiza muchas solicitudes simultáneas podría establecer apenas unas cuantas conexiones de servidor y, en consecuencia, enviar toda su carga a solo unos pocos procesos. Antes de ajustar nuestro balanceo de carga, el uso de nuestro servicio variaba mucho: algunos procesos de cola atendían entre 5 y 10 veces más solicitudes simultáneas que el promedio.
Lo descubrimos por casualidad durante un incidente: pese a detener al cliente que sobrecargaba parte de nuestro servicio, un subconjunto de procesos siguió degradado mucho después de la ráfaga de tráfico. De hecho, observamos que esos procesos sufrían una degradación descontrolada y recibían cada vez más solicitudes hasta que los reiniciábamos. Cuando un pod se sobrecargaba, algún comportamiento fijaba aún más tráfico en él. Era una clase de falla que algunos integrantes de nuestro equipo conocían bien por trabajos anteriores: falla metaestable(se abre en una nueva ventana).
Sospechamos del grupo de conexiones y lo comprobamos limitando la duración máxima de reutilización de las conexiones. Esto efectivamente redujo la degradación y confirmó que nuestra investigación iba por buen camino. Una investigación más profunda reveló que TCPConnector de aiohttp en Python reutiliza las conexiones mediante LIFO de forma predeterminada: la conexión devuelta más recientemente se selecciona para la siguiente solicitud. Normalmente, es una opción predeterminada razonable: reutilizar las conexiones recientes permite que las conexiones adicionales creadas para gestionar ráfagas de tráfico agoten su tiempo de espera mientras están inactivas, lo que reduce la sobrecarga de mantenerlas. En nuestro caso, generó una falla metaestable. Durante una ráfaga, las solicitudes enviadas a servidores más lentos y sobrecargados devolvían las conexiones al grupo más tarde. Por eso, las solicitudes posteriores las seleccionaban con más frecuencia y concentraban gradualmente más tráfico en los pods que ya tenían dificultades. Modificar el grupo de conexiones para usar la reutilización FIFO rompió este ciclo de retroalimentación e incluso redujo la variación de solicitudes en estado estable.
Figura 04A · Agrupación de conexiones del lado del cliente
LIFO devuelve el trabajo nuevo al proceso lento
Tras una ráfaga de solicitudes, los servidores más lentos devuelven al final las conexiones al grupo. LIFO hace que se concentre más trabajo en esos mismos servidores lentos.
Una ráfaga inicial llega a A, B y al proceso C, que es más lento.
Figura 04B · Agrupación de conexiones del lado del cliente
FIFO rompe el ciclo de retroalimentación de reutilización de conexiones
FIFO mantiene más conexiones activas después de una ráfaga, pero distribuye las cargas de forma equitativa entre todos los servidores.
Una ráfaga inicial llega a A, B y al proceso C, que es más lento.
Hoy dependemos principalmente de Istio y Envoy para ofrecer agrupación de conexiones y mejores estrategias de balanceo que consideren la carga de los servidores en toda la infraestructura de OpenAI, con lo que evitamos este problema por completo.
Un efecto secundario de optimizar para reducir el retraso de asyncio y tener tantos procesos de Python es que resulta muy fácil saturar las dependencias posteriores con una enorme cantidad de conexiones, lo que se conoce como una “avalancha de solicitudes”.
Una implementación diaria normal, si no se configura para avanzar lentamente, puede generar una rotación considerable de CPU debido al ciclo de conexiones. Asimismo, una fuga de conexiones puede derribar la red al saturar la puerta de enlace NAT. Estos problemas tampoco son inusuales en otros servicios, pero tener un orden de magnitud más de procesos reduce considerablemente el umbral que los desencadena. A menudo se saturan recursos relacionados con la red que los clientes no esperan tener que gestionar en estado estable si solo consideran el rendimiento.
También recurrimos a Envoy para maximizar la consolidación de conexiones. Lo usamos para actualizar las conexiones HTTP/1 de Python a HTTP/2 y aprovechar la multiplexación; luego agrupamos esas conexiones y ampliamos su vida útil. Envoy también nos brinda un lugar central para implementar límite de solicitudes y disyuntores, que serían menos eficaces en cada proceso independiente de Python.
Figura 05 · Consolidación de conexiones
Las mismas solicitudes, menos conexiones
La agrupación de conexiones y la multiplexación de conexiones HTTP/2 ayudan a reducir la carga de conexiones en los servicios posteriores.
Una razón por la que pudimos escalar Python hasta este punto fue la API limitada de Habitat, que mantiene predecible el costo de las solicitudes. En lugar de permitir que los clientes construyan consultas SQL arbitrarias que podrían provocar grandes recorridos de tablas o uniones entre muchas tablas, Habitat ofrece una API NoSQL sencilla. La ausencia de una API potente es una concesión explícita del diseño de Habitat.
Buscamos optimizar las solicitudes sencillas, predecibles y de trabajo constante. Según nuestra experiencia, estos sistemas son mucho más fáciles de escalar y difíciles de configurar o usar mal. Las solicitudes con expansión impredecible son peligrosas en términos operativos: complican el aislamiento y el balanceo de carga, e introducen aumentos abruptos de latencia que son difíciles de gestionar al escalar tanto el servicio como sus clientes.
Antes de migrar a Habitat y Azure Cosmos DB, la mayoría de los datos en línea de OpenAI se almacenaban en Postgres. En ese momento, era fácil revisar todos los cambios de consultas y esquemas para verificar que se comportaran correctamente y operaran con datos indexados antes de implementarlos en producción. A medida que crecieron el equipo y los productos, esto pronto se volvió inmanejable y provocó interrupciones frecuentes: una sola consulta nueva y costosa en una ruta crítica podía derribar la base de datos.
El problema es el desequilibrio de costos: escribir consultas SQL costosas y difíciles de ejecutar es fácil y barato. En Habitat, evitamos esto y hacemos que las consultas costosas sean sumamente evidentes del lado del cliente. No hay consultas ilimitadas que puedan sobrecargar Habitat, y las uniones complejas y los recorridos de grafos exigen que los equipos de producto hagan parte del trabajo pesado, lo que favorece diseños más eficientes en general.
Habitat ofrece una API NoSQL basada en tipos de objetos y aristas definidos por el cliente, inspirada en TAO(se abre en una nueva ventana). Los clientes predefinen los objetos, las aristas y sus relaciones, pero no el contenido de cada tipo. Las relaciones resultantes se parecen a un grafo, pero Habitat no admite las consultas típicas de recorrido de grafos, salvo las consultas de aristas directas de un objeto específico.
Dividimos este grafo para que cada objeto y sus aristas correspondientes estén ubicados en la misma partición de almacenamiento, pero no hacemos un esfuerzo deliberado en la base de datos por ubicar juntos los objetos y los objetos remotos a los que apuntan sus aristas. El resultado es que el modelo se divide fácilmente para escalar horizontalmente, pero los recorridos de grafos son ineficientes porque cualquier salto entre objetos puede requerir datos de dos cuentas de Azure Cosmos DB completamente distintas y almacenadas en regiones diferentes.
Para los clientes con necesidades de consulta más complejas, ofrecemos una vista secundaria sin conexión de Habitat mediante Rockset. Usamos la captura de datos modificados (CDC) para transmitir cambios del almacenamiento en línea a instancias aisladas de Rockset casi en tiempo real. Cada equipo de cliente debe escalar su propia instancia de Rockset para atender sus necesidades de consultas complejas.
Este aprovisionamiento de Rockset genera fricción adicional para nuestros clientes, pero creemos que es la concesión adecuada en este momento: las consultas sencillas son la opción predeterminada, con una vía alternativa para quienes necesitan consultas complejas. Este diseño aísla nuestro almacenamiento en línea de las cargas analíticas y de búsqueda con uso intensivo de lectura.
Posponer un año la reescritura de Python nos permitió centrarnos en desafíos más urgentes y relevantes durante nuestro hipercrecimiento. Con una plataforma más madura y un crecimiento cada vez más rápido, y siendo el segundo servicio de OpenAI con más núcleos —y el cuarto por nuestra presencia de Envoy—, finalmente llegó el momento de dejar atrás Python. En su punto máximo, Python nos ayudó a atender más de 20 millones de solicitudes por segundo.
En el segundo trimestre de 2026, con solo 2 ingenieros, Codex y GPT‑5.5, pudimos reescribir todo el servicio en Rust. Este nuevo servicio de Rust ya gestiona el 95 % de nuestras solicitudes de producción; retiraremos Python por completo en las próximas semanas. Nuestros datos muestran que el servicio de Rust es 6 veces más eficiente en CPU y 15 veces más eficiente en memoria que la versión de Python, con latencias promedio y de cola considerablemente menores. Planeamos compartir más aprendizajes en una futura publicación.
El servicio de Python —y ahora de Rust— es solo una faceta de Habitat. En la segunda parte de esta serie sobre cómo escalamos rápidamente nuestro almacenamiento en línea para atender a más de 1 000 millones de usuarios de ChatGPT, hablaremos de la capa de almacenamiento y de cómo Habitat gestiona más de 500 petabytes y más de 70 millones de solicitudes por segundo.
Si quieres trabajar en sistemas OLTP a escala de vanguardia y te interesa este tipo de ingeniería, consulta esta vacante en nuestro equipo.


