Брзо скалирање на онлајн-складиштето за над 1 милијарда корисници на ChatGPT
Како ја приспособивме нашата платформа за складирање апликациски податоци Habitat, напишана во Python, за да управуваме со невиден раст.
Од Џон Ли, Чаомин Ју и Бен Рис, членови на техничкиот персонал
Секој производ на OpenAI зависи од брз и сигурен пристап до податоци, без разлика дали некој се најавува, ги проверува поставките на Codex или започнува нов разговор во ChatGPT. Секое од овие дејства може да бара многу одделни пребарувања на податоци пред производот да одговори. Ако тие барања се бавни, и производот делува бавно. Ако тие барања не успеат, производот целосно престанува да работи.
Habitat е платформата за онлајн-складирање што ја изградивме за производите на OpenAI брзо и сигурно да пристапуваат до потребните информации. Habitat сега обработува повеќе од 70 милиони барања во секунда и поддржува производи што секоја недела ги користат над 1 милијарда луѓе во речиси 40 географски региони. Habitat првпат беше пуштен за поддршка на GPT на DevDay 2023, како едноставна клиентска библиотека во Python поврзана со една база на податоци. Денес е сложен дистрибуиран систем што опслужува повеќе од 500 петабајти податоци.
Слика 01 · Што е Habitat?
Платформа за онлајн-складирање
Habitat е платформата за онлајн-складирање што ја изградивме за производите на OpenAI брзо и сигурно да пристапуваат до потребните информации.
- Барање
- Одговор
- Промени (CDC)
Изградбата и управувањето со инфраструктура во овој обем не е лесен подвиг, но само по себе не е ни особено тешко. Нашата ситуација беше уникатна поради невидената брзина со која моравме да се скалираме за да го поддржиме огромниот раст на корисниците и побарувачката за производите, додека истовремено градевме зрела платформа. Системските инженери често градат за 10 пати поголем обем и се надеваат дека тоа ќе издржи неколку години додека се подготвуваат за следното зголемување од 10 пати. Во нашиот случај, во последните три години растевме повеќе од 10 пати од година во година. Затоа изградбата и управувањето со Habitat беа низа тактички одлуки и внимателно редоследување: разбирање на секоја компонента до најниско ниво за максимално да го искористиме постојниот технолошки пакет, додека се справувавме со недостигот од капацитет за складирање и пресметување за да добиеме време за темелни инвестиции.
- 70M+
барања во секунда
- 1B+
луѓе секоја недела
- 500 PB+
податоци
Како што растеше OpenAI, мораше да расте и Habitat: прво да стане доволно сигурен за критичниот сообраќај на производите, потоа доволно брз за корисниците ширум светот и, конечно, вешто да работи во огромен обем. Оваа објава е прва од серија во два дела за тоа како го скалиравме онлајн-складирањето. Во оваа објава ќе споделиме како се развиваше Habitat, зошто од библиотека го претворивме во услуга и како услуга напишана на невообичаен јазик за опслужување, Python, ја претворивме во сигурен платформски слој за складирање.
Во идна објава детално ќе објасниме како обезбедивме сигурност на архитектурата со повеќе корисници во голем обем, каква е нашата слоевита стратегија за оптимизирање на читањето и како го проширивме партнерството со Azure Cosmos DB за сигурно справување со невидена побарувачка.
Habitat започна од едноставна идеја: инженерите за производи не треба да размислуваат за управување со бази на податоци. Habitat првпат беше пуштен за поддршка на GPT на DevDay 2023, како мала библиотека во Python што комуницираше со главниот сервер на ChatGPT. Поддржуваше мал број операции што во позадина се пресликуваа на базата на податоци Azure Cosmos DB.
Задачата на библиотеката беше на тимовите за производи да им овозможи едноставно складирање и преземање податоци без да мора да ги совладаат основните технички детали. Habitat ја извршуваше неопходната работа: утврдуваше за какви податоци станува збор, од каде треба да дојдат или каде да одат, дали барањето е дозволено и друго.
Инженерите за производи не мора да се грижат за пребарување на шемата, насочување, овластување, шифрирање, серијализација, обликување на барањата и здружување врски. Не мора ни да размислуваат од каде доаѓаат податоците: Azure Cosmos DB, кеш-мемории или други видови складишта.
Слика 02 · Услугата Habitat
Поедноставен тек на барањата во Habitat
Со издвојување на логиката за складирање во самостојна услуга, воспоставивме единствена контролна точка за поставувања, набљудливост и подобрувања на платформата.
- Барање
- Одговор
Оваа библиотека во Python работеше добро и Habitat брзо се прифати меѓу инженерите за производи во OpenAI, иако немаше координирана централна иницијатива за напуштање на самостојното користење Postgres и Azure Cosmos DB.
Со развојот на потребите на производите, програмерите лесно додаваа во заедничката библиотека поддршка за функции како клиентско кеширање, компресија или шифрирање.
До средината на 2025 година, Habitat ги достигна границите како клиентска имплементација. Со растот на сложеноста на слојот Habitat и бројот на услуги на OpenAI, промените на протоколот со повратна компатибилност станаа неизводливи.
Во еден случај сакавме да го ограничиме опсегот на влијание од прекин во кој било регион врз најкритичните множества податоци, мигрирајќи ги во регионално распределени сметки на Azure Cosmos DB. За оваа промена требаше да внесеме дополнителна логика за насочување во клиентот, исклучена зад функционална ознака, да обезбедиме нејзино поставување кај сите клиенти, а потоа да ја вклучиме ознаката.
Координирањето на поставувањата низ десетици услуги и соработката со секој тим траеја со денови. Пред да го овозможиме тоа, сфативме дека сакаме да воведеме пресликување за да потврдиме дека логиката за делење ќе биде точна. За тоа беа потребни уште неколку дена. Поправка на грешка во нешто за кое сфативме дека е неточно? Уште неколку дена. На крајот бевме подготвени да ја вклучиме ознаката, но еден тим ја врати својата услуга од неповрзани причини на претходна, неисправна верзија на клиентот, предизвикувајќи го токму прекинот што толку се трудевме да го избегнеме.
Промените во клиентската библиотека бараа сложена координација меѓу десетици услуги - процес што стануваше сè покревок, понеефикасен и поподложен на оперативни дефекти. За да го намалиме ова оперативно разгранување при идните поставувања, решивме Habitat да стане посебна услуга.
Со издвојување на логиката за складирање во самостојна услуга, воспоставивме единствена контролна точка за поставувања, можност за набљудување и подобрувања на платформата. Наместо да управуваме со расцепкани ажурирања, можевме централно да воведуваме подобрувања од кои веднаш ќе има корист секој производ на OpenAI.
Централизираната услуга ни дава и единствена контролна точка за најсилните механизми за безбедност и приватност на податоците. Во услугата Habitat можеме централно да ги спроведуваме политиките за контрола на пристапот, да водиме дневници за ревизија и да го ограничуваме пристапот до основните ресурси за складирање како Azure Cosmos DB. Habitat има клучна улога во заштитата на корисничките податоци и спречувањето неовластен пристап од надворешни, внатрешни и агентски актери.
Знаевме дека ни треба услуга, но сè уште не сакавме да се откажеме од Python, и покрај дополнителните трошоци што ги носи како услуга. Користењето Python за услуга со висок капацитет ја зголеми мрежната латентност и донесе значителни трошоци за скалирање на CPU и меморијата во споредба со локалното извршување на библиотеката. Покрај тоа, увидовме дека неефикасноста на Python нема да биде прифатлива при 100 пати поголем обем, па идното препишување беше речиси неизбежно.
Сепак, на ова гледавме како на стратешко преземање технички долг. Тогаш нашата главна цел не беше оптимизација на трошоците или ресурсите, туку да им овозможиме непречена работа на програмерите на производи и да постигнеме стабилност на платформата. Со краткорочно прифаќање на компромисите во перформансите на услугата во Python, можевме да им дадеме предност на понеодложните предизвици, да ги воспоставиме основните API и да изградиме робусна инфраструктура.
Исто така, свесно се обложивме дека брзиот напредок на нашите модели за програмирање ќе го поедностави техничкиот пат во иднина. Се обложивме дека, кога ќе стане неопходна целосна миграција од Python, Codex и GPT ќе ја направат остварлива. На крајот се покажа дека сме биле во право.
Извршувањето на Habitat како услуга во Python не беше оптимално во поглед на перформансите, но беше неопходен избор. Python ни овозможува да напредуваме брзо, но тоа не значи дека можевме да ја занемариме претпазливоста и да прифатиме значително поголема латентност. Кога просечното корисничко барање предизвикува стотици повици кон базата, корисникот го чувствува најбавниот повик. Утврдивме дека главниот предизвик при извршување услуга во Python во овој обем е управувањето со крајните латентности.
Asyncio му помага на Python истовремено да извршува задачи ограничени од влезно-излезни операции, но не го заобиколува Python GIL и не овозможува паралелизам на CPU. Покрај посредувањето барања со многу влезно-излезни операции, Habitat извршува бројни задачи што интензивно го користат CPU и задачи во позадина: насочување, компресија, шифрирање, проверка на контролни суми, проверка на состојбата на зависните услуги, пресликување и осигурување на барањата.
Со толку многу задачи што интензивно го користат CPU и задачи во позадина во услугата, доцнењето во распоредувањето на asyncio лесно може да стане главен фактор во крајната латентност на барањата. Пред приспособувањето за првичното пуштање на услугата, во трагите на барањата со латентност p99 и повисока забележавме дека, иако зависното складиште одговараше брзо, барањата често запираа додека чекаа соодветната корутина повторно да се закаже за да го анализира одговорот.
Слика 03 · Следење на доцнењето на asyncio
Истовременоста не е паралелизам на CPU
Asyncio во Python овозможува истовремена обработка на барања, но во даден момент на нишката на CPU се извршува само едно барање. Ова силно влијае врз латентноста на барањата кога има многу работа за CPU.
Мало оптоварување на CPU
Кратки чекори на Python; чекањата за В/И се преклопуваатГолемо оптоварување на CPU
Долгите чекори на Python ги задржуваат подготвените одговориКај услугите во Python во OpenAI, покрај стандардните показатели за искористеност и заситеност на меморијата, CPU, мрежата и дискот, од клучно значење е да се следи и циклусот asyncio и неговата оптовареност, а потоа соодветно да се приспособи.
Со периодично закажување задачи во позадина и бележење на разликата меѓу очекуваното и вистинското време на извршување, емпириски го мериме доцнењето во распоредувањето на циклусот на настани во реално време. При висока искористеност и многу скапи задачи, дури и умерен број истовремени барања по процес е доволен да предизвика значителни отстапувања во распоредувањето - до стотици милисекунди, а во некои гранични случаи и неколку секунди.
Затоа секој процес опслужува само мал број истовремени барања, а наместо тоа масовно го зголемуваме бројот на работни процеси во Python.
При првичното пуштање на услугата, со профилирање на CPU во продукциска средина откривме една од основните причини за големото доцнење на asyncio, а со тоа и за високите крајни латентности: периодичното анализирање JSON на конфигурациите на функционалните ознаки преку Statsig - алатка за управување функционални ознаки, A/B-тестови и друго.
Стандардно, Statsig беше поставен да проверува освежени конфигурации секоја минута, без временско отстапување, а конфигурацијата ги содржеше сите продукциски правила од сите услуги. Одделно беше донесена архитектонска одлука да се извршуваат до 8 процеси во Python по pod за поголема искористеност на CPU и помала латентност. Во комбинација, ова значеше дека секоја минута во секој pod ќе има момент кога сите работни процеси ќе ја запрат обработката на тековните барања и ќе ги трошат циклусите на CPU анализирајќи огромна конфигурациска датотека.
Откако профилирањето на CPU ни помогна да ја најдеме основната причина, решението беше едноставно: да поставиме помала, насочена конфигурација, да го продолжиме интервалот за освежување и да додадеме временско отстапување кај ваквите задачи во позадина.
За да се одржи мало доцнење на asyncio, клучно е и добро да се балансираат барањата меѓу серверските процеси; без соодветно приспособување, здружувањето врски може да го попречи тоа.
Со клиентско здружување врски, еден клиентски процес што испраќа многу истовремени барања може да воспостави само неколку серверски врски и така целото оптоварување да го насочи кон само неколку процеси. Пред да го приспособиме балансирањето на оптоварувањето, искористеноста на услугата многу варираше, при што некои крајни процеси опслужуваа 5–10 пати повеќе истовремени барања од просекот.
Ова случајно го откривме при инцидент кога, иако го запревме клиентот што преоптоваруваше дел од услугата, дел од процесите останаа во влошена состојба долго по наглиот сообраќај. Всушност, забележавме дека состојбата на тие процеси неконтролирано се влошуваше и добиваа сè повеќе барања додека не ги рестартиравме. Штом еден pod ќе се преоптовареше, некаков механизам насочуваше уште повеќе сообраќај кон него. Тоа беше вид дефект што некои колеги добро го познаваа од претходната работа: метастабилен дефект(се отвора во нов прозорец).
Се посомневавме дека причината е групата врски и го проверивме тоа со ограничување на максималното времетраење на повторната употреба на врските. Тоа навистина го ограничи влошувањето и потврди дека истрагата се движи во вистинската насока. Понатамошната истрага покажа дека TCPConnector од aiohttp во Python стандардно користи LIFO за повторна употреба на врските: за следното барање се избира најскоро вратената врска. Обично тоа е разумна стандардна поставка: повторната употреба на неодамнешните врски им овозможува на дополнителните врски создадени за нагол сообраќај да истечат поради неактивност, со што се намалува трошокот за нивно одржување. Во нашиот случај, тоа создаде метастабилен дефект. При нагол прилив, барањата до побавните преоптоварени сервери подоцна ги враќаа врските во групата, па следните барања почесто ги избираа токму нив и постепено насочуваа уште повеќе сообраќај кон pods што веќе имаа тешкотии. Со измена на групата врски за повторна употреба според FIFO ја прекинавме оваа повратна јамка и ја намаливме варијансата на барањата во стабилна состојба.
Слика 04A · Клиентско здружување врски
LIFO ја враќа новата работа кон бавниот процес
По нагол прилив на барања, побавните сервери последни ги враќаат врските во групата. LIFO предизвикува уште повеќе работа да се концентрира на истите побавни сервери.
Почетниот нагол прилив стигнува до A, B и побавниот процес C.
Слика 04B · Клиентско здружување врски
FIFO ја прекинува повратната јамка на повторната употреба на врските
По нагол прилив, FIFO одржува повеќе активни врски, но правично го балансира оптоварувањето меѓу сите сервери.
Почетниот нагол прилив стигнува до A, B и побавниот процес C.
Денес главно се потпираме на Istio и Envoy за здружување врски и подобри стратегии за балансирање што го земаат предвид оптоварувањето на серверите низ инфраструктурата на OpenAI, со што целосно го избегнуваме овој проблем.
Несакан ефект од приспособувањето за мало доцнење на asyncio и големиот број процеси во Python е тоа што зависните системи многу лесно се преоптоваруваат со огромен број врски - појава позната како „стампедо од барања“.
Вообичаено дневно поставување, ако не е приспособено да се одвива бавно, може да предизвика значителни флуктуации на CPU поради постојаното обновување на врските. Или, истекување на врски може да ја собори мрежата со заситување на NAT-портата. Овие проблеми не се невообичаени ни кај други услуги, но прагот за нивно активирање значително се намалува кога има десетпати повеќе процеси. Често се заситуваат мрежните ресурси за кои клиентите, врз основа само на капацитетот, не очекуваат дека ќе треба да ги поддржуваат во стабилна состојба.
Се потпираме и на Envoy за максимално консолидирање на врските. Го користиме за надградба на HTTP/1-врските на Python во HTTP/2 за да го искористиме мултиплексирањето, а потоа ги здружуваме тие врски и го продолжуваме нивниот животен век. Envoy ни дава и централно место за воведување ограничувања на стапката и прекинувачи на колото, кои би биле помалку ефикасни во секој самостоен процес во Python.
Слика 05 · Консолидирање на врските
Истите барања, помалку врски
Здружувањето врски и мултиплексирањето HTTP/2 помагаат да се намали оптоварувањето со врски кај зависните услуги.
Една од причините што можевме толку многу да го скалираме Python беше ограниченото API на Habitat, кое го одржува трошокот на барањата предвидлив. Наместо да им дозволи на клиентите да создаваат произволни барања во SQL што може да предизвикаат скенирање големи табели или спојување многу табели, Habitat нуди едноставно NoSQL API. Отсуството на моќно API е свесен компромис во дизајнот на Habitat.
Настојуваме да оптимизираме за едноставни, предвидливи барања со постојан обем на работа. Според нашето искуство, ваквите системи многу полесно се скалираат и тешко е да се конфигурираат или користат погрешно. Барањата со непредвидливо разгранување се оперативно опасни: ја усложнуваат изолацијата и балансирањето на оптоварувањето и создаваат нагли скокови на латентноста што тешко се скалираат и за услугата и за нејзините клиенти.
Пред да преминеме на Habitat и Azure Cosmos DB, повеќето онлајн-податоци на OpenAI беа складирани во Postgres. Тогаш беше лесно да се прегледаат сите промени на барањата и шемата, за пред објавувањето во продукција да се потврди дека работат правилно и користат индексирани податоци. Со растот на тимот и производите, ова брзо стана неодржливо и често предизвикуваше прекини, при што едно ново и скапо барање на критична патека ја соборуваше базата.
Проблемот е во нерамнотежата на трошоците: евтино и лесно е да се напишат барања во SQL што се скапи и тешки за извршување. Во Habitat го избегнуваме ова и правиме скапите барања да бидат веднаш очигледни кај клиентот. Нема неограничени барања што може да го преоптоварат Habitat, а сложените спојувања и минувања низ графа бараат дел од тешката работа да ја извршат тимовите за производи, што придонесува кон поефикасни дизајни.
Habitat нуди NoSQL API моделирано околу типови објекти и врски што ги дефинира клиентот, инспирирано од TAO(се отвора во нов прозорец). Клиентите однапред ги дефинираат објектите и врските и нивните меѓусебни односи, но не и содржината на секој тип. Добиените односи наликуваат на граф, но самиот Habitat не поддржува вообичаени барања за минување низ графата, освен пребарување на директните врски на одреден објект.
Ја делиме графата така што секој објект и неговите врски се сместени заедно во партиција на ниво на складиштето, но на ниво на базата не се обидуваме систематски да ги сместиме заедно објектите и оддалечените објекти кон кои водат нивните врски. Како резултат, моделот лесно се дели за хоризонтално скалирање, но минувањето низ графата е неефикасно бидејќи секој премин меѓу објекти може да бара преземање од две сосема различни сметки на Azure Cosmos DB, складирани во различни региони.
За клиентите со посложени потреби за пребарување обезбедуваме секундарен офлајн приказ на Habitat преку Rockset. Користиме снимање промени на податоците (CDC) за да ги пренесуваме промените од онлајн-складиштето кон изолирани примероци на Rockset речиси во реално време. Секој тим на клиент е одговорен да го скалира сопствениот примерок на Rockset за своите сложени пребарувања.
Обезбедувањето Rockset внесува дополнителна сложеност за клиентите, но сметаме дека во моментов тоа е вистинскиот компромис: едноставните барања се стандардни, а на оние на кои им требаат сложени им се нуди алтернатива. Овој дизајн го изолира нашето онлајн-складиште од аналитички и пребарувачки задачи со многу читања.
Одложувањето на препишувањето од Python за една година ни овозможи при хиперрастот да се сосредоточиме на поитни и повлијателни предизвици. Со созревањето на платформата и понатамошното забрзување на растот, а бидејќи бевме втора најголема услуга според бројот на јадра во OpenAI и четврта според опфатот на Envoy, конечно дојде време да го надминеме Python. На својот врв, Python ни помогна да опслужуваме повеќе од 20 милиони барања во секунда.
Во вториот квартал од 2026 година, со само 2 инженери, Codex и GPT‑5.5, успеавме целосно да ја препишеме услугата во Rust. Новата услуга во Rust сега обработува 95% од нашите продукциски барања; во следните недели целосно ќе го повлечеме Python. Нашите податоци покажуваат дека услугата во Rust е 6 пати поефикасна со CPU и 15 пати поефикасна со меморијата од верзијата во Python, со значително пониски просечни и крајни латентности. Планираме да споделиме повеќе сознанија во идна блог-објава.
Услугата во Python, а сега и во Rust, е само еден аспект на Habitat. Во вториот дел од серијата за тоа како брзо го скалиравме онлајн-складиштето за повеќе од 1 милијарда корисници на ChatGPT, ќе зборуваме за слојот за складирање и како Habitat опслужува повеќе од 500 петабајти и над 70 милиони барања во секунда.
Ако сакате да работите на OLTP-системи во граничен обем и ве интересира вакво инженерство, погледнете ја оваа отворена позиција во нашиот тим.


