Gå til hovedindhold
OpenAI

11. september 2026

Ingeniørarbejde

Hurtig skalering af onlinelager til over 1 milliard ChatGPT‑brugere

Sådan tilpassede vi vores lagerplatform Habitat i Python til en hidtil uset vækst.

Af Jon Lee, Chaomin Yu og Ben Ries, tekniske medarbejdere

Indlæser ...

Alle OpenAI-produkter afhænger af hurtig og pålidelig adgang til data – uanset om en bruger logger ind, tjekker sine Codex-indstillinger eller starter en ny samtale i ChatGPT. Hver af disse handlinger kan kræve mange separate dataopslag, før produktet kan svare. Hvis anmodningerne er langsomme, føles produktet langsomt. Hvis anmodningerne mislykkes, holder produktet helt op med at fungere.

Habitat er den onlinelagerplatform, vi byggede, så OpenAI-produkter hurtigt og pålideligt kan få adgang til de nødvendige oplysninger. Habitat håndterer nu mere end 70 millioner anmodninger i sekundet og understøtter produkter, som hver uge bruges af over en milliard mennesker i næsten 40 geografiske regioner. Habitat blev først lanceret til understøttelse af GPT'er ved DevDay 2023 som et enkelt Python-bibliotek på klientsiden med forbindelse til én database. I dag er det et komplekst distribueret system, der betjener mere end 500 petabyte data.

Figur 01 · Hvad er Habitat?

Onlinelagerplatform

Habitat er den onlinelagerplatform, vi byggede, så OpenAI-produkter hurtigt og pålideligt kan få adgang til de nødvendige oplysninger.

  • Anmodning
  • Svar
  • Ændringer (CDC)

Klienter

Onlinelagerplatform

Lagerressourcer

  • ChatGPT
  • API
  • Codex
  • Interne tjenester
  • Og mere

Habitat

  • CachelagringCachelagre
  • ACL-politikkerGodkendelse
  • Placering og dataresidensDataresidens
  • KrypteringDatasikkerhed
  • IsoleringMultitenancy
  • HastighedsbegrænsningTilpasning af anmodninger
  • RoutingSkemaopslag · Dataresidens
  • Azure Cosmos DBOnlinelager
  • NanobaseOnlinelager
  • ValkeyCachelagre
  • BloblagerLagerressourcer
CDC-tjenesterRegistrering af dataændringer
  • Databricks
  • Rockset
  • Kafka
  • Og mere

Det er ikke nogen lille bedrift at bygge og drive infrastruktur i denne skala, men det er heller ikke i sig selv usædvanligt vanskeligt. Det særlige ved vores situation var den hidtil usete hastighed, hvormed vi måtte skalere for at følge med den enorme vækst i brugere og produktefterspørgsel, samtidig med at vi opbyggede en moden platform. Systemudviklere bygger ofte til ti gange større skala og håber, at det holder i nogle år, mens de forbereder den næste tidobling. I vores tilfælde er vi vokset mere end ti gange år for år i de seneste tre år. Opbygningen og driften af Habitat har derfor været en række taktiske beslutninger i nøje rækkefølge: Vi har forstået hver komponent helt ned i detaljen for at få mest muligt ud af vores eksisterende teknologistak, samtidig med at vi har afværget kapacitetsmangel på lager og beregning og dermed skabt tid til grundlæggende investeringer.

  • 70 mio.+

    anmodninger pr. sekund

  • 1 mia.+

    mennesker hver uge

  • 500 PB+

    data

I takt med at OpenAI voksede, måtte Habitat følge med: Først skulle platformen være pålidelig nok til forretningskritisk produkttrafik, dernæst hurtig nok til globale brugere og til sidst kunne drives smidigt i enorm skala. Dette indlæg er det første i en serie på to dele om, hvordan vi skalerede onlinelageret. Her fortæller vi, hvordan Habitat udviklede sig, hvorfor vi ændrede det fra et bibliotek til en tjeneste, og hvordan vi gjorde en tjeneste skrevet i et usædvanligt sprog til denne type systemer – Python – til et pålideligt platformslag for lagring.

I et kommende indlæg går vi i detaljer med, hvordan vi gjorde multitenancy pålidelig i stor skala, vores lagdelte strategi til optimering af læseydelsen, og hvordan vi skalerede samarbejdet med Azure Cosmos DB til pålideligt at håndtere en hidtil uset efterspørgsel.

Hvad er Habitat?

Habitat udsprang af en enkel idé: Produktudviklere bør ikke behøve at tænke på databaseadministration. Habitat blev først lanceret til understøttelse af GPT'er ved DevDay 2023 som et lille Python-bibliotek, der kommunikerede med ChatGPTs primære server. Det understøttede et lille sæt handlinger, som under overfladen blev knyttet til databaseprogrammet Azure Cosmos DB.

Bibliotekets opgave var at give produktteamene en enkel måde at gemme og hente data på, uden at de skulle beherske de underliggende detaljer. Habitat tog sig af det nødvendige arbejde: at afgøre, hvilken slags data det drejede sig om, hvor de skulle komme fra eller sendes hen, om anmodningen var tilladt og så videre.

Produktudviklerne behøvede ikke at beskæftige sig med opslag i skemaer, routing, godkendelse, kryptering, serialisering, tilpasning af anmodninger eller forbindelsespuljer. De behøvede ikke engang at overveje, hvor dataene kom fra: Azure Cosmos DB, cachelagre eller andre lagertyper.

Figur 02 · Habitat-tjenesten

Forenklet anmodningsflow i Habitat

Ved at udskille lagerlogikken i en selvstændig tjeneste fik vi ét centralt kontrolpunkt for udrulninger, observerbarhed og platformforbedringer.

  • Anmodning
  • Svar

Klient

OpenAI

Azure Cosmos DB

Habitat-klient-SDK
envoy
  • habitat-serviceproces 1
  • habitat-serviceproces 2
  • habitat-serviceproces 3
habitat-envoy
  • habitat-cosmos-db-us0
  • habitat-cosmos-db-us1
  • habitat-cosmos-db-eu0

Python-biblioteket fungerede godt, og Habitat blev hurtigt taget i brug af produktudviklere hos OpenAI, selv om der ikke var en samlet central indsats for at gå væk fra selvbetjent Postgres og Azure Cosmos DB.

Efterhånden som produktbehovene udviklede sig, var det endda nemt for produktudviklerne at føje understøttelse af funktioner som cachelagring på klientsiden, komprimering og kryptering til det fælles bibliotek.

Opbygning af en tjeneste til bedre understøttelse af flere komplekse produkter

I midten af 2025 havde Habitat nået grænsen for, hvad en implementering på klientsiden kunne klare. Efterhånden som Habitat-laget blev mere komplekst, og OpenAI fik flere tjenester, blev bagudkompatible protokolændringer uigennemførlige.

I ét tilfælde ville vi begrænse konsekvenserne af et nedbrud i en enkelt region for vores mest kritiske datasæt ved at migrere dem til en række regionalt distribuerede Azure Cosmos DB-konti. Ændringen krævede ekstra routinglogik i klienten, deaktiveret bag et feature flag, udrulning til alle klienter og derefter aktivering af flaget.

Det tog flere dage at koordinere udrulninger på tværs af snesevis af tjenester og samarbejde med hvert team om dem. Før aktiveringen indså vi, at vi ville indføre skyggekopiering for at sikre, at sharding-logikken fungerede korrekt. Det tog endnu et par dage at udrulle. En fejlrettelse til noget, vi opdagede var forkert? Endnu et par dage. Til sidst var vi klar til at aktivere flaget, men så rullede et af teamene af andre årsager sin tjeneste tilbage til en tidligere klient med fejl. Det udløste netop det nedbrud, vi så ihærdigt havde forsøgt at undgå.

Ændringer af klientbiblioteket krævede kompleks koordinering mellem snesevis af tjenester – en proces, som blev stadig mere skrøbelig, ineffektiv og udsat for driftsfejl. For at reducere denne driftsmæssige spredning ved fremtidige udrulninger besluttede vi at gøre Habitat til en selvstændig tjeneste.

Ved at udskille lagerlogikken i en selvstændig tjeneste fik vi ét centralt kontrolpunkt for udrulninger, observerbarhed og platformforbedringer. I stedet for at håndtere spredte opdateringer kunne vi implementere forbedringer centralt, så alle OpenAI-produkter fik gavn af dem med det samme.

En centraliseret tjeneste giver os også ét kontrolpunkt, hvor vi kan levere de stærkeste grundfunktioner til datasikkerhed og privatliv. I Habitat-tjenesten kan vi centralt håndhæve politikker for adgangskontrol, føre revisionslog og begrænse adgangen til underliggende lagerressourcer som Azure Cosmos DB. Habitat spiller en afgørende rolle i beskyttelsen af brugerdata og forebyggelsen af uautoriseret adgang fra eksterne, interne og agentbaserede aktører.

Lancering af en Python-tjeneste i stor skala

Vi vidste, at vi havde brug for en tjeneste, men vi ville endnu ikke migrere væk fra Python, selv med Pythons ekstra overhead som tjeneste. Brugen af Python til en tjeneste med højt gennemløb øgede netværkslatensen og gav betydelige ekstra omkostninger til skalering af CPU og hukommelse sammenlignet med lokal biblioteksafvikling. Vi indså desuden, at Pythons ineffektivitet ikke ville være acceptabel ved 100 gange større skala, så en senere omskrivning var næsten uundgåelig.

Vi betragtede det dog som en strategisk påtagelse af teknisk gæld. Vores primære mål var på det tidspunkt ikke at optimere omkostninger eller ressourcer, men at fjerne blokeringer for produktudviklerne og skabe en stabil platform. Ved på kort sigt at acceptere ydelseskompromiserne ved en Python-tjeneste kunne vi prioritere mere presserende udfordringer, fastlægge vores centrale API'er og opbygge en robust infrastruktur.

Vi satsede også bevidst på, at den hurtige udvikling af vores egne kodningsmodeller ville gøre den tekniske vej enklere fremover. Vi satsede på, at Codex og GPT ville gøre migreringen mulig, når en fuld migrering væk fra Python blev nødvendig. Det sats viste sig senere at holde stik.

Det ville ikke være optimalt for ydelsen at køre Habitat som en Python-tjeneste, men det var et nødvendigt valg. Python gør det muligt at arbejde hurtigt, men det betød ikke, at vi kunne se stort på risikoen og acceptere markant højere latenser. Når en gennemsnitlig brugeranmodning udløser hundredvis af databasekald, er det det langsomste databasekald, brugeren mærker. Vi har konstateret, at den største udfordring ved at køre en Python-tjeneste i denne skala er at håndtere disse halelatenser.

Måling af asyncio-forsinkelsen

Asyncio hjælper Python med at afvikle I/O-bundne arbejdsbelastninger samtidigt, men omgår ikke Pythons GIL og giver ikke CPU-parallelitet. Ud over I/O-tung videresendelse af anmodninger håndterer Habitat mange CPU-tunge opgaver og baggrundsopgaver: routing, komprimering, kryptering, kontrolsummer, sundhedstjek af downstream-systemer, skyggekopiering og hedging af anmodninger.

Med så mange CPU-tunge arbejdsbelastninger og baggrundsopgaver i vores tjeneste kan forsinkelse i asyncio-planlægningen let dominere anmodningernes halelatens. Før finjusteringen op til den første lancering viste spor af anmodninger med p99-latens eller højere, at downstream-lageret svarede hurtigt, men anmodningerne ofte gik i stå, mens de ventede på, at den ansvarlige coroutine igen blev planlagt til at fortolke svaret.

Figur 03 · Måling af asyncio-forsinkelsen

Samtidighed er ikke CPU-parallelitet

Python asyncio muliggør samtidig behandling af anmodninger, men kun én anmodning ad gangen kører på CPU-tråden. Det påvirker anmodningernes latens markant, når der skal udføres meget CPU-arbejde.

CPU-behandling af anmodning/svarPython-netværkslæsning/-skrivningVent på Cosmos

Lidt CPU-arbejde

Korte Python-trin; I/O-ventetider overlapper

Meget CPU-arbejde

Lange Python-trin holder klare svar tilbage

0.0 / 40 illustrative enheder

For Python-tjenester hos OpenAI er det ud over almindelige målinger af udnyttelse og mætning for hukommelse, CPU, netværk og disk afgørende også at overvåge asyncio-løkken og dens belastning og derefter justere systemet tilsvarende.

Ved regelmæssigt at planlægge baggrundsopgaver og registrere forskellen mellem forventet og faktisk udførelsestid kan vi empirisk måle event-løkkens planlægningsforsinkelse i realtid. Ved høj udnyttelse og mange krævende opgaver er selv et beskedent antal samtidige anmodninger pr. proces nok til at skabe betydelig variation i planlægningen – op til flere hundrede millisekunder og i enkelte tilfælde flere sekunder.

Derfor lader vi hver proces betjene få samtidige anmodninger og skalerer i stedet antallet af Python-workerprocesser kraftigt ud.

Reduktion af halelatens i vores feature flag-konfigurationer

Ved den første lancering fandt vi gennem CPU-profilering af den aktive tjeneste en grundlæggende årsag til stor asyncio-forsinkelse og dermed høje halelatenser: periodisk JSON-fortolkning af vores feature flag-konfigurationer via Statsig, et værktøj til administration af feature flags, A/B-test og mere.

Statsig var som standard indstillet til at hente opdaterede konfigurationer hvert minut uden jitter, og konfigurationen omfattede alle produktionsregler for samtlige tjenester. I en anden sammenhæng var det arkitektonisk besluttet at køre op til otte Python-processer pr. pod for at øge CPU-udnyttelsen og sænke latenserne. Tilsammen betød det, at alle workers i hver pod på et tidspunkt hvert minut standsede behandlingen af igangværende anmodninger og i stedet brugte CPU-cyklusser på at fortolke en enorm konfigurationsfil.

Da CPU-profileringen havde hjulpet os med at finde årsagen, var løsningen enkel: udrul en mindre, målrettet konfiguration, forlæng opdateringsintervallet, og føj jitter til denne type baggrundsopgaver.

Belastningsfordeling og administration af forbindelsespuljer

For at holde asyncio-forsinkelsen lav er det også afgørende at fordele anmodningerne godt mellem serverprocesserne. Uden justering kan forbindelsespuljer modarbejde dette.

Med forbindelsespuljer på klientsiden kan en enkelt klientproces, der sender mange samtidige anmodninger, nøjes med at oprette få serverforbindelser og dermed sende hele sin belastning til få processer. Før vi justerede vores belastningsfordeling, varierede udnyttelsen meget. Enkelte processer i de mest belastede processer betjente 5-10 gange så mange samtidige anmodninger som gennemsnittet.

Vi opdagede det tilfældigt under en hændelse, hvor nogle processer forblev forringede længe efter den pludselige trafikstigning, selv om vi havde stoppet den klient, der overbelastede en del af tjenesten. Faktisk oplevede disse processer en accelererende forringelse og modtog stadig flere anmodninger, indtil vi genstartede dem. Når en pod først blev overbelastet, fastholdt en bestemt adfærd mere trafik på den overbelastede pod. Det var en fejltype, som nogle af vores kolleger kendte godt fra tidligere arbejde: metastabil fejl(åbner i et nyt vindue).

Vi mistænkte forbindelsespuljen og testede mistanken ved at begrænse forbindelsernes maksimale genbrugstid. Det begrænsede faktisk forringelsen og bekræftede, at vores undersøgelse var på rette spor. Yderligere undersøgelser viste, at Pythons aiohttp TCPConnector som standard genbruger forbindelser efter LIFO-princippet: Den senest returnerede forbindelse vælges til den næste anmodning. Det er normalt en fornuftig standard: Genbrug af nylige forbindelser gør det muligt for de ekstra forbindelser, der oprettes ved spidsbelastning, at udløbe, når de er inaktive, og reducerer dermed omkostningen ved at opretholde dem. I dette tilfælde skabte det en metastabil fejl for os. Under en spidsbelastning blev forbindelser fra anmodninger til langsommere, overbelastede servere returneret senere til puljen og derfor oftere valgt af efterfølgende anmodninger. Det samlede gradvist mere trafik på de pods, der allerede var pressede. Ved at ændre forbindelsespuljen til FIFO-genbrug brød vi feedbackløkken og reducerede samtidig variationen i anmodninger ved stabil drift.

Figur 04A · Forbindelsespuljer på klientsiden

LIFO sender nyt arbejde tilbage til den langsomme proces

Efter en spidsbelastning returnerer langsommere servere deres forbindelser til puljen sidst. LIFO får mere arbejde til at samle sig på de samme langsommere servere.

En indledende spidsbelastning rammer A, B og den langsommere proces C.

Figur 04B · Forbindelsespuljer på klientsiden

FIFO bryder feedbackløkken ved genbrug af forbindelser

FIFO bevarer flere aktive forbindelser efter en spidsbelastning, men fordeler arbejdsbelastningen jævnt mellem alle servere.

En indledende spidsbelastning rammer A, B og den langsommere proces C.

I dag bruger vi primært Istio og Envoy til forbindelsespuljer og bedre strategier for belastningsfordeling baseret på serverbelastning i hele OpenAI's infrastruktur, så problemet helt undgås.

Sådan undgår vi at overvælde downstream-ressourcer

En bivirkning ved at optimere til lav asyncio-forsinkelse og have så mange Python-processer er, at downstream-afhængigheder meget let overvældes af det enorme antal forbindelser – det såkaldte »thundering herd«-problem.

En almindelig daglig udrulning kan, hvis den ikke er indstillet til at gå langsomt, skabe betydelig CPU-aktivitet som følge af udskiftning af forbindelser. Eller en forbindelseslækage kan lægge netværket ned ved at mætte NAT-gatewayen. Problemerne er heller ikke usædvanlige for andre tjenester, men med ti gange så mange processer sænkes tærsklen markant. Det mætter ofte netværksressourcer, som klienterne ud fra gennemløbet alene ikke forventer at skulle håndtere under stabil drift.

Vi bruger også Envoy til at maksimere samlingen af forbindelser. Vi bruger Envoy til at opgradere Pythons HTTP/1-forbindelser til HTTP/2 og udnytte multiplexing, hvorefter forbindelserne samles i puljer og får længere levetid. Envoy giver os også ét centralt sted til at implementere hastighedsbegrænsninger og circuit breakers, som ville være mindre effektive i hver enkelt selvstændig Python-proces.

Figur 05 · Samling af forbindelser

De samme anmodninger, færre forbindelser

Forbindelsespuljer og multiplexing af HTTP/2-forbindelser reducerer forbindelsesbelastningen på downstream-systemer.

AnmodningSvarInaktiv keep-alive

Hvorfor Habitat kan mindre

En af grundene til, at vi kunne skalere Python så langt, var Habitats begrænsede API, som gør omkostningen pr. anmodning forudsigelig. I stedet for at lade klienter opbygge vilkårlige SQL-forespørgsler, som kan føre til store tabelscanninger eller joins på tværs af mange tabeller, tilbyder Habitat en enkel NoSQL-API. Fraværet af en avanceret API er et bevidst kompromis i Habitats design.

Vi optimerer efter enkle, forudsigelige anmodninger med en konstant arbejdsmængde. Vores erfaring er, at disse systemer er væsentligt lettere at skalere og svære at bruge forkert. Anmodninger med uforudsigelig fan-out er driftsmæssigt farlige: De gør isolering og belastningsfordeling vanskeligere og skaber pludselige latensstigninger, som er svære at skalere til for både tjenesten og dens klienter.

Før vi gik over til Habitat og Azure Cosmos DB, blev de fleste af OpenAI's onlinedata lagret i Postgres. Dengang var det let at gennemgå alle ændringer af forespørgsler og skemaer og sikre, at de opførte sig korrekt og brugte indekserede data, før de blev sendt i produktion. Da teamet og produkterne voksede, blev det hurtigt uoverskueligt og førte ofte til nedbrud, hvor én ny, dyr forespørgsel på en kritisk sti lagde databasen ned.

Problemet er en ubalance i omkostningerne: Det er billigt og nemt at skrive SQL-forespørgsler, som er dyre og vanskelige at køre. I Habitat undgår vi dette og gør dyre forespørgsler meget tydelige på klientsiden. Der findes ingen ubegrænsede forespørgsler, som kan overbelaste Habitat, og komplekse joins og graftraverseringer kræver, at produktteamene selv udfører en del af det tunge arbejde. Det fremmer generelt mere effektive design.

Habitat tilbyder en NoSQL-API bygget op omkring klientdefinerede objekt- og kanttyper, inspireret af TAO(åbner i et nyt vindue). Klienterne definerer på forhånd objekter og kanter samt deres indbyrdes relationer, men ikke indholdet af hver type. De resulterende relationer ligner en graf, men Habitat understøtter ikke selv almindelige graftraverseringsforespørgsler ud over opslag af direkte kanter for et bestemt objekt.

Vi partitionerer grafen, så hvert objekt og dets tilhørende kanter ligger sammen i en partition på lagerniveau. På databaseniveau forsøger vi dog ikke målrettet at placere objekter sammen med de fjernobjekter, som kanterne peger på. Det betyder, at modellen nemt kan partitioneres med henblik på horisontal skalering, men at graftraversering er ineffektiv, fordi et enkelt hop mellem objekter kan kræve hentning fra to helt forskellige Azure Cosmos DB-konti i forskellige regioner.

Til klienter med mere komplekse forespørgselsbehov tilbyder vi en sekundær offlinevisning af Habitat via Rockset. Vi bruger change data capture (CDC) til at streame ændringer fra onlinelageret til isolerede Rockset-instanser næsten i realtid. Hvert klientteam er ansvarligt for at skalere sin egen Rockset-instans til sine komplekse forespørgselsbehov.

Denne klargøring af Rockset giver klienterne ekstra besvær, men vi mener, at det lige nu er det rette kompromis: Enkle forespørgsler er standarden, mens brugere med behov for komplekse forespørgsler får en alternativ mulighed. Dette design isolerer vores onlinelager fra læsetunge analyse- og søgebelastninger.

Migrering fra Python til Rust

Ved at udskyde en omskrivning af Python-løsningen i et år kunne vi under vores hypervækst fokusere på mere presserende og betydningsfulde udfordringer. Platformen var blevet mere moden, væksten accelererede fortsat, og tjenesten var nu den næststørste hos OpenAI målt på antal kerner og den fjerdestørste målt på Envoy-kapacitet. Derfor var tiden endelig inde til at gå videre fra Python. På sit højeste hjalp Python os med at betjene mere end 20 millioner anmodninger i sekundet.

I andet kvartal 2026 kunne blot to udviklere med Codex og GPT‑5.5 omskrive hele tjenesten i Rust. Den nye Rust-tjeneste håndterer nu 95 % af vores produktionsanmodninger, og i de kommende uger udfaser vi Python helt. Vores data viser, at Rust-tjenesten er seks gange mere CPU-effektiv og 15 gange mere hukommelseseffektiv end Python-versionen og har markant lavere gennemsnits- og halelatenser. Vi vil dele flere erfaringer i et kommende blogindlæg.

Optimering af vores databaselag, Azure Cosmos DB

Python-tjenesten – og nu Rust-tjenesten – er kun én del af Habitat. I anden del af serien om, hvordan vi hurtigt skalerede vores onlinelager til at betjene over en milliard ChatGPT‑brugere, ser vi nærmere på lagerlaget, og hvordan Habitat betjener mere end 500 petabyte og over 70 millioner anmodninger i sekundet.

Hvis du vil arbejde med OLTP-systemer i banebrydende skala og interesserer dig for denne type udvikling, kan du se denne ledige stilling på vores team.

Forfattere

Jon Lee, Chaomin Yu og Ben Ries