Стрімке масштабування сховища для понад 1 мільярда користувачів ChatGPT
Як ми адаптували платформу зберігання застосунків Habitat на Python до безпрецедентного зростання.
Автори: Джон Лі, Чаомінь Ю та Бен Ріс, технічні фахівці
Кожен продукт OpenAI залежить від швидкого й надійного доступу до даних — коли користувач входить у систему, перевіряє налаштування Codex або починає нову розмову в ChatGPT. Перш ніж продукт зможе відповісти, кожна з цих дій може потребувати багатьох окремих пошуків даних. Якщо ці запити повільні, продукт здається повільним. Якщо ці запити завершуються помилкою, продукт узагалі перестає працювати.
Habitat — створена нами платформа оперативного зберігання, що забезпечує продуктам OpenAI швидкий і надійний доступ до потрібної інформації. Нині Habitat обробляє понад 70 мільйонів запитів щосекунди й підтримує продукти, якими щотижня користується понад мільярд людей майже в 40 географічних регіонах. Habitat уперше запустили для підтримки GPT на DevDay 2023 як просту клієнтську бібліотеку Python, підключену до однієї бази даних. Сьогодні це складна розподілена система, що обслуговує понад 500 петабайтів даних.
Рисунок 01 · Що таке Habitat?
Платформа оперативного зберігання
Habitat — створена нами платформа оперативного зберігання, що забезпечує продуктам OpenAI швидкий і надійний доступ до потрібної інформації.
- Запит
- Відповідь
- Зміни (CDC)
Створювати й експлуатувати інфраструктуру такого масштабу непросто, але й не надзвичайно складно. Унікальною нашу ситуацію зробила безпрецедентна швидкість масштабування: нам довелося підтримувати разюче зростання кількості користувачів і попиту на продукти, водночас розбудовуючи зрілу платформу. Системні інженери часто проєктують із десятикратним запасом масштабу й сподіваються, що його вистачить на кілька років підготовки до наступного десятикратного зростання. У нашому випадку протягом останніх трьох років ми щороку зростали більш ніж удесятеро. Тому створення й експлуатація Habitat перетворилися на низку тактичних рішень і ретельно визначених кроків: ми вивчали кожен компонент до найнижчого рівня, вичавлювали максимум із наявного стека й долали дефіцит ресурсів зберігання та обчислень, виграючи час для фундаментальних інвестицій.
- 70 млн+
запитів за секунду
- 1 млрд+
людей щотижня
- 500 ПБ+
даних
Зі зростанням 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 добре працювала, і продуктові інженери OpenAI швидко почали використовувати Habitat, хоча централізованої кампанії з відмови від самостійного використання Postgres і Azure Cosmos DB не було.
З розвитком потреб продуктів розробники також могли легко додавати до спільної бібліотеки підтримку клієнтського кешування, стиснення чи шифрування.
До середини 2025 року Habitat досяг межі можливостей клієнтської реалізації. У міру ускладнення рівня Habitat і збільшення кількості служб OpenAI зміни протоколу зі зворотною сумісністю стали нездійсненними.
В одному випадку ми хотіли зменшити зону впливу збою окремого регіону на найкритичніші набори даних, перенісши їх до низки регіонально розподілених облікових записів Azure Cosmos DB. Для цього потрібно було додати в клієнт логіку маршрутизації, вимкнену прапорцем функції, розгорнути її для всіх клієнтів, а потім увімкнути прапорець.
Координація розгортання в десятках служб і робота з кожною командою тривали кілька днів. Перш ніж це ввімкнути, ми вирішили додати тіньове дублювання, щоб перевірити правильність логіки шардингу. На його розгортання пішло ще кілька днів. А виправлення виявленої помилки? Ще кілька днів. Зрештою ми були готові ввімкнути прапорець, але одна з команд з інших причин відкотила свою службу до ранішої версії клієнта з помилкою, спричинивши саме той збій, якого ми так намагалися уникнути.
Зміни клієнтської бібліотеки вимагали складної координації десятків служб, і цей процес ставав дедалі крихкішим, неефективнішим та вразливішим до операційних збоїв. Щоб зменшити операційне розгалуження майбутніх розгортань, ми вирішили винести Habitat в окрему службу.
Відокремивши логіку зберігання в самостійну службу, ми створили єдину точку керування розгортанням, спостережуваністю та вдосконаленнями платформи. Замість керування розрізненими оновленнями ми могли централізовано впроваджувати вдосконалення, одразу приносячи користь кожному продукту OpenAI.
Централізована служба також дає нам єдину контрольну точку для найнадійніших механізмів безпеки й конфіденційності даних. У службі Habitat ми можемо централізовано застосовувати політики контролю доступу, вести журнал аудиту й обмежувати доступ до базових ресурсів зберігання, як-от Azure Cosmos DB. Habitat відіграє критичну роль у захисті даних користувачів і запобіганні несанкціонованому доступу з боку зовнішніх, внутрішніх суб’єктів та агентів.
Ми розуміли, що нам потрібна служба, але ще не хотіли відмовлятися від Python, попри додаткові накладні витрати його використання як служби. Використання Python для служби з високою пропускною здатністю збільшило мережеву затримку та суттєво підвищило витрати на масштабування процесорних і ресурсів пам’яті порівняно з локальним виконанням бібліотеки. Крім того, ми розуміли, що неефективність Python буде неприйнятною за стократного масштабу, тож майбутнє переписування було майже неминучим.
Однак ми розглядали це як стратегічне прийняття технічного боргу. Тоді нашою головною метою була не оптимізація витрат чи ресурсів, а усунення перешкод для розробників продуктів і забезпечення стабільності платформи. Тимчасово погодившись на компроміси в продуктивності служби Python, ми змогли зосередитися на нагальніших проблемах, сформувати основні API та створити надійну інфраструктуру.
Ми також свідомо зробили ставку на те, що стрімкий розвиток наших моделей для програмування згодом спростить технічний шлях. Ми розраховували, що коли настане час повністю відмовитися від Python, Codex і GPT зроблять таку міграцію здійсненною. Зрештою ця ставка зіграла.
З погляду продуктивності запуск Habitat як служби Python був неоптимальним, але необхідним вибором. Python дає нам змогу рухатися швидко, але це не означає, що ми могли забути про обережність і погодитися на відчутно більші затримки. Коли середній запит користувача спричиняє сотні звернень до бази даних, користувач відчуває затримку найповільнішого з них. Ми з’ясували, що головна складність роботи служби Python у такому масштабі — керування хвостовими затримками.
Asyncio допомагає Python паралельно виконувати завдання, обмежені введенням-виведенням, але не дає змоги обійти GIL Python і забезпечити паралельне виконання на процесорі. Окрім проксування запитів із великою часткою введення-виведення, Habitat виконує багато ресурсомістких для процесора обов’язків і фонових завдань: маршрутизацію, стиснення, шифрування, обчислення контрольних сум, перевірку стану нижчих служб, тіньове дублювання та хеджування запитів.
За такої кількості ресурсомістких і фонових завдань затримка планування asyncio легко може стати головною складовою хвостової затримки запитів. До оптимізації перед першим запуском служби трасування запитів із затримкою p99 і вище показувало: хоча нижче сховище відповідало швидко, запити часто зависали в очікуванні повторного планування відповідної корутини для розбору відповіді.
Рисунок 03 · Відстеження затримки asyncio
Конкурентність — не паралельне виконання на процесорі
Asyncio в Python дає змогу конкурентно обробляти запити, але в кожен момент у потоці процесора виконується лише один запит. Це суттєво впливає на затримку запитів, коли потрібно виконати багато роботи на процесорі.
Мале навантаження на процесор
Короткі етапи Python; очікування введення-виведення перекриваютьсяВелике навантаження на процесор
Через довгі етапи Python готові відповіді чекаютьДля служб Python в OpenAI, крім стандартних показників використання та насичення пам’яті, процесора, мережі й диска, критично важливо стежити за циклом asyncio та його завантаженням і відповідно налаштовувати систему.
Періодично плануючи фонові завдання та фіксуючи різницю між очікуваним і фактичним часом виконання, ми можемо емпірично вимірювати затримку планування циклу подій у реальному часі. За високого завантаження й великої кількості дорогих завдань навіть помірної кількості одночасних запитів на процес достатньо для значних коливань планування — до сотень мілісекунд, а в окремих випадках і кількох секунд.
Тому кожен процес обслуговує лише невелику кількість одночасних запитів, натомість ми значно масштабуємо кількість робочих процесів Python.
Під час першого запуску служби профілювання процесора в робочому середовищі виявило одну з першопричин великої затримки asyncio, а отже й хвостових затримок: періодичний розбір JSON-конфігурацій прапорців функцій через Statsig — інструмент керування прапорцями, проведення A/B-тестів тощо.
За замовчуванням Statsig щохвилини й без випадкового зсуву опитував оновлені конфігурації, які містили всі робочі правила всіх служб. Крім того, було ухвалено архітектурне рішення запускати до восьми процесів Python на pod, щоб краще завантажити процесор і зменшити затримки. У результаті щохвилини в кожному pod наставав момент, коли всі робочі процеси припиняли обробляти поточні запити й витрачали процесорний час на розбір величезного файла конфігурації.
Коли профілювання процесора допомогло встановити першопричину, виправлення було простим: розгорнути меншу цільову конфігурацію, збільшити інтервал оновлення й додати випадковий зсув до таких фонових завдань.
Щоб затримка asyncio залишалася низькою, критично важливо рівномірно розподіляти запити між серверними процесами; без налаштування пул з’єднань також може цьому заважати.
За клієнтського пулу з’єднань один клієнтський процес із багатьма одночасними запитами може створити лише кілька з’єднань із серверами й унаслідок цього спрямувати все навантаження лише на кілька процесів. До зміни балансування навантаження використання ресурсів у нашій службі значно різнилося: деякі хвостові процеси обслуговували у 5–10 разів більше одночасних запитів, ніж у середньому.
Ми випадково виявили це під час інциденту: навіть після зупинки клієнта, який перевантажував частину служби, деякі процеси залишалися в погіршеному стані ще довго після сплеску трафіку. Насправді стан цих процесів неконтрольовано погіршувався: вони отримували дедалі більше запитів, аж поки ми їх не перезапустили. Щойно pod перевантажувався, певний механізм закріплював на ньому ще більше трафіку. Деякі наші колеги добре знали цей клас відмов із попередньої роботи: метастабільна відмова(відкривається у новому вікні).
Ми запідозрили пул з’єднань і перевірили гіпотезу, обмеживши максимальну тривалість повторного використання з’єднання. Це справді обмежило погіршення й підтвердило правильність напряму розслідування. Подальше розслідування показало, що TCPConnector бібліотеки aiohttp у Python за замовчуванням повторно використовує з’єднання за принципом LIFO: для наступного запиту вибирається з’єднання, повернене останнім. Зазвичай це розумне налаштування: повторне використання недавніх з’єднань дає змогу додатковим з’єднанням, створеним для сплеску трафіку, завершитися за тайм-аутом бездіяльності й зменшує витрати на їх підтримку. Але в нашому випадку це спричинило метастабільну відмову. Під час сплеску запитів повільніші перевантажені сервери пізніше повертали з’єднання до пулу, тому наступні запити частіше вибирали саме їх, поступово зосереджуючи ще більше трафіку на pod, які вже мали проблеми. Перехід пулу з’єднань на повторне використання FIFO розірвав цей цикл зворотного зв’язку й навіть зменшив розкид кількості запитів у сталому стані.
Рисунок 04A · Пул з’єднань на боці клієнта
LIFO повертає нову роботу повільному процесу
Після сплеску запитів повільніші сервери повертають з’єднання до пулу останніми. Через LIFO більше роботи зосереджується на тих самих повільніших серверах.
Початковий сплеск досягає A, B і повільнішого процесу C.
Рисунок 04B · Пул з’єднань на боці клієнта
FIFO розриває цикл зворотного зв’язку повторного використання з’єднань
Після сплеску FIFO зберігає більше активних з’єднань, але справедливо розподіляє навантаження між усіма серверами.
Початковий сплеск досягає A, B і повільнішого процесу C.
Сьогодні в інфраструктурі OpenAI ми переважно покладаємося на Istio й Envoy, які забезпечують пули з’єднань і кращі стратегії балансування з урахуванням навантаження серверів, цілком усуваючи цю проблему.
Побічний ефект оптимізації для низької затримки asyncio й великої кількості процесів Python — нижчі залежності дуже легко перевантажити величезною кількістю з’єднань. Це явище називають «стадним ефектом».
Звичайне щоденне розгортання, якщо не сповільнити його належним налаштуванням, може спричинити значні коливання навантаження на процесор через оновлення з’єднань. А витік з’єднань може вивести мережу з ладу, наситивши шлюз NAT. Такі проблеми трапляються й в інших службах, але на порядок більша кількість процесів суттєво знижує поріг їх виникнення, часто насичуючи мережеві ресурси, які клієнти не очікують підтримувати в сталому стані, якщо орієнтуються лише на пропускну здатність.
Ми також використовуємо Envoy, щоб максимально консолідувати з’єднання. За його допомогою ми перетворюємо з’єднання HTTP/1 Python на HTTP/2, користуємося мультиплексуванням, а потім об’єднуємо ці з’єднання в пул і подовжуємо термін їх дії. Envoy також дає нам централізоване місце для реалізації обмежень частоти й автоматичних запобіжників, які були б менш ефективними в кожному окремому процесі Python.
Рисунок 05 · Консолідація з’єднань
Ті самі запити, менше з’єднань
Пули з’єднань і мультиплексування з’єднань HTTP/2 допомагають зменшити навантаження з’єднаннями на нижчі служби.
Однією з причин, чому нам вдалося так масштабувати Python, став обмежений API Habitat, завдяки якому вартість запитів залишається передбачуваною. Замість можливості створювати довільні SQL-запити, що можуть спричинити сканування великих таблиць або об’єднання багатьох таблиць, Habitat надає простий API NoSQL. Відсутність потужного API — свідомий компроміс у дизайні Habitat.
Ми оптимізуємо систему для простих і передбачуваних запитів зі сталою кількістю роботи. З нашого досвіду, такі системи значно легше масштабувати, а помилитися чи неправильно скористатися ними складно. Запити з непередбачуваним розгалуженням операційно небезпечні: вони ускладнюють ізоляцію та балансування навантаження й створюють різкі стрибки затримки, до яких важко масштабувати і службу, і її клієнтів.
До переходу на Habitat і Azure Cosmos DB більшість оперативних даних OpenAI зберігалася в Postgres. Тоді було легко перевіряти всі зміни запитів і схем, щоб до випуску у виробниче середовище переконатися в їхній коректності та роботі з індексованими даними. Зі зростанням команди й продуктів це швидко стало некерованим і часто спричиняло збої: один новий дорогий запит на критичному шляху міг вивести базу даних із ладу.
Проблема полягає в дисбалансі витрат: написати SQL-запит, який дорого й складно виконувати, дешево та легко. У Habitat ми цього уникаємо й робимо дорогі запити вкрай помітними на боці клієнта. Тут немає необмежених запитів, здатних перевантажити Habitat, а складні об’єднання та обходи графів вимагають від продуктових команд виконувати частину важкої роботи, що загалом сприяє ефективнішому проєктуванню.
Habitat надає API NoSQL, побудований навколо визначених клієнтом типів об’єктів і ребер за зразком TAO(відкривається у новому вікні). Клієнти заздалегідь визначають об’єкти, ребра та зв’язки між ними, але не вміст кожного типу. Утворені зв’язки нагадують граф, але сам Habitat не підтримує типові запити обходу графа, крім запитів прямих ребер певного об’єкта.
Ми розділяємо цей граф так, щоб кожен об’єкт і відповідні ребра містилися разом в одному розділі сховища, але на рівні бази даних не намагаємося спеціально розміщувати поруч об’єкти й віддалені об’єкти, на які вказують їхні ребра. Завдяки цьому модель легко розділяється для горизонтального масштабування, але обходи графа неефективні, адже кожен перехід між об’єктами може вимагати отримання даних із двох різних облікових записів Azure Cosmos DB у різних регіонах.
Клієнтам зі складнішими потребами в запитах ми надаємо автономне вторинне представлення Habitat через Rockset. За допомогою фіксації змін даних (CDC) ми майже в реальному часі передаємо зміни з оперативного сховища до ізольованих екземплярів Rockset. Кожна клієнтська команда самостійно масштабує свій екземпляр Rockset для складних запитів.
Розгортання Rockset створює додаткові незручності для клієнтів, але нині ми вважаємо цей компроміс правильним: прості запити використовуються за замовчуванням, а для складних залишається запасний шлях. Така архітектура ізолює наше оперативне сховище від аналітичних і пошукових навантажень з інтенсивним читанням.
Відклавши переписування Python на рік, у період надстрімкого зростання ми змогли зосередитися на нагальніших і важливіших завданнях. Коли платформа подорослішала, а зростання й далі прискорювалося, настав час відмовитися від Python: за кількістю ядер ця служба вже була другою в OpenAI, а за масштабом Envoy — четвертою. На піку Python допомагав нам обслуговувати понад 20 мільйонів запитів щосекунди.
У другому кварталі 2026 року лише двоє інженерів за допомогою Codex і GPT‑5.5 змогли повністю переписати службу на Rust. Нова служба Rust уже обробляє 95% наших робочих запитів; у найближчі тижні ми повністю виведемо Python з експлуатації. За нашими даними, служба Rust у шість разів ефективніше використовує процесор і в 15 разів — пам’ять, ніж версія Python, а її середні та хвостові затримки значно нижчі. У майбутній статті ми плануємо поділитися додатковими висновками.
Служба на Python, а тепер на Rust, — лише одна зі складових Habitat. У другій частині цієї серії про стрімке масштабування оперативного сховища для понад мільярда користувачів ChatGPT ми розповімо про рівень зберігання та про те, як Habitat обслуговує понад 500 петабайтів і 70 мільйонів запитів щосекунди.
Якщо ви хочете працювати над системами OLTP у передовому масштабі й цікавитеся такою інженерією, перегляньте відкриту вакансію в нашій команді.


