Snabb skalning av onlinelagring för över 1 miljard ChatGPT‑användare
Så anpassade vi vår lagringsplattform Habitat i Python för en tillväxt utan motstycke.
Av Jon Lee, Chaomin Yu och Ben Ries, tekniska medarbetare
Alla OpenAI-produkter är beroende av snabb och tillförlitlig åtkomst till data, oavsett om någon loggar in, kontrollerar sina Codex-inställningar eller inleder en ny konversation i ChatGPT. Var och en av dessa åtgärder kan kräva många separata datauppslagningar innan produkten kan svara. Om begärandena är långsamma upplevs produkten som långsam. Om begärandena misslyckas slutar produkten att fungera helt.
Habitat är den onlinelagringsplattform vi byggde för att OpenAI-produkter snabbt och tillförlitligt ska kunna komma åt den information de behöver. Habitat hanterar nu över 70 miljoner begäranden per sekund och stöder produkter som används av mer än en miljard människor varje vecka i nästan 40 geografiska regioner. Habitat lanserades först för att stödja GPT:er vid DevDay 2023, som ett enkelt Python-bibliotek på klientsidan anslutet till en enda databas. I dag är det ett komplext distribuerat system som hanterar över 500 petabyte data.
Figur 01 · Vad är Habitat?
Plattform för onlinelagring
Habitat är den onlinelagringsplattform vi byggde för att OpenAI-produkter snabbt och tillförlitligt ska kunna komma åt den information de behöver.
- Begäran
- Svar
- Ändringar (CDC)
Att bygga och driva infrastruktur i den här skalan är ingen liten bedrift, men heller inte särskilt svårt. Det unika i vår situation var den exempellösa takt som vi behövde skala i för att möta en häpnadsväckande användartillväxt och produktefterfrågan, samtidigt som vi byggde en mogen plattform. Systemingenjörer bygger ofta för tio gånger större skala och hoppas att det ska räcka i några år medan de förbereder nästa tiodubbling. I vårt fall har vi vuxit mer än tio gånger om året under de senaste tre åren. Att bygga och driva Habitat har därför handlat om en rad taktiska beslut och rätt ordningsföljd: att förstå varje komponent på lägsta nivå för att få ut så mycket som möjligt av vår befintliga stack, samtidigt som vi hanterat brist på lagrings- och beräkningskapacitet för att vinna tid till grundläggande investeringar.
- 70 mn+
begäranden per sekund
- 1 md+
människor varje vecka
- 500 PB+
data
När OpenAI växte behövde Habitat växa med företaget: först genom att bli tillräckligt tillförlitligt för verksamhetskritisk produkttrafik, sedan tillräckligt snabbt för globala användare och slutligen genom att skickligt fungera i enorm skala. Det här inlägget är det första i en serie i två delar om hur vi skalade onlinelagringen. Här berättar vi hur Habitat utvecklades, varför vi gjorde om det från ett bibliotek till en tjänst och hur vi tänjde en tjänst skriven i ett ovanligt språk för serverstackar – Python – till ett tillförlitligt plattformslager för lagring.
I ett kommande inlägg går vi in på hur vi skapade tillförlitlig multitenans i stor skala, vår skiktade strategi för att optimera läsprestanda och hur vi skalade vårt samarbete med Azure Cosmos DB för att tillförlitligt hantera en efterfrågan utan motstycke.
Habitat började med en enkel idé: produktutvecklare ska inte behöva tänka på databashantering. Habitat lanserades först för att stödja GPT:er vid DevDay 2023, som ett litet Python-bibliotek som samverkade med ChatGPT:s huvudserver. Det stödde ett fåtal åtgärder som i bakgrunden mappades till databasprogrammet Azure Cosmos DB.
Bibliotekets uppgift var att ge produktteamen ett enkelt sätt att lagra och hämta data utan att behöva behärska de underliggande detaljerna. Habitat skötte det nödvändiga arbetet: att avgöra vilken typ av data det handlade om, varifrån de skulle hämtas eller vart de skulle skickas, om begäran var tillåten och så vidare.
Produktutvecklarna behöver inte tänka på uppslagning i Schema, routning, auktorisering, kryptering, serialisering, formning av begäranden eller anslutningspoolning. De behövde inte ens fundera på varifrån data kom: Azure Cosmos DB, cacheminnen eller andra typer av lagring.
Figur 02 · Habitat-tjänsten
Förenklat flöde för Habitat-begäranden
Genom att frikoppla lagringslogiken till en fristående tjänst skapade vi en enda kontrollpunkt för driftsättningar, observerbarhet och plattformsförbättringar.
- Begäran
- Svar
Python-biblioteket fungerade väl och Habitat fick snabbt spridning bland produktutvecklare på OpenAI, trots att det inte gjordes någon samordnad central satsning för att lämna självbetjänad Postgres och Azure Cosmos DB.
När produktbehoven utvecklades var det dessutom enkelt för produktutvecklarna att utöka det gemensamma biblioteket med stöd för funktioner som cachelagring på klientsidan, komprimering eller kryptering.
I mitten av 2025 hade Habitat nått gränsen för vad en implementering på klientsidan klarade. När Habitat-lagret blev mer komplext och antalet OpenAI-tjänster ökade blev bakåtkompatibla protokolländringar ogenomförbara.
I ett fall ville vi begränsa konsekvenserna av ett avbrott i en enskild region för våra mest kritiska datauppsättningar genom att migrera dem till en uppsättning regionalt distribuerade Azure Cosmos DB-konton. Ändringen krävde att vi lade till extra routningslogik i klienten, inaktiverad bakom en funktionsflagga, såg till att den distribuerades till alla klienter och därefter aktiverade flaggan.
Det tog flera dagar att samordna driftsättningar för dussintals tjänster och arbeta med varje team för att lansera ändringen. Innan vi aktiverade den insåg vi att vi ville införa viss skuggning för att säkerställa att shardningslogiken fungerade korrekt. Det tog ytterligare ett par dagar att driftsätta. En felrättning för något vi insåg var fel? Ytterligare ett par dagar. Till slut var vi redo att aktivera flaggan, men då återställde ett av teamen av orelaterade skäl sin tjänst till en tidigare klient med fel och orsakade just det avbrott som vi hade arbetat så hårt för att undvika.
Ändringar i klientbiblioteket krävde komplicerad samordning mellan dussintals tjänster – en process som blev allt bräckligare, ineffektivare och mer utsatt för driftfel. För att minska denna operativa spridning vid framtida driftsättningar beslutade vi att göra Habitat till en egen tjänst.
Genom att frikoppla lagringslogiken till en fristående tjänst skapade vi en enda kontrollpunkt för driftsättningar, observerbarhet och plattformsförbättringar. I stället för att hantera utspridda uppdateringar kunde vi genomföra förbättringar centralt och ge alla OpenAI-produkter omedelbar nytta.
En centraliserad tjänst ger oss också en enda kontrollpunkt där vi kan tillhandahålla de starkaste grundfunktionerna för datasäkerhet och integritet. I Habitat-tjänsten kan vi centralt genomdriva åtkomstpolicyer, utföra granskningsloggning och begränsa åtkomsten till underliggande lagringsresurser som Azure Cosmos DB. Habitat spelar en avgörande roll för att skydda användardata och förhindra obehörig åtkomst från externa och interna aktörer samt agentaktörer.
Vi visste att vi behövde en tjänst, men ville inte lämna Python riktigt än, trots det extra overhead som Python medförde som tjänst. Att använda Python för en tjänst med hög kapacitet ökade nätverkslatensen och medförde betydande skalningskostnader för CPU och minne jämfört med lokal körning som bibliotek. Vi insåg också att Pythons ineffektivitet inte skulle vara acceptabel vid 100 gånger större skala, vilket gjorde en framtida omskrivning nästan oundviklig.
Vi såg dock detta som ett strategiskt åtagande av teknisk skuld. Vårt främsta mål var då inte att optimera kostnader eller resurser, utan att undanröja hinder för produktutvecklarna och skapa en stabil plattform. Genom att kortsiktigt acceptera prestandakompromisserna med en Python-tjänst kunde vi prioritera mer akuta utmaningar, etablera våra centrala API:er och bygga en robust infrastruktur.
Vi gjorde också en kalkylerad satsning på att den snabba utvecklingen av våra egna kodningsmodeller skulle förenkla den tekniska vägen framåt. Vi satsade på att Codex och GPT skulle göra migreringen genomförbar när vi väl behövde lämna Python helt. Den satsningen visade sig till slut vara rätt.
Att köra Habitat som en Python-tjänst skulle ge sämre prestanda, men var ett nödvändigt val. Python låter oss arbeta snabbt, men det innebar inte att vi kunde strunta i riskerna och acceptera märkbart högre latenser. När en genomsnittlig användarbegäran leder till hundratals databasanrop är det det långsammaste anropet som användaren märker. Vi har sett att den största utmaningen med en Python-tjänst i den här skalan är att hantera svanslatenserna.
Asyncio hjälper Python att köra I/O-bundna arbetslaster parallellt, men kringgår inte Pythons GIL och ger ingen CPU-parallellism. Utöver I/O-tung proxyhantering av begäranden sköter Habitat många CPU-tunga funktioner och bakgrundsuppgifter: routning, komprimering, kryptering, kontrollsummering, hälsokontroller av underliggande tjänster, skuggning och hedgning av begäranden.
Med så många CPU-tunga arbetslaster och bakgrundsuppgifter i tjänsten kan schemaläggningsfördröjningen i asyncio lätt dominera svanslatensen. Före finjusteringen inför den första lanseringen såg vi i spårningar av begäranden med p99-latens eller högre att lagringen nedströms svarade snabbt, men att begäranden ofta stannade upp i väntan på att ansvarig korutin skulle schemaläggas igen för att tolka svaret.
Figur 03 · Spåra asyncio-fördröjningen
Samtidighet är inte CPU-parallellism
Python asyncio kan behandla begäranden samtidigt, men bara en begäran i taget körs på CPU-tråden. Det påverkar latenserna kraftigt när mycket CPU-arbete behöver utföras.
Lite CPU-arbete
Korta Python-steg; I/O-väntan överlapparMycket CPU-arbete
Långa Python-steg låter färdiga svar väntaFör Python-tjänster på OpenAI är det, utöver vanliga mått på utnyttjande och mättnad för minne, CPU, nätverk och disk, avgörande att övervaka asyncio-loopen och dess belastning och sedan finjustera därefter.
Genom att regelbundet schemalägga bakgrundsuppgifter och registrera skillnaden mellan förväntad och faktisk körtid kan vi empiriskt mäta eventloopens schemaläggningsfördröjning i realtid. Vid högt utnyttjande och många kostsamma uppgifter räcker även ett måttligt antal samtidiga begäranden per process för att skapa betydande variationer i schemaläggningen, upp till hundratals millisekunder och i vissa ytterlighetsfall flera sekunder.
Därför låter vi varje process endast hantera ett litet antal samtidiga begäranden och skalar i stället ut antalet Python-arbetsprocesser kraftigt.
Vid den första lanseringen hittade vi genom CPU-profilering i produktion en grundorsak till hög asyncio-fördröjning och därmed höga svanslatenser: regelbunden JSON-tolkning av våra funktionsflaggkonfigurationer via Statsig, ett verktyg för att hantera funktionsflaggor, köra A/B-tester med mera.
Som standard var Statsig konfigurerat att hämta uppdaterade konfigurationer varje minut utan tidsvariation, och konfigurationen innehöll alla produktionsregler för samtliga tjänster. På annat håll hade vi fattat ett arkitekturbeslut om att köra upp till åtta Python-processer per podd för att öka CPU-användningen och sänka latenserna. Tillsammans innebar detta att varje podd en gång i minuten fick ett ögonblick då alla dess arbetsprocesser slutade bearbeta pågående begäranden och i stället använde CPU-cyklerna till att tolka en enorm konfigurationsfil.
När CPU-profileringen hade hjälpt oss hitta grundorsaken var lösningen enkel: driftsätt en mindre, riktad konfiguration, förläng uppdateringsintervallet och lägg till viss tidsvariation för sådana bakgrundsuppgifter.
För att hålla asyncio-fördröjningen låg är det också avgörande att fördela begäranden väl mellan serverprocesserna. Utan finjustering kan anslutningspoolning motverka detta.
Med anslutningspoolning på klientsidan kan en enda klientprocess som gör många samtidiga begäranden upprätta bara ett fåtal serveranslutningar och därmed skicka hela sin belastning till ett fåtal processer. Innan vi justerade lastbalanseringen varierade utnyttjandet kraftigt i tjänsten, och vissa processer i svansen hanterade 5–10 gånger fler samtidiga begäranden än genomsnittet.
Vi upptäckte detta av en slump vid en incident där en delmängd processer fortsatte att fungera dåligt långt efter trafikpuckeln, trots att vi hade stoppat klienten som överbelastade en del av tjänsten. Processerna försämrades faktiskt okontrollerat och tog emot allt fler begäranden tills vi startade om dem. När en podd väl hade överbelastats gjorde ett visst beteende att ännu mer trafik bands till den. Detta var en typ av fel som några av våra kollegor kände väl igen från tidigare arbete: metastabilt fel(öppnas i ett nytt fönster).
Vi misstänkte att anslutningspoolen var orsaken och testade detta genom att begränsa den maximala tiden för återanvändning av anslutningar. Det begränsade mycket riktigt försämringen och bekräftade att undersökningen var på rätt spår. Vidare undersökning visade att Pythons aiohttp TCPConnector som standard återanvänder anslutningar enligt LIFO: den senast återkomna anslutningen väljs till nästa begäran. Normalt är detta en rimlig standard: när nya anslutningar återanvänds kan de extra anslutningar som skapats för trafikpucklar nå sin tidsgräns för inaktivitet, vilket minskar kostnaden för att underhålla dem. I vårt fall skapade det ett metastabilt fel. Under en trafikpuckel återförde begäranden till långsammare, överbelastade servrar anslutningar till poolen senare. Därför valdes de oftare av efterföljande begäranden, så att allt mer trafik gradvis koncentrerades till de poddar som redan hade problem. Genom att ändra anslutningspoolen till återanvändning enligt FIFO bröt vi återkopplingsloopen och minskade även variationen i begäranden vid stabil drift.
Figur 04A · Anslutningspoolning på klientsidan
LIFO skickar tillbaka nytt arbete till den långsamma processen
Efter en trafikpuckel återför långsammare servrar sina anslutningar till poolen sist. LIFO gör att mer arbete koncentreras till samma långsammare servrar.
En inledande trafikpuckel når A, B och den långsammare processen C.
Figur 04B · Anslutningspoolning på klientsidan
FIFO bryter återkopplingsloopen vid återanvändning av anslutningar
FIFO behåller fler aktiva anslutningar efter en trafikpuckel, men fördelar arbetslasten rättvist mellan alla servrar.
En inledande trafikpuckel når A, B och den långsammare processen C.
I dag förlitar vi oss främst på Istio och Envoy för anslutningspoolning och bättre balanseringsstrategier som tar hänsyn till serverbelastning i hela OpenAI:s infrastruktur, vilket helt undviker problemet.
En bieffekt av finjusteringen för låg asyncio-fördröjning och det stora antalet Python-processer är att underliggande beroenden mycket lätt kan överbelastas av det enorma antalet anslutningar, en så kallad ”thundering herd”.
En vanlig daglig driftsättning kan, om den inte har finjusterats för att gå långsamt, orsaka betydande CPU-belastning när anslutningar byts ut. Eller så kan en anslutningsläcka slå ut nätverket genom att mätta NAT-gatewayen. Sådana problem är inte ovanliga för andra tjänster heller, men tröskeln sänks betydligt när man har en storleksordning fler processer. Det mättar ofta nätverksresurser som klienterna, utifrån enbart Kapacitet, inte förväntar sig att behöva hantera vid stabil drift.
Vi använder också Envoy för att maximera sammanslagningen av anslutningar. Vi använder Envoy för att uppgradera Pythons HTTP/1-anslutningar till HTTP/2 och dra nytta av multiplexering, och därefter poola anslutningarna och förlänga deras livslängd. Envoy ger oss också en central plats för hastighetsbegränsningar och kretsbrytare, som skulle vara mindre effektiva i varje fristående Python-process.
Figur 05 · Sammanslagning av anslutningar
Samma begäranden, färre anslutningar
Anslutningspoolning och multiplexering av HTTP/2-anslutningar minskar anslutningsbelastningen på underliggande tjänster.
En anledning till att vi kunde skala Python så långt var Habitats begränsade API, som gör kostnaden per begäran förutsägbar. I stället för att låta klienter skapa godtyckliga SQL-frågor som kan leda till omfattande tabellsökningar eller kopplingar mellan många tabeller exponerar Habitat ett enkelt NoSQL-API. Avsaknaden av ett kraftfullt API är en medveten kompromiss i Habitats utformning.
Vi optimerar för enkla och förutsägbara begäranden med konstant arbetsmängd. Enligt vår erfarenhet är sådana system betydligt enklare att skala och svåra att använda fel. Begäranden med oförutsägbar förgrening är driftmässigt riskabla: de försvårar isolering och lastbalansering och skapar latensstup som är svåra att skala för både tjänsten och dess klienter.
Innan vi gick över till Habitat och Azure Cosmos DB lagrades merparten av OpenAI:s onlinedata i Postgres. På den tiden var det enkelt att granska alla ändringar av frågor och Schema för att säkerställa att de fungerade väl och använde indexerade data innan de driftsattes i produktion. När teamet och produkterna växte blev detta snabbt ohanterligt och orsakade ofta avbrott, eftersom en enda ny, kostsam fråga i ett kritiskt flöde kunde slå ut databasen.
Problemet är kostnadsobalansen: det är billigt och enkelt att skriva SQL-frågor som är dyra och svåra att köra. I Habitat undviker vi detta och gör kostsamma frågor mycket tydliga på klientsidan. Det finns inga obegränsade frågor som kan överbelasta Habitat, och komplexa kopplingar och graftraverseringar kräver att produktteamen gör en del av det tunga arbetet, vilket överlag främjar effektivare lösningar.
Habitat exponerar ett NoSQL-API uppbyggt kring klientdefinierade objekt- och kanttyper, inspirerat av TAO(öppnas i ett nytt fönster). Klienterna fördefinierar objekt och kanter samt deras inbördes relationer, men inte innehållet i varje typ. De resulterande relationerna liknar en graf, men Habitat stöder inte vanliga frågor för graftraversering utöver frågor om direkta kanter från ett visst objekt.
Vi partitionerar grafen så att varje objekt och dess tillhörande kanter samlokaliseras i en partition på lagringsnivå, men gör ingen samordnad insats på databasnivå för att samlokalisera objekt med de fjärrobjekt som deras kanter pekar på. Det innebär att Modellen enkelt kan partitioneras för horisontell skalbarhet, men att graftraverseringar blir ineffektiva eftersom varje steg mellan objekt kan kräva hämtning från två helt olika Azure Cosmos DB-konton i olika regioner.
För klienter med mer komplexa frågebehov erbjuder vi en sekundär offlinevy av Habitat via Rockset. Vi använder Change Data Capture (CDC) för att strömma ändringar från onlinelagringen till isolerade Rockset-instanser i nära realtid. Varje klientteam ansvarar för att skala sin egen Rockset-instans efter sina komplexa frågebehov.
Denna etablering av Rockset medför extra friktion för klienterna, men vi anser att det just nu är rätt kompromiss: enkla frågor är standard, samtidigt som det finns en utväg för dem som behöver komplexa frågor. Denna utformning isolerar onlinelagringen från läsintensiva analys- och sökarbetslaster.
Genom att skjuta upp omskrivningen av Python i ett år kunde vi fokusera på mer brådskande och betydelsefulla utmaningar under vår hypertillväxt. När plattformen hade mognat och tillväxten fortsatte accelerera – samtidigt som tjänsten var OpenAI:s näst största räknat i antal kärnor och den fjärde största sett till Envoy-avtryck – var det till slut dags att lämna Python. Som mest hjälpte Python oss att hantera över 20 miljoner begäranden per sekund.
Under andra kvartalet 2026 kunde vi med bara två utvecklare, Codex och GPT‑5.5 skriva om hela tjänsten i Rust. Den nya Rust-tjänsten hanterar nu 95 procent av våra produktionsbegäranden. Under de kommande veckorna avvecklar vi Python helt. Våra data visar att Rust-tjänsten är 6 gånger mer CPU-effektiv och 15 gånger mer minneseffektiv än Python-versionen, med betydligt lägre genomsnitts- och svanslatenser. Vi planerar att dela fler lärdomar i ett framtida blogginlägg.
Python-tjänsten – och numera Rust-tjänsten – är bara en del av Habitat. I del II av den här serien om hur vi snabbt skalade vår onlinelagring för över en miljard ChatGPT‑användare berättar vi om lagringslagret och hur Habitat hanterar över 500 petabyte och mer än 70 miljoner begäranden per sekund.
Om du vill arbeta med OLTP-system i banbrytande skala och är intresserad av den här typen av teknik kan du se den lediga tjänsten i vårt team.


