Scalarea rapidă a stocării online pentru peste 1 miliard de utilizatori ChatGPT
Cum am adaptat în Python platforma de stocare a aplicațiilor Habitat pentru o creștere fără precedent.
De Jon Lee, Chaomin Yu și Ben Ries, membri ai personalului tehnic
Fiecare produs OpenAI depinde de accesul rapid și fiabil la date, indiferent dacă cineva se conectează, își verifică setările Codex sau începe o conversație nouă în ChatGPT. Fiecare dintre aceste acțiuni poate necesita numeroase căutări separate de date înainte ca produsul să poată răspunde. Dacă solicitările sunt lente, produsul pare lent. Dacă solicitările eșuează, produsul încetează complet să funcționeze.
Habitat este platforma de stocare online pe care am construit-o pentru ca produsele OpenAI să poată accesa rapid și fiabil informațiile necesare. Habitat procesează acum peste 70 de milioane de solicitări pe secundă și susține, în aproape 40 de regiuni geografice, produse folosite săptămânal de peste un miliard de persoane. Habitat a fost lansat inițial pentru a susține GPT‑urile la DevDay 2023, ca o simplă bibliotecă Python pe partea de client, conectată la o singură bază de date. Astăzi este un sistem distribuit complex, care deservește peste 500 de petabyți de date.
Figura 01 · Ce este Habitat?
Platformă de stocare online
Habitat este platforma de stocare online pe care am construit-o pentru ca produsele OpenAI să poată accesa rapid și fiabil informațiile necesare.
- Solicitare
- Răspuns
- Modificări (CDC)
Construirea și operarea infrastructurii la această scară nu este deloc simplă, dar nici deosebit de dificilă. Situația noastră a fost unică prin viteza fără precedent cu care a trebuit să scalăm pentru a susține creșterea uluitoare a numărului de utilizatori și cererea pentru produse, construind simultan o platformă matură. Deseori, inginerii de sisteme proiectează pentru o scară de zece ori mai mare și speră că aceasta va rezista câțiva ani, timp în care se pregătesc pentru următoarea creștere de zece ori. În cazul nostru, am crescut de peste zece ori de la an la an în ultimii trei ani. Prin urmare, construirea și operarea Habitat au presupus o succesiune de decizii tactice: înțelegerea fiecărei componente până la cel mai profund nivel pentru a exploata la maximum stiva existentă, evitând totodată crizele de capacitate de stocare și calcul pentru a câștiga timp în vederea investițiilor fundamentale.
- 70 mil.+
solicitări pe secundă
- 1 mld.+
persoane în fiecare săptămână
- 500 PB+
date
Pe măsură ce OpenAI a crescut, Habitat a trebuit să țină pasul: mai întâi devenind suficient de fiabil pentru traficul esențial al produselor, apoi suficient de rapid pentru utilizatorii globali și, în cele din urmă, capabil să opereze cu agilitate la scară uriașă. Acest articol este primul dintr-o serie de două părți despre modul în care am scalat stocarea online. În acest articol vom prezenta evoluția Habitat, motivele pentru care l-am transformat dintr-o bibliotecă într-un serviciu și cum am extins un serviciu scris într-un limbaj neobișnuit pentru această utilizare, Python, până a devenit un strat fiabil al platformei de stocare.
Într-un articol viitor, vom explica în detaliu cum am asigurat fiabilitatea arhitecturii multi-client la scară largă, strategia noastră pe mai multe niveluri pentru optimizarea performanței citirilor și modul în care ne-am extins parteneriatul cu Azure Cosmos DB pentru a face față în mod fiabil unei cereri fără precedent.
Habitat a pornit de la o idee simplă: inginerii de produs nu ar trebui să fie nevoiți să se ocupe de administrarea bazelor de date. Habitat a fost lansat inițial pentru a susține GPT‑urile la DevDay 2023, ca o mică bibliotecă Python care interacționa cu serverul principal ChatGPT. Aceasta accepta un set restrâns de operațiuni care erau asociate intern aplicației de bază de date Azure Cosmos DB.
Rolul bibliotecii era să ofere echipelor de produs o modalitate simplă de a stoca și prelua date fără să fie nevoite să stăpânească detaliile infrastructurii. Habitat se ocupa de operațiunile necesare: identifica tipul datelor, sursa sau destinația lor, dacă solicitarea era permisă și așa mai departe.
Inginerii de produs nu trebuie să se preocupe de căutarea schemei, rutare, autorizare, criptare, serializare, modelarea solicitărilor și gruparea conexiunilor. Nici măcar nu trebuiau să se gândească de unde provin datele: Azure Cosmos DB, memorii cache sau alte tipuri de stocare.
Figura 02 · Serviciul Habitat
Flux simplificat al solicitărilor Habitat
Separând logica de stocare într-un serviciu autonom, am creat un punct unic de control pentru implementări, observabilitate și îmbunătățirea platformei.
- Solicitare
- Răspuns
Biblioteca Python a funcționat bine, iar Habitat a fost adoptat rapid de inginerii de produs de la OpenAI, deși nu a existat un efort centralizat de renunțare la utilizarea autonomă a Postgres și Azure Cosmos DB.
Pe măsură ce nevoile produselor evoluau, dezvoltatorii puteau chiar să adauge ușor în biblioteca comună funcții precum memorarea în cache pe partea de client, compresia sau criptarea.
La jumătatea anului 2025, Habitat își atinsese limitele ca implementare pe partea de client. Pe măsură ce stratul Habitat devenise mai complex, iar numărul serviciilor OpenAI crescuse, modificările de protocol compatibile cu versiunile anterioare deveniseră nepractice.
Într-un caz, am vrut să reducem impactul unei întreruperi într-o singură regiune asupra celor mai importante seturi de date, migrându-le într-un grup de conturi Azure Cosmos DB distribuite regional. Modificarea a necesitat introducerea unei logici suplimentare de rutare în client, dezactivată printr-un semnalizator de funcționalitate, implementarea ei pentru toți clienții și apoi activarea semnalizatorului.
Coordonarea implementărilor în zeci de servicii și colaborarea cu fiecare echipă au durat câteva zile. Înainte de activare, ne-am dat seama că doream să introducem duplicarea solicitărilor pentru a verifica dacă logica de partiționare era corectă. Implementarea acesteia a mai durat câteva zile. Remedierea unei erori pe care tocmai o identificaserăm? Alte câteva zile. În cele din urmă eram gata să activăm semnalizatorul, dar una dintre echipe și-a readus serviciul, din motive fără legătură, la o versiune anterioară și defectuoasă a clientului, provocând chiar întreruperea pe care ne străduiserăm atât de mult să o evităm.
Modificările aduse bibliotecii client necesitau o coordonare complexă între zeci de servicii, un proces care s-a dovedit tot mai fragil, ineficient și predispus la erori operaționale. Pentru a reduce numărul de servicii care necesitau coordonare la implementările viitoare, am decis să separăm Habitat într-un serviciu de sine stătător.
Separând logica de stocare într-un serviciu autonom, am creat un punct unic de control pentru implementări, observabilitate și îmbunătățirea platformei. În loc să gestionăm actualizări fragmentate, puteam implementa centralizat îmbunătățiri, oferind avantaje imediate fiecărui produs OpenAI.
Un serviciu centralizat ne oferă și un punct unic de control pentru cele mai solide mecanisme de securitate și confidențialitate a datelor. În serviciul Habitat putem aplica centralizat politici de control al accesului, efectua jurnalizarea pentru audit și limita accesul la resursele de stocare subiacente, precum Azure Cosmos DB. Habitat are un rol esențial în protejarea datelor utilizatorilor și prevenirea accesului neautorizat din partea actorilor externi, interni și de tip agent.
Știam că avem nevoie de un serviciu, dar nu voiam încă să renunțăm la Python, chiar dacă utilizarea sa pentru un serviciu implica un consum suplimentar de resurse. Utilizarea Python pentru un serviciu cu randament ridicat a mărit latența rețelei și a generat costuri semnificative de scalare a procesorului și memoriei față de execuția unei biblioteci locale. În plus, știam că ineficiențele Python nu vor fi acceptabile la o scară de 100 de ori mai mare, astfel că o rescriere ulterioară era aproape inevitabilă.
Am considerat însă că această datorie tehnică era asumată strategic. Obiectivul nostru principal nu era atunci optimizarea costurilor sau a resurselor, ci deblocarea dezvoltatorilor de produse și stabilizarea platformei. Acceptând pe termen scurt compromisurile de performanță ale unui serviciu Python, am putut prioritiza problemele mai urgente, defini API-urile esențiale și construi o infrastructură robustă.
Am mizat, în mod calculat, și pe faptul că evoluția rapidă a propriilor modele de programare va simplifica ulterior parcursul tehnic. Am mizat pe faptul că, atunci când va deveni necesară migrarea completă de la Python, Codex și GPT o vor face posibilă. În cele din urmă, pariul s-a dovedit corect.
Rularea Habitat ca serviciu Python nu era optimă ca performanță, dar era o alegere necesară. Python ne permite să avansăm rapid, dar asta nu înseamnă că puteam ignora riscurile și accepta latențe mult mai mari. Când o solicitare obișnuită a utilizatorului generează sute de apeluri către baza de date, utilizatorul resimte cel mai lent dintre acestea. Am constatat că principala dificultate a rulării unui serviciu Python la această scară este gestionarea latențelor extreme.
Asyncio ajută Python să execute simultan sarcini limitate de operațiuni I/O, dar nu ocolește GIL-ul Python și nu oferă paralelism la nivel de CPU. Pe lângă intermedierea solicitărilor cu multe operațiuni I/O, Habitat gestionează numeroase responsabilități și sarcini de fundal solicitante pentru CPU: rutare, compresie, criptare, calcularea sumelor de control, verificarea stării serviciilor din aval, duplicarea solicitărilor și hedging.
Cu atât de multe sarcini de fundal și volume de lucru solicitante pentru CPU, întârzierea de planificare asyncio poate ajunge ușor să domine latența extremă a solicitărilor. Înainte de optimizarea lansării inițiale, urmele solicitărilor cu latență p99 sau mai mare arătau că, deși stocarea din aval răspundea rapid, solicitările se blocau frecvent în așteptarea replanificării corutinei responsabile pentru analizarea răspunsului.
Figura 03 · Monitorizarea întârzierii asyncio
Concurența nu înseamnă paralelism la nivelul CPU-ului
Modulul asyncio din Python permite procesarea concurentă a cererilor, dar pe firul de execuție al CPU-ului se execută o singură cerere la un moment dat. Acest lucru afectează semnificativ latența cererilor atunci când este necesar un volum mare de procesare CPU.
Volum mic de procesare CPU
Etape Python scurte; perioadele de așteptare I/O se suprapunVolum mare de procesare CPU
Etapele Python lungi țin răspunsurile pregătite în așteptarePentru serviciile Python de la OpenAI, am constatat că, pe lângă măsurarea indicatorilor standard de utilizare și saturație pentru memorie, CPU, rețea și disc, este esențial să monitorizăm bucla asyncio și gradul său de ocupare, apoi să ajustăm sistemul în consecință.
Planificând periodic sarcini de fundal și înregistrând diferența dintre momentul estimat și cel efectiv al execuției, putem măsura empiric, în timp real, întârzierea de planificare a buclei de evenimente. La un grad ridicat de utilizare și cu multe sarcini costisitoare, chiar și un număr modest de solicitări simultane per proces produce variații semnificative de planificare, de până la sute de milisecunde și, în unele cazuri-limită, de câteva secunde.
Prin urmare, limităm fiecare proces la un număr mic de cereri simultane și creștem masiv numărul de procese de lucru Python.
La lansarea inițială, profilarea CPU a serviciului activ ne-a dezvăluit una dintre cauzele principale ale întârzierilor asyncio mari și ale latențelor extreme rezultate: analizarea periodică a configurațiilor JSON ale semnalizatoarelor de funcționalități prin Statsig, un instrument pentru gestionarea acestora, testare A/B și altele.
În mod implicit, Statsig verifica la fiecare minut, fără variații ale intervalului, dacă existau configurații actualizate, iar configurația includea toate regulile de producție din toate serviciile. În altă parte, s-a decis la nivel de arhitectură rularea a până la opt procese Python per pod, pentru o utilizare mai mare a procesorului și latențe mai mici. Împreună, acestea făceau ca, în fiecare minut, toate procesele worker ale fiecărui pod să întrerupă la un moment dat procesarea solicitărilor în curs și să folosească procesorul pentru analizarea unui fișier de configurare enorm.
După ce profilarea CPU ne-a ajutat să identificăm cauza, remedierea a fost simplă: am implementat o configurație mai mică și specifică, am mărit intervalul de reîmprospătare și am introdus variații ale intervalului pentru astfel de sarcini de fundal.
Pentru a menține o întârziere asyncio redusă, este esențială și buna echilibrare a solicitărilor între procesele server; fără optimizare, gruparea conexiunilor poate ajunge să producă efectul opus.
În cazul grupării conexiunilor pe partea de client, un singur proces client care lansează multe solicitări simultane poate stabili doar câteva conexiuni la server și, prin urmare, poate trimite întreaga încărcare către doar câteva procese. Înainte să ajustăm echilibrarea încărcării, gradul de utilizare al serviciului varia mult, unele procese extreme deservind de 5–10 ori mai multe solicitări simultane decât media.
Am descoperit acest lucru întâmplător, în timpul unui incident în care, deși opriserăm clientul care supraîncărca o parte a serviciului, un subset de procese a rămas degradat mult după încheierea vârfului de trafic. De fapt, am observat că aceste procese se degradau necontrolat, primind tot mai multe solicitări până când le-am repornit. După supraîncărcarea unui pod, un anumit comportament direcționa și mai mult trafic către acesta. Era o categorie de defecțiuni pe care unii colegi o cunoșteau bine din activitatea anterioară: defecțiunea metastabilă(se deschide într-o fereastră nouă).
Am suspectat grupul de conexiuni și am testat ipoteza limitând durata maximă de reutilizare a unei conexiuni; degradarea a fost într-adevăr limitată, confirmând direcția investigației. Investigațiile ulterioare au arătat că TCPConnector din aiohttp pentru Python reutilizează implicit conexiunile în ordine LIFO: conexiunea returnată cel mai recent este selectată pentru solicitarea următoare. În mod normal, este o opțiune implicită rezonabilă: reutilizarea conexiunilor recente permite conexiunilor suplimentare create pentru vârfurile de trafic să expire după perioada de inactivitate, reducând costurile menținerii lor. În cazul nostru, aceasta a produs o defecțiune metastabilă. În timpul unui vârf, solicitările trimise serverelor mai lente și supraîncărcate returnau conexiunile în grup mai târziu, astfel că erau selectate mai des de solicitările ulterioare, concentrând treptat și mai mult trafic pe podurile care aveau deja probleme. Modificarea grupului pentru reutilizarea FIFO a conexiunilor a întrerupt această buclă de feedback și a redus inclusiv variația solicitărilor în starea stabilă.
Figura 04A · Gruparea conexiunilor pe partea de client
LIFO trimite lucrările noi înapoi la procesul lent
După un vârf de solicitări, serverele mai lente returnează ultimele conexiunile în grup. LIFO favorizează concentrarea unui volum mai mare de procesare pe aceleași servere lente.
Un vârf inițial ajunge la A, B și la procesul C, care este mai lent.
Figura 04B · Gruparea conexiunilor pe partea de client
FIFO întrerupe bucla de feedback a reutilizării conexiunilor
FIFO menține mai multe conexiuni active după un vârf, dar distribuie echitabil volumele de lucru între toate serverele.
Un vârf inițial ajunge la A, B și la procesul C, care este mai lent.
În prezent, ne bazăm în principal pe Istio și Envoy pentru gruparea conexiunilor și strategii mai bune de echilibrare, care țin cont de încărcarea serverelor în infrastructura OpenAI și evită complet această problemă.
Un efect secundar al optimizării pentru întârzieri mici în asyncio și al folosirii unui număr atât de mare de procese Python este că serviciile de care depindem pot fi foarte ușor suprasolicitate de numărul uriaș de conexiuni, fenomen cunoscut drept „efect de avalanșă”.
O implementare zilnică obișnuită, dacă nu este reglată să se desfășoare lent, poate produce fluctuații semnificative ale utilizării CPU din cauza ciclării conexiunilor. Iar o pierdere de conexiuni poate scoate rețeaua din funcțiune prin saturarea gateway-ului NAT. Aceste probleme apar și la alte servicii, dar pragul de declanșare scade semnificativ când există de zece ori mai multe procese, saturând adesea resurse de rețea pe care clienții nu se așteaptă să le gestioneze în stare stabilă dacă iau în calcul doar randamentul.
Ne bazăm pe Envoy și pentru a maximiza concentrarea conexiunilor. Îl folosim pentru a transforma conexiunile HTTP/1 din Python în HTTP/2, profitând de multiplexare, apoi pentru a grupa conexiunile și a le prelungi durata de viață. Envoy ne oferă și un punct central pentru implementarea limitelor de rată și a întrerupătoarelor de circuit, care ar fi mai puțin eficiente în fiecare proces Python autonom.
Figura 05 · Concentrarea conexiunilor
Aceleași solicitări, mai puține conexiuni
Gruparea conexiunilor și multiplexarea conexiunilor HTTP/2 ajută la reducerea numărului de conexiuni pentru serviciile din aval.
Un motiv pentru care am putut scala Python atât de mult a fost API-ul restrâns al Habitat, care menține previzibil costul solicitărilor. În loc să le permită clienților să construiască interogări SQL arbitrare, care ar putea scana tabele mari sau uni numeroase tabele, Habitat oferă un API NoSQL simplu. Lipsa unui API puternic este un compromis asumat explicit în proiectarea Habitat.
Urmărim să optimizăm solicitările simple, previzibile și cu un volum constant de procesare. Din experiența noastră, aceste sisteme sunt mult mai ușor de scalat și greu de configurat greșit sau utilizat necorespunzător. Solicitările cu ramificare imprevizibilă sunt periculoase operațional: complică izolarea și echilibrarea încărcării și produc praguri abrupte de latență, greu de gestionat la scalare atât pentru serviciu, cât și pentru clienți.
Înainte de trecerea la Habitat și Azure Cosmos DB, majoritatea datelor online ale OpenAI erau stocate în Postgres. La acea vreme, puteam examina ușor toate modificările interogărilor și schemei pentru a ne asigura, înainte de implementarea în producție, că funcționau corect și operau asupra datelor indexate. Pe măsură ce echipa și produsele s-au extins, acest proces a devenit rapid imposibil de gestionat și a cauzat frecvent întreruperi, când o singură interogare nouă și costisitoare dintr-un flux intens utilizat scotea baza de date din funcțiune.
Problema este dezechilibrul costurilor: interogările SQL costisitoare și dificil de executat sunt ieftin și ușor de scris. În Habitat evităm acest lucru, iar interogările costisitoare sunt extrem de evidente pentru client. Nu există interogări nelimitate care să poată supraîncărca Habitat, iar uniunile complexe și parcurgerile de grafuri impun echipelor de produs o parte din munca dificilă, ceea ce favorizează în ansamblu proiectări mai eficiente.
Habitat expune un API NoSQL structurat în jurul unor tipuri de obiecte și muchii definite de client, inspirat de TAO(se deschide într-o fereastră nouă). Clienții definesc în prealabil obiectele, muchiile și relațiile dintre ele, dar nu și conținutul fiecărui tip. Relațiile rezultate seamănă cu un graf, însă Habitat nu acceptă interogări obișnuite de parcurgere a grafului, cu excepția interogării muchiilor conectate direct la un anumit obiect.
Partiționăm graful astfel încât fiecare obiect și muchiile sale să fie colocate într-o partiție la nivelul stocării, dar nu depunem eforturi sistematice la nivelul bazei de date pentru a coloca obiectele cu obiectele îndepărtate indicate de muchiile lor. Astfel, modelul se partiționează ușor pentru scalare orizontală, dar parcurgerea grafului este ineficientă, deoarece orice salt între obiecte poate necesita preluarea datelor din două conturi Azure Cosmos DB complet diferite, stocate în regiuni distincte.
Pentru clienții cu nevoi de interogare mai complexe, oferim o vedere secundară offline a datelor din Habitat, accesibilă prin Rockset. Folosim capturarea modificărilor de date (CDC) pentru a transmite modificările din stocarea online către instanțe Rockset izolate, aproape în timp real. Fiecare echipă client este responsabilă de scalarea propriei instanțe Rockset în funcție de nevoile sale de interogare complexă.
Configurarea acestor instanțe Rockset presupune un efort suplimentar pentru clienții noștri, dar considerăm că este compromisul potrivit în acest moment: interogările simple sunt opțiunea implicită, iar cei care au nevoie de interogări complexe au la dispoziție o alternativă. Această arhitectură izolează stocarea online de sarcinile de analiză și căutare care implică un volum mare de citiri.
Amânarea cu un an a rescrierii codului Python ne-a permis să ne concentrăm asupra problemelor mai urgente și cu impact mai mare în perioada de creștere accelerată. Odată cu maturizarea platformei și accelerarea continuă a creșterii, iar serviciul fiind al doilea ca număr de nuclee la OpenAI și al patrulea ca amprentă Envoy, venise în sfârșit momentul să depășim limitele Python. La apogeu, Python ne-a ajutat să procesăm peste 20 de milioane de solicitări pe secundă.
În trimestrul al doilea din 2026, cu doar doi ingineri, Codex și GPT‑5.5, am reușit să rescriem întregul serviciu în Rust. Noul serviciu Rust procesează acum 95% dintre solicitările din producție; în următoarele săptămâni vom retrage complet Python. Datele noastre arată că serviciul Rust este de șase ori mai eficient ca utilizare a procesorului și de 15 ori mai eficient ca utilizare a memoriei decât versiunea Python, având latențe medii și extreme semnificativ mai mici. Intenționăm să prezentăm mai multe concluzii într-un articol viitor.
Serviciul Python, iar acum Rust, este doar una dintre fațetele Habitat. În partea a doua a seriei despre scalarea rapidă a stocării online pentru peste un miliard de utilizatori ChatGPT, vom prezenta stratul de stocare și modul în care Habitat gestionează peste 500 de petabyți și 70 de milioane de solicitări pe secundă.
Dacă vrei să lucrezi la sisteme OLTP la o scară fără precedent și te interesează acest tip de inginerie, consultă acest post disponibil în echipa noastră.


