Ir al contenido principal
OpenAI

11 de septiembre de 2026

Ingeniería

Escalar rápidamente el almacenamiento en línea para más de 1000 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, miembros del personal técnico

Cargando…

Todos los productos de OpenAI dependen de un acceso rápido y fiable a los datos, ya sea para iniciar sesión, consultar la configuración de Codex o empezar una conversación nueva 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 parece 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 a la información necesaria con rapidez y fiabilidad. Habitat ya gestiona más de 70 millones de solicitudes por segundo y presta servicio a productos que usan más de 1000 millones de personas cada semana en casi 40 regiones geográficas. Habitat se lanzó inicialmente para admitir GPTs en DevDay 2023, como una sencilla biblioteca Python del lado del cliente conectada a una única base de datos. Hoy es un complejo sistema distribuido que sirve 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 a la información necesaria con rapidez y fiabilidad.

  • Solicitud
  • Respuesta
  • Cambios (CDC)

Clientes

Plataforma de almacenamiento en línea

Recursos de almacenamiento

  • ChatGPT
  • API
  • Codex
  • Servicios internos
  • Y más

Habitat

  • Almacenamiento en cachéCachés
  • Políticas de ACLAutorización
  • Ubicación y residencia de datosResidencia de datos
  • CifradoSeguridad de los datos
  • AislamientoArquitectura multiinquilino
  • Limitación de frecuenciaAdaptación de solicitudes
  • EnrutamientoBúsqueda de esquemas · Residencia de datos
  • Azure Cosmos DBAlmacenamiento en línea
  • NanobaseAlmacenamiento en línea
  • ValkeyCachés
  • Almacenamiento de blobsRecursos de almacenamiento
Servicios de CDCCaptura de datos modificados
  • Databricks
  • Rockset
  • Kafka
  • Y más

Crear y operar infraestructura a esta escala no es tarea fácil, aunque tampoco resulta especialmente difícil. Lo que hizo única nuestra situación fue la velocidad sin precedentes a la que tuvimos que escalar para responder al asombroso crecimiento de usuarios y a la demanda de los productos, mientras construíamos una plataforma madura. Los ingenieros de sistemas suelen diseñar para multiplicar por 10 la escala y esperan que aguante unos años mientras se preparan para el siguiente aumento de 10 veces. En nuestro caso, hemos crecido más de 10 veces interanualmente durante los últimos tres años. Por ello, crear y operar Habitat ha exigido una sucesión y secuenciación de decisiones tácticas: comprender cada componente al nivel más bajo para exprimir al máximo nuestra pila existente, mientras evitábamos limitaciones de almacenamiento y computación para ganar tiempo de cara a inversiones fundamentales.

  • Más de 70 M

    solicitudes por segundo

  • Más de 1000 M

    personas cada semana

  • Más de 500 PB

    datos

A medida que OpenAI crecía, Habitat tuvo que crecer a su ritmo: primero, hasta ser suficientemente fiable para el tráfico crítico de los productos; después, suficientemente rápido para usuarios de todo el mundo; y, por último, hasta operar con agilidad a una escala enorme. 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 biblioteca en servicio y cómo transformamos un servicio escrito en un lenguaje poco habitual para esta función —Python— en una capa fiable de plataforma de almacenamiento.

En una futura publicación, detallaremos cómo logramos una arquitectura multiinquilino fiable a gran escala, nuestra estrategia por capas para optimizar el rendimiento de lectura y cómo ampliamos nuestra colaboración con Azure Cosmos DB para responder con fiabilidad a una demanda sin precedentes.

¿Qué es Habitat?

Habitat nació de una idea sencilla: los ingenieros de producto no deberían tener que pensar en la gestión de bases de datos. Habitat se lanzó inicialmente para admitir GPTs en DevDay 2023 como una pequeña biblioteca Python que interactuaba con el servidor principal de ChatGPT. Admitía un pequeño conjunto de operaciones que internamente se asignaban a Azure Cosmos DB, la aplicación de base de datos.

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 el tipo de datos, de dónde debían proceder —o adónde debían ir—, si la solicitud estaba permitida, etc.

Los ingenieros de producto no tenían que preocuparse por la búsqueda de esquemas, el enrutamiento, la autorización, el cifrado, la serialización, la adaptación de solicitudes ni la agrupación de conexiones. Ni siquiera tenían que considerar de dónde procedí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 los despliegues, la observabilidad y las mejoras de la plataforma.

  • Solicitud
  • Respuesta

Cliente

OpenAI

Azure Cosmos DB

SDK cliente de Habitat
envoy
  • habitat-serviceproceso 1
  • habitat-serviceproceso 2
  • habitat-serviceproceso 3
habitat-envoy
  • habitat-cosmos-db-us0
  • habitat-cosmos-db-us1
  • habitat-cosmos-db-eu0

Esta biblioteca Python funcionó bien y Habitat se adoptó rápidamente entre los ingenieros de producto de OpenAI, pese a que no hubo un impulso centralizado para dejar de usar Postgres y Azure Cosmos DB en modo autoservicio.

A medida que evolucionaban las necesidades del producto, los desarrolladores podían incluso añadir fácilmente a la biblioteca compartida funciones como almacenamiento en caché, compresión o cifrado del lado del cliente.

Crear un servicio que admita mejor varios productos complejos

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 el número de servicios de OpenAI, los cambios de protocolo retrocompatibles se hicieron inviables.

En una ocasión, quisimos reducir el impacto de una interrupción en una sola región sobre nuestros conjuntos de datos más críticos, migrándolos a varias cuentas de Azure Cosmos DB distribuidas regionalmente. Este cambio exigía introducir en el cliente lógica de enrutamiento adicional desactivada mediante un indicador de función, desplegarla en todos los clientes y, después, activar el indicador.

Coordinar los despliegues de decenas de servicios y colaborar con cada equipo para implementarlos llevó días. Antes de habilitarlo, nos dimos cuenta de que queríamos introducir cierta duplicación para comprobar que la lógica de particionamiento fuera correcta. Desplegarla llevó un par de días más. ¿Corregir un error que descubrimos? Otro par de días. Al final estábamos listos para activar el indicador, pero uno de los equipos revirtió su servicio, por motivos no relacionados, 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 fallos operativos. Para reducir esta dispersión operativa en futuros despliegues, 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 los despliegues, la observabilidad y las mejoras de la plataforma. En vez de gestionar 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 donde aplicar las medidas más sólidas de seguridad y privacidad de los datos. En el servicio Habitat podemos 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 impedir accesos no autorizados de actores externos, internos y agentes.

Lanzar un servicio Python a gran escala

Sabíamos que necesitábamos un servicio, pero aún no queríamos abandonar Python, pese a la sobrecarga adicional que conllevaba usarlo como servicio. Usar Python para un servicio de alto rendimiento aumentó la latencia de red y añadió costes considerables de escalado de CPU y memoria frente a la ejecución de una biblioteca local. Además, sabíamos que las ineficiencias de Python no serían aceptables a una escala 100 veces mayor, por lo que una futura reescritura era casi inevitable.

Sin embargo, lo consideramos una incursión estratégica en la deuda técnica. Nuestro principal objetivo entonces no era optimizar costes o recursos, sino desbloquear a los desarrolladores de producto y estabilizar la plataforma. Al aceptar a corto plazo las desventajas de rendimiento de un servicio Python, pudimos priorizar retos más inmediatos, establecer nuestras API principales y crear una infraestructura sólida.

También apostamos de forma calculada por que el rápido avance de nuestros propios modelos de programación simplificaría el camino técnico en el futuro. Apostamos por que, cuando fuera necesaria una migración completa desde Python, Codex y GPT la harían viable. Al final, acertamos.

Ejecutar Habitat como servicio Python no sería óptimo en términos de rendimiento, pero era una decisión necesaria. Python nos permite avanzar rápido, pero eso no significaba que pudiéramos ignorar los riesgos y aceptar latencias mucho peores. Cuando una solicitud media de usuario genera cientos de llamadas a bases de datos, el usuario percibe la llamada más lenta. Hemos comprobado que el principal reto de ejecutar un servicio Python a esta escala es gestionar estas latencias de cola.

Medir el retraso de asyncio

Asyncio ayuda a Python a ejecutar simultáneamente cargas de trabajo limitadas por E/S, pero no permite sortear el GIL de Python ni aporta paralelismo de CPU. Además de redirigir solicitudes con mucha E/S, Habitat asume muchas tareas en segundo plano y responsabilidades intensivas en CPU: enrutamiento, compresión, cifrado, sumas de comprobación, comprobación del estado de servicios posteriores, duplicación de solicitudes y cobertura frente a retrasos.

Con tantas cargas intensivas en CPU y tareas en segundo plano, 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 a que se reprogramara la corrutina encargada 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 la CPU. Esto afecta mucho a las latencias de las solicitudes cuando hay una gran carga de CPU.

Procesamiento de solicitudes y respuestas en la CPULectura y escritura de red de PythonEspera de Cosmos

Carga de CPU baja

Pasos breves de Python; las esperas de E/S se solapan

Carga de CPU alta

Los pasos largos de Python mantienen esperando las respuestas listas

0.0/40 unidades ilustrativas

En los servicios Python de OpenAI, además de medir las métricas habituales de uso y saturación de memoria, CPU, red y disco, consideramos fundamental supervisar el bucle de asyncio, comprobar su carga y ajustar el sistema en consecuencia.

Al programar periódicamente tareas en segundo plano y registrar la diferencia entre el momento 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 un número moderado de solicitudes simultáneas por proceso basta para causar fluctuaciones importantes en la planificación: hasta cientos de milisegundos y, en casos extremos, varios segundos.

Por ello, hacemos que cada proceso atienda pocas solicitudes simultáneas y, en su lugar, aumentamos enormemente el número de procesos de trabajo de Python.

Reducir la latencia de cola en la configuración de indicadores de funciones

Durante el lanzamiento inicial, el perfilado de CPU del servicio en producción reveló una causa del elevado retraso de asyncio —y de las consiguientes latencias de cola—: el análisis periódico en JSON de la configuración de indicadores de funciones mediante Statsig, una herramienta para gestionar estos indicadores, realizar pruebas A/B y más.

De forma predeterminada, Statsig consultaba cada minuto si había configuraciones actualizadas, sin fluctuación aleatoria, y la configuración incluía todas las reglas de producción de todos los servicios. Por otro lado, se decidió ejecutar hasta 8 procesos Python por pod para aumentar el uso de CPU y reducir las latencias. En conjunto, esto significaba que, cada minuto, todos los procesos de trabajo de cada pod se detenían en algún momento: dejaban de procesar solicitudes en curso y dedicaban sus ciclos de CPU a analizar un enorme archivo de configuración.

Una vez que el perfilado de CPU nos ayudó a hallar la causa, la solución fue sencilla: desplegar una configuración más pequeña y específica, ampliar el intervalo de actualización y añadir cierta fluctuación aleatoria a estas tareas en segundo plano.

Equilibrar cargas y gestionar grupos de conexiones

Para mantener bajo el retraso de asyncio, también es fundamental equilibrar bien la carga de solicitudes entre los procesos de servidor; sin los ajustes adecuados, la agrupación de conexiones puede resultar contraproducente.

Con la agrupación de conexiones del lado del cliente, un único proceso cliente que haga muchas solicitudes simultáneas puede establecer solo unas pocas conexiones con el servidor y, por ello, enviar toda su carga a unos pocos procesos. Antes de ajustar el equilibrio de carga, el uso del servicio variaba mucho: algunos procesos de cola atendían entre 5 y 10 veces más solicitudes simultáneas que la media.

Lo descubrimos por casualidad durante un incidente: pese a detener el cliente que sobrecargaba parte del 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 fallo que algunos compañeros conocían bien por trabajos anteriores: el fallo metaestable(se abre en una ventana nueva).

Sospechábamos del grupo de conexiones y lo comprobamos limitando la duración máxima de reutilización, lo que redujo la degradación y confirmó que investigábamos en la dirección correcta. Una investigación más profunda reveló que TCPConnector de aiohttp para Python reutiliza conexiones mediante LIFO de forma predeterminada: selecciona para la siguiente solicitud la conexión devuelta más recientemente. Normalmente es una opción predeterminada razonable: reutilizar conexiones recientes permite que las conexiones adicionales creadas para gestionar ráfagas de tráfico caduquen por inactividad, lo que reduce la sobrecarga de mantenerlas. En nuestro caso, provocó un fallo metaestable. Durante una ráfaga, las solicitudes dirigidas a servidores lentos y sobrecargados devolvían más tarde las conexiones al grupo. Por ello, 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 reutilizarlas mediante FIFO rompió este bucle de realimentació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 son los últimos en devolver 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 bucle de realimentación de la reutilización de conexiones

FIFO mantiene más conexiones activas tras una ráfaga, pero equilibra la carga de forma justa entre todos los servidores.

Una ráfaga inicial llega a A, B y al proceso C, que es más lento.

Actualmente dependemos sobre todo de Istio y Envoy para agrupar conexiones y aplicar mejores estrategias de equilibrio basadas en la carga de los servidores en toda la infraestructura de OpenAI, lo que evita por completo este problema.

Evitar saturar los recursos posteriores

Un efecto secundario de optimizar para reducir el retraso de asyncio y tener tantos procesos Python es que resulta muy fácil saturar las dependencias posteriores con una enorme cantidad de conexiones, fenómeno conocido como «estampida».

Un despliegue diario normal, si no se ajusta para que avance lentamente, puede generar una rotación considerable de CPU debido a los ciclos de conexión. Una fuga de conexiones también puede tumbar la red al saturar la puerta de enlace NAT. Estos problemas tampoco son raros en otros servicios, pero el umbral que los desencadena disminuye mucho cuando hay un orden de magnitud más de procesos. Esto suele saturar recursos de red que, basándose solo en el rendimiento, los clientes no esperan tener que gestionar en estado estable.

También usamos Envoy para maximizar la concentración de conexiones. Lo usamos para actualizar las conexiones HTTP/1 de Python a HTTP/2, aprovechar la multiplexación, agruparlas y prolongar su vida útil. Envoy también nos ofrece un lugar central donde implementar límites de frecuencia y disyuntores, que serían menos eficaces en cada proceso Python independiente.

Figura 05 · Concentració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.

SolicitudRespuestaConexión persistente inactiva

Por qué Habitat hace menos

Una razón por la que pudimos llevar Python tan lejos fue la API limitada de Habitat, que mantiene predecible el coste de las solicitudes. En lugar de permitir que los clientes creen consultas SQL arbitrarias que puedan provocar barridos de tablas grandes o combinaciones entre muchas tablas, Habitat ofrece una sencilla API NoSQL. La falta de una API potente es una decisión explícita de diseño de Habitat.

Buscamos optimizar solicitudes sencillas, predecibles y con una carga 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 una expansión impredecible son peligrosas desde el punto de vista operativo: complican el aislamiento y el equilibrio de carga, e introducen saltos bruscos de latencia 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. Entonces era fácil revisar todas las consultas y los cambios de esquema para comprobar que se comportaban bien y operaban sobre datos indexados antes de desplegarlos en producción. A medida que crecieron el equipo y los productos, esto pronto se volvió inmanejable y causó frecuentes interrupciones: bastaba una nueva consulta costosa en una ruta crítica para tumbar la base de datos.

El problema es el desequilibrio de costes: escribir consultas SQL costosas y difíciles de ejecutar resulta barato y sencillo. En Habitat evitamos esto y hacemos que las consultas costosas sean sumamente evidentes para el cliente. No hay consultas sin límites que puedan sobrecargar Habitat. Además, las combinaciones complejas y los recorridos de grafos exigen que los equipos de producto asuman parte del trabajo pesado, lo que ayuda a optimizar el conjunto con diseños más eficientes.

Habitat ofrece una API NoSQL basada en tipos de objetos y aristas definidos por el cliente e inspirada en TAO(se abre en una ventana nueva). Los clientes predefinen los objetos y las aristas, así como sus relaciones, pero no el contenido de cada tipo. Las relaciones resultantes se asemejan a un grafo, pero Habitat no admite las consultas habituales de recorrido de grafos, salvo las consultas de aristas directas de un objeto concreto.

Particionamos este grafo para colocar cada objeto y sus aristas correspondientes en la misma partición de almacenamiento, pero no hacemos un esfuerzo coordinado 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 particiona fácilmente para escalar en horizontal, pero los recorridos de grafos son ineficientes, ya que un salto entre objetos puede exigir recuperar 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 a través de Rockset. Usamos la captura de datos modificados (CDC) para transmitir los cambios del almacenamiento en línea a instancias aisladas de Rockset casi en tiempo real. Cada equipo cliente se encarga de escalar su propia instancia de Rockset según sus necesidades de consultas complejas.

Este aprovisionamiento de Rockset añade fricción para nuestros clientes, pero creemos que ahora mismo es la contrapartida adecuada: hacer que las consultas sencillas sean la opción predeterminada y ofrecer una vía alternativa a quienes necesiten consultas complejas. Este diseño aísla nuestro almacenamiento en línea de las cargas analíticas y de búsqueda con muchas lecturas.

Migrar de Python a Rust

Aplazar un año la reescritura de Python nos permitió centrarnos en retos más urgentes y relevantes durante nuestro hipercrecimiento. Con la plataforma ya más madura, el crecimiento cada vez más rápido y Habitat convertido en el segundo servicio de OpenAI por número de núcleos —y el cuarto por presencia de Envoy—, por fin llegó el momento de dejar atrás Python. En su punto álgido, Python nos ayudó a atender más de 20 millones de solicitudes por segundo.

En el segundo trimestre de 2026, solo 2 ingenieros, Codex y GPT‑5.5 lograron reescribir todo el servicio en Rust. Este nuevo servicio en 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 en Rust es 6 veces más eficiente en CPU y 15 veces más eficiente en memoria que la versión en Python, con latencias medias y de cola considerablemente menores. Compartiremos más aprendizajes en una futura publicación.

Optimizar nuestra capa de base de datos: Azure Cosmos DB

El servicio en Python —y ahora en Rust— es solo una faceta de Habitat. En la segunda parte de esta serie, que explica cómo escalamos rápidamente nuestro almacenamiento en línea para atender a más de 1000 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 de nuestro equipo.

Autores

Jon Lee, Chaomin Yu y Ben Ries