Passer au contenu principal
OpenAI

11 septembre 2026

Ingénierie

Mettre le stockage en ligne à l’échelle pour plus d’un milliard d’utilisateurs de ChatGPT

Comment nous avons adapté Habitat, notre plateforme de stockage applicatif en Python, à une croissance sans précédent.

Par Jon Lee, Chaomin Yu et Ben Ries, membres du personnel technique

Chargement…

Chaque produit d’OpenAI dépend d’un accès rapide et fiable aux données, qu’une personne ouvre une session, vérifie ses paramètres Codex ou lance une nouvelle conversation dans ChatGPT. Chacune de ces actions peut exiger de nombreuses recherches de données distinctes avant que le produit puisse répondre. Si ces requêtes sont lentes, le produit semble lent. Si elles échouent, le produit cesse complètement de fonctionner.

Habitat est la plateforme de stockage en ligne que nous avons créée pour permettre aux produits d’OpenAI d’accéder rapidement et fiablement aux renseignements requis. Habitat traite maintenant plus de 70 millions de requêtes par seconde et soutient, dans près de 40 régions géographiques, des produits utilisés chaque semaine par plus d’un milliard de personnes. Habitat a d’abord été lancé pour prendre en charge les GPT lors du DevDay 2023, comme simple bibliothèque Python côté client reliée à une seule base de données. Aujourd’hui, c’est un système distribué complexe qui fournit plus de 500 pétaoctets de données.

Figure 01 · Qu’est-ce qu’Habitat?

Plateforme de stockage en ligne

Habitat est la plateforme de stockage en ligne que nous avons créée pour permettre aux produits d’OpenAI d’accéder rapidement et fiablement aux renseignements requis.

  • Requête
  • Réponse
  • Modifications (CDC)

Clients

Plateforme de stockage en ligne

Ressources de stockage

  • ChatGPT
  • API
  • Codex
  • Services internes
  • Et plus encore

Habitat

  • Mise en cacheCaches
  • Politiques de LCAAutorisation
  • Emplacement et résidence des donnéesRésidence des données
  • ChiffrementSécurité des données
  • IsolationArchitecture multilocataire
  • Limitation du débitFormatage des requêtes
  • RoutageRecherche de schéma · Résidence des données
  • Azure Cosmos DBStockage en ligne
  • NanobaseStockage en ligne
  • ValkeyCaches
  • Stockage d’objets blobRessources de stockage
Services CDCCapture des données modifiées
  • Databricks
  • Rockset
  • Kafka
  • Et plus encore

Créer et exploiter une infrastructure à cette échelle n’est pas une mince affaire, sans être particulièrement difficile non plus. Notre situation était unique en raison de la vitesse sans précédent à laquelle nous avons dû évoluer pour soutenir une croissance vertigineuse du nombre d’utilisateurs et de la demande, tout en bâtissant une plateforme mature. Souvent, les ingénieurs système conçoivent pour une échelle dix fois supérieure et espèrent qu’elle suffira quelques années, tout en préparant la prochaine multiplication par dix. Dans notre cas, nous avons plus que décuplé chaque année au cours des trois dernières années. La création et l’exploitation d’Habitat ont donc reposé sur une série de décisions tactiques et leur séquençage : comprendre chaque composant au niveau le plus bas pour tirer le maximum de notre pile existante, tout en repoussant les pénuries de stockage et de calcul afin de gagner du temps pour les investissements fondamentaux.

  • 70 M+

    requêtes par seconde

  • 1 G+

    personnes chaque semaine

  • 500 Po+

    de données

À mesure qu’OpenAI grandissait, Habitat devait suivre : devenir d’abord assez fiable pour le trafic essentiel des produits, puis assez rapide pour les utilisateurs mondiaux et, enfin, fonctionner habilement à très grande échelle. Ce billet est le premier d’une série en deux parties sur la mise à l’échelle de notre stockage en ligne. Nous expliquerons ici comment Habitat a évolué, pourquoi nous avons transformé cette bibliothèque en service et comment nous avons poussé un service écrit dans un langage inhabituel pour ce rôle, Python, jusqu’à en faire une couche de plateforme de stockage fiable.

Dans un prochain billet, nous détaillerons la fiabilité de l’architecture multilocataire à grande échelle, notre stratégie par couches pour optimiser les lectures et l’expansion de notre partenariat avec Azure Cosmos DB afin de répondre fiablement à une demande sans précédent.

Qu’est-ce qu’Habitat?

Habitat est né d’une idée simple : les ingénieurs produit ne devraient pas avoir à penser à la gestion des bases de données. Habitat a d’abord été lancé pour prendre en charge les GPT lors du DevDay 2023, sous forme d’une petite bibliothèque Python interagissant avec le serveur principal de ChatGPT. Elle prenait en charge un petit ensemble d’opérations correspondant, en arrière-plan, à l’application de base de données Azure Cosmos DB.

La bibliothèque devait offrir aux équipes produit un moyen simple de stocker et de récupérer des données sans devoir maîtriser les détails sous-jacents. Habitat effectuait le travail nécessaire : déterminer le type de données, leur origine ou leur destination, si la requête était autorisée, et ainsi de suite.

Les ingénieurs produit n’ont pas à se soucier de la recherche de schémas, du routage, de l’autorisation, du chiffrement, de la sérialisation, du formatage des requêtes ni du regroupement des connexions. Ils n’avaient même pas à considérer la provenance des données : Azure Cosmos DB, caches ou autres types de stockage.

Figure 02 · Service Habitat

Flux simplifié d’une requête Habitat

En séparant la logique de stockage dans un service autonome, nous avons créé un point de contrôle unique pour les déploiements, l’observabilité et les améliorations de la plateforme.

  • Requête
  • Réponse

Client

OpenAI

Azure Cosmos DB

SDK client Habitat
envoy
  • habitat-serviceprocessus 1
  • habitat-serviceprocessus 2
  • habitat-serviceprocessus 3
habitat-envoy
  • habitat-cosmos-db-us0
  • habitat-cosmos-db-us1
  • habitat-cosmos-db-eu0

Cette bibliothèque Python fonctionnait bien et a été rapidement adoptée par les ingénieurs produit d’OpenAI, même sans effort centralisé pour délaisser Postgres et Azure Cosmos DB en libre-service.

À mesure que les besoins évoluaient, les développeurs de produits pouvaient même facilement ajouter à la bibliothèque partagée des fonctions comme la mise en cache côté client, la compression ou le chiffrement.

Créer un service pour mieux soutenir plusieurs produits complexes

À la mi-2025, Habitat avait atteint les limites d’une mise en œuvre côté client. Avec la complexité croissante de la couche Habitat et la multiplication des services d’OpenAI, les changements de protocole rétrocompatibles étaient devenus irréalisables.

Nous voulions notamment réduire le rayon d’impact d’une panne régionale sur nos jeux de données les plus essentiels en les migrant vers des comptes Azure Cosmos DB répartis régionalement. Ce changement exigeait d’ajouter au client une logique de routage, désactivée derrière un indicateur de fonctionnalité, de la déployer chez tous les clients, puis d’activer l’indicateur.

La coordination des déploiements dans des dizaines de services et avec chaque équipe a pris des jours. Avant l’activation, nous avons compris qu’il fallait ajouter une mise en miroir pour valider la logique de partitionnement. Son déploiement a pris deux autres jours. Une correction pour un élément que nous avions découvert incorrect? Encore deux jours. Nous étions enfin prêts à activer l’indicateur lorsqu’une équipe, pour des raisons sans rapport, a rétabli son service à une version antérieure et boguée du client, causant la panne que nous avions tant cherché à éviter.

Les modifications de la bibliothèque cliente exigeaient une coordination complexe entre des dizaines de services, un processus toujours plus fragile, inefficace et vulnérable aux défaillances opérationnelles. Pour réduire cette dispersion opérationnelle lors des futurs déploiements, nous avons décidé de faire d’Habitat un service distinct.

En séparant la logique de stockage dans un service autonome, nous avons créé un point de contrôle unique pour les déploiements, l’observabilité et les améliorations de la plateforme. Au lieu de gérer des mises à jour fragmentées, nous pouvions centraliser les améliorations et en faire profiter immédiatement chaque produit d’OpenAI.

Un service centralisé nous offre aussi un point de contrôle unique pour fournir les meilleurs mécanismes de sécurité et de confidentialité des données. Le service Habitat nous permet d’appliquer centralement les politiques de contrôle d’accès, de tenir des journaux d’audit et de limiter l’accès aux ressources de stockage sous-jacentes comme Azure Cosmos DB. Habitat joue un rôle essentiel dans la protection des données utilisateur et la prévention des accès non autorisés par des acteurs externes, internes ou agents.

Lancer un service Python à grande échelle

Nous savions qu’il nous fallait un service, mais nous ne voulions pas encore abandonner Python, malgré sa surcharge supplémentaire comme service. Utiliser Python pour un service à haut débit a accru la latence réseau et considérablement augmenté les coûts de mise à l’échelle du CPU et de la mémoire par rapport à l’exécution locale de la bibliothèque. De plus, nous savions que les inefficacités de Python seraient inacceptables à une échelle 100 fois supérieure, rendant une éventuelle réécriture presque certaine.

Nous considérions toutefois cela comme une incursion stratégique dans la dette technique. Notre objectif principal n’était alors pas d’optimiser les coûts ou les ressources, mais de débloquer les développeurs de produits et de stabiliser la plateforme. En acceptant à court terme les compromis de performance d’un service Python, nous avons pu prioriser des défis plus immédiats, établir nos API principales et bâtir une infrastructure robuste.

Nous avons aussi parié, de façon calculée, que les progrès rapides de nos propres modèles de programmation simplifieraient la voie technique à l’avenir. Nous avons parié que, lorsqu’une migration complète hors de Python deviendrait nécessaire, Codex et GPT la rendraient réalisable. Ce pari s’est finalement avéré juste.

Exécuter Habitat comme service Python serait sous-optimal sur le plan des performances, mais nécessaire. Python nous permet d’avancer rapidement, mais pas de négliger toute prudence et d’accepter des latences nettement pires. Quand une requête utilisateur moyenne entraîne des centaines d’appels à la base de données, l’utilisateur ressent la lenteur du plus lent. Nous avons constaté que le principal défi d’un service Python à cette échelle consiste à gérer ces latences de fin de distribution.

Suivre le délai d’asyncio

Asyncio aide Python à exécuter simultanément des charges limitées par les E/S, mais ne permet pas de contourner le GIL de Python ni de paralléliser le CPU. En plus de relayer les requêtes à forte intensité d’E/S, Habitat assume de nombreuses responsabilités et tâches d’arrière-plan exigeantes pour le CPU : routage, compression, chiffrement, sommes de contrôle, vérification de l’état des services en aval, mise en miroir des requêtes et envoi de requêtes redondantes. : routage, compression, chiffrement, sommes de contrôle, vérification de l’état des services en aval, mise en miroir et couverture des requêtes.

Avec autant de charges exigeantes pour le CPU et de tâches d’arrière-plan dans notre service, le délai de planification d’asyncio peut facilement dominer la latence de fin de distribution des requêtes. Avant l’optimisation de notre lancement initial, les traces des requêtes à latence p99 ou supérieure montraient que, malgré la réponse rapide du stockage en aval, les requêtes bloquaient souvent en attendant que la coroutine responsable soit replanifiée pour analyser la réponse.

Figure 03 · Suivi du délai d’asyncio

La simultanéité n’est pas le parallélisme CPU

Asyncio de Python permet le traitement simultané des requêtes, mais une seule requête s’exécute à la fois sur le fil CPU. Cela influe fortement sur la latence des requêtes lorsqu’il y a beaucoup de travail CPU à effectuer.

Traitement CPU des requêtes et réponsesLecture/écriture réseau PythonAttente de Cosmos

Travail CPU faible

Brèves étapes Python; les attentes d’E/S se chevauchent

Travail CPU élevé

De longues étapes Python font attendre les réponses prêtes

0.0 / 40 unités illustratives

Pour les services Python d’OpenAI, en plus des mesures standard d’utilisation et de saturation de la mémoire, du CPU, du réseau et du disque, il est essentiel de surveiller la boucle asyncio et son niveau d’activité, puis de l’ajuster en conséquence.

En planifiant périodiquement des tâches d’arrière-plan et en enregistrant l’écart entre les heures d’exécution prévue et réelle, nous pouvons mesurer empiriquement et en temps réel le délai de planification de la boucle d’événements. À forte utilisation, avec de nombreuses tâches coûteuses, même un nombre modeste de requêtes simultanées par processus suffit à produire une importante gigue de planification, allant jusqu’à des centaines de millisecondes et, dans certains cas limites, plusieurs secondes.

Nous limitons donc chaque processus à un petit nombre de requêtes simultanées et augmentons plutôt massivement le nombre de processus de travail Python.

Réduire une latence de fin de distribution dans nos configurations d’indicateurs de fonctionnalité

Lors du lancement initial, le profilage CPU du service en production a révélé une cause fondamentale du délai élevé d’asyncio et des fortes latences de fin de distribution : l’analyse JSON périodique de nos configurations d’indicateurs de fonctionnalité par Statsig, un outil qui gère ces indicateurs et permet notamment d’effectuer des tests A/B.

Par défaut, Statsig vérifiait chaque minute les configurations actualisées, sans gigue, et la configuration comprenait toutes les règles de production de tous les services. Ailleurs, une décision d’architecture avait fixé jusqu’à huit processus Python par pod afin d’accroître l’utilisation du CPU et de réduire les latences. Ainsi, chaque minute, tous les processus de travail d’un pod cessaient momentanément de traiter les requêtes en cours pour consacrer leurs cycles CPU à l’analyse d’un énorme fichier de configuration.

Une fois le profilage CPU utilisé pour trouver la cause, la correction était simple : déployer une configuration ciblée plus petite, allonger l’intervalle d’actualisation et ajouter de la gigue à ces tâches d’arrière-plan.

Équilibrer les charges et gérer les groupes de connexions

Pour maintenir un faible délai d’asyncio, il est aussi essentiel de bien équilibrer les requêtes entre les processus serveur; sans ajustement, le regroupement des connexions peut également aller à l’encontre de cet objectif.

Avec le regroupement des connexions côté client, un processus client qui effectue de nombreuses requêtes simultanées peut n’établir qu’une poignée de connexions serveur et ainsi envoyer toute sa charge à quelques processus seulement. Avant d’ajuster notre équilibrage de charge, l’utilisation de notre service variait grandement, certains processus en fin de distribution traitant de 5 à 10 fois plus de requêtes simultanées que la moyenne.

Nous l’avons découvert lors d’un incident fortuit : malgré l’arrêt du client qui surchargeait une partie du service, un sous-ensemble de processus est resté dégradé bien après la pointe de trafic. En fait, ces processus se dégradaient de façon incontrôlée, recevant toujours plus de requêtes jusqu’à leur redémarrage. Lorsqu’un pod devenait surchargé, un comportement y épinglait encore plus de trafic. Certains de nos collègues connaissaient bien cette catégorie de défaillances grâce à leurs emplois précédents : la défaillance métastable(s'ouvre dans une nouvelle fenêtre).

Soupçonnant le groupe de connexions, nous avons plafonné la durée maximale de réutilisation des connexions. Cela a effectivement limité la dégradation et confirmé l’orientation de notre enquête. Une enquête approfondie a révélé que le TCPConnector d’aiohttp de Python réutilise par défaut les connexions selon LIFO : la connexion retournée le plus récemment est choisie pour la prochaine requête. C’est normalement un choix raisonnable : réutiliser les connexions récentes permet aux connexions supplémentaires créées pour absorber les pointes d’expirer en état d’inactivité, ce qui réduit leur coût de maintien. Dans ce cas-ci, cela a créé une défaillance métastable. Pendant une pointe, les connexions associées aux requêtes envoyées aux serveurs surchargés plus lents revenaient plus tard dans le groupe. Elles étaient donc sélectionnées plus souvent par les requêtes suivantes, concentrant progressivement le trafic sur les pods déjà en difficulté. Modifier le groupe de connexions pour une réutilisation FIFO a rompu cette boucle de rétroaction et même réduit la variance des requêtes en régime stable.

Figure 04A · Regroupement des connexions côté client

LIFO renvoie le nouveau travail au processus lent

Après une pointe de requêtes, les serveurs plus lents retournent leurs connexions au groupe en dernier. LIFO concentre davantage de travail sur ces mêmes serveurs plus lents.

Une pointe initiale atteint A, B et le processus C, plus lent.

Figure 04B · Regroupement des connexions côté client

FIFO rompt la boucle de rétroaction de réutilisation des connexions

FIFO maintient plus de connexions actives après une pointe, mais répartit équitablement les charges entre tous les serveurs.

Une pointe initiale atteint A, B et le processus C, plus lent.

Aujourd’hui, nous comptons surtout sur Istio et Envoy pour regrouper les connexions et mieux équilibrer la charge selon celle des serveurs dans toute l’infrastructure d’OpenAI, évitant ainsi complètement ce problème.

Éviter d’inonder les ressources en aval

Un effet secondaire de l’optimisation visant un faible délai d’asyncio et du grand nombre de processus Python est qu’il devient très facile de submerger les dépendances en aval avec un nombre immense de connexions, phénomène appelé « troupeau tonitruant ».

Un déploiement quotidien normal, s’il n’est pas réglé pour être lent, peut provoquer une importante agitation du CPU en raison du renouvellement des connexions. Une fuite de connexions peut aussi mettre le réseau hors service en saturant la passerelle NAT. Ces problèmes touchent aussi couramment d’autres services, mais leur seuil de déclenchement est nettement abaissé par un nombre de processus supérieur d’un ordre de grandeur, qui sature souvent des ressources réseau que les clients ne prévoient pas devoir gérer en régime stable d’après le seul débit.

Nous comptons aussi sur Envoy pour maximiser la convergence de nos connexions. Nous l’utilisons pour faire passer les connexions HTTP/1 de Python à HTTP/2 afin de profiter du multiplexage, puis pour regrouper ces connexions et prolonger leur durée de vie. Envoy nous offre aussi un emplacement central pour appliquer des limites de débit et des disjoncteurs, qui seraient moins efficaces dans chaque processus Python autonome.

Figure 05 · Convergence des connexions

Les mêmes requêtes, moins de connexions

Le regroupement des connexions et le multiplexage HTTP/2 réduisent la charge de connexion sur les services en aval.

RequêteRéponseMaintien en vie inactif

Pourquoi Habitat en fait moins

Si nous avons pu pousser Python aussi loin, c’est notamment grâce à l’API restreinte d’Habitat, qui rend le coût des requêtes prévisible. Plutôt que de permettre aux clients de créer des requêtes SQL arbitraires pouvant entraîner de vastes balayages ou des jointures entre plusieurs tables, Habitat expose une API NoSQL simple. L’absence d’une API puissante est un compromis explicite dans la conception d’Habitat.

Nous cherchons à optimiser les requêtes simples, prévisibles et à charge constante. D’après notre expérience, ces systèmes sont beaucoup plus faciles à mettre à l’échelle et difficiles à mal concevoir ou utiliser. Les requêtes à dispersion imprévisible sont dangereuses sur le plan opérationnel : elles compliquent l’isolation et l’équilibrage de charge, et créent des seuils de latence difficiles à gérer à grande échelle pour le service et ses clients.

Avant notre passage à Habitat et à Azure Cosmos DB, la plupart des données en ligne d’OpenAI étaient stockées dans Postgres. À l’époque, il était facile d’examiner toutes les modifications de requêtes et de schémas pour vérifier leur bon comportement et leur utilisation de données indexées avant la mise en production. Avec la croissance de l’équipe et des produits, cela est vite devenu ingérable et causait souvent des pannes lorsqu’une seule nouvelle requête coûteuse sur un chemin critique mettait la base de données hors service.

Le problème tient au déséquilibre des coûts : écrire des requêtes SQL coûteuses et difficiles à exécuter est simple et peu coûteux. Dans Habitat, nous évitons ce problème et rendons les requêtes coûteuses extrêmement évidentes côté client. Aucune requête illimitée ne peut surcharger Habitat; les jointures complexes et les parcours de graphe obligent les équipes produit à effectuer une partie du travail lourd, ce qui favorise globalement des conceptions plus efficaces.

Habitat expose une API NoSQL fondée sur des types d’objets et d’arêtes définis par le client, inspirée de TAO(s'ouvre dans une nouvelle fenêtre). Les clients prédéfinissent les objets, les arêtes et leurs relations, mais pas le contenu de chaque type. Les relations obtenues ressemblent à un graphe, mais Habitat ne prend pas en charge les requêtes habituelles de parcours de graphe, sauf l’interrogation des arêtes directes d’un objet donné.

Nous partitionnons ce graphe afin que chaque objet et ses arêtes soient regroupés dans une partition de stockage, sans toutefois chercher, au niveau de la base de données, à regrouper les objets et les objets distants visés par leurs arêtes. Le modèle se partitionne donc facilement pour une mise à l’échelle horizontale, mais les parcours de graphe sont inefficaces, car chaque saut entre objets peut nécessiter une récupération dans deux comptes Azure Cosmos DB entièrement distincts et stockés dans différentes régions.

Pour les clients ayant des besoins d’interrogation plus complexes, nous offrons une vue secondaire hors ligne d’Habitat par l’intermédiaire de Rockset. Nous utilisons la capture des données modifiées (CDC) pour diffuser presque en temps réel les changements du stockage en ligne vers des instances Rockset isolées. Chaque équipe cliente doit mettre sa propre instance Rockset à l’échelle pour ses besoins d’interrogation complexes.

Ce provisionnement de Rockset ajoute des difficultés pour nos clients, mais nous estimons que c’est actuellement le bon compromis : faire des requêtes simples la norme, tout en offrant une issue à ceux qui ont besoin de requêtes complexes. Cette conception isole notre stockage en ligne des charges analytiques et de recherche à lecture intensive.

Migrer de Python vers Rust

Reporter d’un an la réécriture de Python nous a permis de nous concentrer sur des défis plus urgents et déterminants pendant notre hypercroissance. La plateforme gagnant en maturité et notre croissance continuant de s’accélérer, alors qu’Habitat était le deuxième service d’OpenAI par nombre de cœurs et le quatrième par empreinte Envoy, le temps était enfin venu de dépasser Python. À son apogée, Python nous a aidés à traiter plus de 20 millions de requêtes par seconde.

Au deuxième trimestre de 2026, avec seulement deux ingénieurs, Codex et GPT‑5.5, nous avons pu réécrire tout le service en Rust. Ce nouveau service Rust traite maintenant 95 % de nos requêtes de production; nous abandonnerons complètement Python dans les prochaines semaines. Nos données montrent que le service Rust est six fois plus efficace en CPU et quinze fois plus efficace en mémoire que la version Python, avec des latences moyennes et de fin de distribution nettement inférieures. Nous comptons présenter d’autres apprentissages dans un prochain billet.

Optimiser notre couche de base de données, Azure Cosmos DB

Le service Python, désormais en Rust, n’est qu’une facette d’Habitat. Dans la deuxième partie de cette série sur la mise à l’échelle rapide de notre stockage en ligne pour plus d’un milliard d’utilisateurs de ChatGPT, nous aborderons la couche de stockage et la façon dont Habitat traite plus de 500 pétaoctets et plus de 70 millions de requêtes par seconde.

Si vous souhaitez travailler sur des systèmes OLTP à une échelle de pointe et que ce type d’ingénierie vous intéresse, consultez ce poste vacant dans notre équipe.

Auteurs

Jon Lee, Chaomin Yu, Ben Ries