23 октября 2026 года OpenAI выложила вторую часть про Habitat. Jon Lee, Hrachya Hakobyan и Chaomin Yu разбирают, как слой хранения на Azure Cosmos DB снова собрали в один общий контейнер и как медиана чтения по миру упала примерно со 120 мс до 10 мс.
Habitat это онлайн-хранилище, через которое ChatGPT, API, Codex и внутренние сервисы достают настройки, состояние и связи между объектами. В этой серии платформа больше 500 ПБ, больше миллиарда пользователей и около 40 регионов. Отвечать она должна за миллисекунды. Первая часть от 11 сентября была про то, как Python-клиент стал отдельным сервисом. На сайте тот текст уже есть: 70 млн запросов в секунду и Rust вместо Python. Здесь база под сервисом.
Общий ключ и слабая партиция
Сначала в каждом аккаунте Cosmos DB жил один контейнер по умолчанию. Нагрузку пускали внутрь и рассчитывали на заявленный потолок: 10 тысяч RU/s на логический ключ партиции. RU/s в Cosmos это единицы запроса в секунду, ими меряют, сколько операций контейнер имеет право обслужить.
Ключ партиции клиенты выбирали сами. Разные продукты часто оказывались на одном и том же user_id или workspace_id. Лимит на ключ общий, и пропускная способность одного пользователя делится между сценариями.
В документации лимит висит на логическом ключе. Режет физическая партиция. До честных 10 тысяч RU/s логический ключ обычно не дотягивает: он делит потолок с теми, кто лежит на том же куске. Узкая продуктовая фича так роняла логин и регистрацию. Один горячий ключ, чужие люди.
Вторая поломка у них называется слабой партицией. Заказанные RU/s контейнер размазывает примерно поровну по физическим партициям. Партиция упёрлась в объём, Cosmos делит её пополам, общий запас запросов при этом не растёт. На схеме до деления 10 тысяч RU/s, после деления по 5 тысяч на каждую из двух новых. Для самых толстых, почти файловых данных рост диска сам снижает потолок, хотя запросов больше не стало.
До Habitat ту же боль они ловили на Postgres. Одна горячая организация клала всю базу. Ключи кэшировали и на промах вешали аренду, чтобы пачка одновременных промахов не затоптала диск.
Отдельные контейнеры и день запуска
Отдельный контейнер на критичный сценарий частично лечит и столкновение ключей, и чужую нагрузку на том же куске: физические партиции свои. Платить приходится заранее посчитанной ёмкостью. Новый контейнер заводили на границе сервиса, не на каждую мелочь. Рядом скрипт, который подтягивает заказанные RU/s к числу физических партиций.
Старые критичные данные остались в общем контейнере, переноса не делали. Чтобы его не добить, новым сервисам и крупным фичам советовали свой контейнер. Аварий на весь сайт стало меньше: падал уже отдельный кусок. Потом накладные расходы догнали.
На общем контейнере скачок сценария с нуля до миллиона запросов в секунду почти не двигает сумму. На отдельном тот же скачок легко становится в тысячу раз выше того, что было, пока сервис гоняли только внутри. Команды выкатывали фичу и забывали поднять контейнер заранее. В день запуска сидели на инциденте и масштабировали в последний момент.
На их схеме это разложено цифрами. Общий контейнер на 100 млн RU/s занят на 80%, свободны 20 млн. Нагрузочный тест на 1 тысячу RU/s это 0,005% запаса, запуск на 1 млн это 5%. Новый контейнер на 10 тысяч RU/s тот же тест занимает на 10%, а запуск требует 10 000% его запаса. Следить за таким контейнером можно, только если знаешь, когда у продукта вырастет трафик. Одной команды, которая считает ёмкость за всех, не хватило. Считали все, и многие считали плохо.
Снова один контейнер
Дальше вместе с Azure они вернулись к одному общему контейнеру и закрыли дыры, из-за которых от него ушли.
Горячие ключи режутся первыми. Один ключ больше не съедает всю физическую партицию.
Пакетные вставки и выгрузки получили более низкий приоритет, чем запросы живых пользователей. Фоновая работа упирается в лимит раньше, чем логин.
Обычный serverless у Cosmos как раз лечил слабые партиции, но делил их только по объёму диска и держал потолок 5 тысяч RU/s на физическую партицию. Тяжёлые по запросам сценарии так не разогнать. Для OpenAI Azure собрала другой serverless: до 10 тысяч RU/s на физическую партицию и деление ещё и по расходу RU, не только по диску. Контейнеры больше не надо караулить и поднимать руками.
После этого, пишут они, месяцами не было инцидентов ни из-за нехватки заранее заказанной ёмкости, ни из-за одного ключа, который забивает соседей. Продуктов при этом стали выпускать быстрее. Свою мультитенантность они называют ранней: большой набор сценариев закрыт средствами Cosmos, отдельную платформу изоляции пока не строили.
Самый крупный аккаунт хранит десятки петабайт и отдаёт больше 20 млн запросов в секунду. По словам OpenAI, Cosmos DB в таком размере больше никто не эксплуатирует.
В конце 2024 отключение электричества ударило по основному региону Azure одного из крупнейших аккаунтов. На переключении выяснилось, что такие аккаунты переезжают на запасной регион несколько часов. Ни OpenAI, ни команда Cosmos этот размер заранее не гоняли.
Примерно через месяц, когда аккаунты возвращали назад, случился второй серьёзный простой. Клиент Cosmos в режиме шлюза разом открыл соединения и послал запросы в исходный регион, хотя обратно переехала только часть физических партиций. Шлюз региона не выдержал лавину подключений.
С тех пор Habitat и Azure смотрят одни и те же слои, от сервиса OpenAI до физических дисков Cosmos. Изменения гоняют на аккаунте размером с прод, но не на проде. SDK правят вместе, друг друга зовут в Teams и могут пинговать напрямую, без очереди поддержки. За год подтянули управление и шлюз, закэшировали служебные данные Cosmos в шлюзе, чтобы не давить служебную партицию, и снизили число соединений с парка Python-процессов Habitat: пул и повторное использование уже открытых. Критичный трафик и фоновый развели по разным VPC.
Чтение со 120 мс до 10 мс
Порядок работ они повторяют из первой части: сначала чтобы работало, потом чтобы было быстро, потом чтобы было дёшево. К 2025 году Habitat уже был критичной инфраструктурой, надёжность стояла первой. Когда система устаканилась, а пользователи разошлись по миру, узким местом стала задержка.
Вычисления и диски часто лежат в разных регионах. Запрос через границу никуда не деть. Пока Habitat сам был медленным, продуктовые команды кэшировали у себя, склеивали запросы, убирали лишние чтения и записи и уносили работу с горячего пути. Так продукты лучше переживали сбой базы. Одновременно они становились сложнее, и выпускать их стали медленнее. Смысл Habitat в обратном: человек, который делает продукт, не обязан разбираться в базе, чтобы быстро выпустить фичу.
В 2026 году взялись за чтение. Это больше 90% запросов к Habitat.
Данные стараются класть рядом с пользователем, когда в регионе хватает места. Отдельные аккаунты Cosmos и так стоят в разных регионах Azure для тех, кому нельзя вывозить данные. Без жёсткого требования по региону так же получается ниже задержка, но датасет большой: многим регионам некуда положить свою долю. Иногда выбор прямой. Либо оставить мощность под диск рядом с людьми, либо отдать её под вычисления, которые отвечают пользователю, и гонять хранилище дальше.
Широко реплицируют только самые маленькие наборы, которым задержка больнее всего. Метаданные авторизации нужны почти на каждый запрос. Часть аккаунтов реплицируется во все 40 регионов Azure, где OpenAI принимает трафик, часть хотя бы в один регион на континент. Чтобы перекладывать наборы между аккаунтами с разной схемой репликации, слой переноса и маршрутизации в Habitat сделали раньше, чем хотели. Места всё равно мало. Так живут единицы наборов, отобранных руками.
Остальное закрывает встроенный кэш. Чтений много, а горячий набор типов маленький: настройки, состояние, свойства. Копия недавно прочитанных объектов и рёбер лежит в Valkey рядом с тем местом, где обрабатывают запрос. В одном регионе кэш и так помогает. Между регионами он ещё прячет дорогу до дальнего диска.
Обычное время жизни записи здесь плохой компромисс. Короткий срок почти не даёт попаданий: запись вылетает, и её снова тащат из базы. Длинный срок повышает попадания и долго отдаёт старое. Явный сброс кэша в момент записи свежее, но его легко сделать криво. Частичный сбой оставляет в кэше прежнюю версию, а в нескольких регионах кэш ещё и разъезжается с репликацией самой базы.
Дальше они пошли путём вроде Meta TAO. Кэш Habitat слушает поток изменений Cosmos DB (change feed) и за секунды разносит сброс по миру. Сервис у них в инфраструктуре читает этот поток и выкидывает локальные копии, когда данные обновились. Следующее чтение промахивается и кладёт в кэш уже новую версию. Попаданий много, окно со старыми данными короткое.
Сам сервис Habitat тоже похудел по задержке: код переписали на Rust, накладные расходы прокси упали. На графике три отметки по порядку. Сначала критичные данные реплицировали в ближние регионы, потом включили кэш в регионе запроса, потом запустили Rust.
Вместе это медиана глобального чтения: примерно 120 мс стало примерно 10 мс. Около 12 раз с тех пор, как в этом году взялись за скорость. Хвост никуда не делся. Самые медленные запросы всё ещё идут в базу за тем, чего нет в кэше, часто через далёкий регион. Когда появится больше мощности и данные станут компактнее, их хотят класть ближе к чтению сразу, а не догонять кэшем.
Поток изменений и хранилище на эксабайты
Тот же захват изменений (CDC) нужен не только кэшу. Миллионы изменений в секунду, тысячи контейнеров, миллионы партиций, больше сотни топиков Kafka. Оттуда кормятся вторичные индексы, которые догоняют с задержкой, перенос данных, офлайн-аналитика и живые продуктовые фичи.
Клиенту не надо самому искать, в каком аккаунте Cosmos лежат его строки, какой воркер в каком регионе их слушает и как масштабировать Kafka. Он регистрирует функцию на изменившиеся строки своего типа. Обвязку держит платформа.
Внутренний контур проверяет, что нужные контейнеры вообще читаются, выбирает регион Cosmos и раздаёт работу тысячам воркеров. Читают с реплик и с более низким приоритетом потока, чтобы фоновый захват не портил задержку живым запросам Habitat. Падения воркеров, битые аренды, кривой конфиг и сбои приёмника на таком парке постоянные. Потоки изолировали друг от друга. Сообщения, которые не доставились насовсем, уходят в отдельную очередь, чтобы одно битое не стопорило остальные. Регион лёг, читатели переключаются на другой регион Cosmos. SDK захвата изменений у Azure тоже поправили: контрольные точки копят в памяти и сбрасывают пачками, а не на каждую строку.
Чтобы тянуть Habitat и хранение переписок на следующий миллиард пользователей, больно реплицировать данные по всей инфраструктуре. Под толстые типы Habitat собирают своё онлайн-хранилище на эксабайты. В основе ядро RocksDB Cloud от Rockset и репликация на Raft. Модель документа та же, что у Cosmos DB. Диск отделён от узла, репликация физическая: флеш-памяти нужно меньше, пик запросов в секунду ниже. Система в разработке, внутри OpenAI.
Для своего Postgres картина знакомая, только цифры другие. Заявленный лимит на ключ не равен лимиту куска, на котором ключ реально лежит. Деление партиции пополам может срезать скорость, пока вы просто копили данные. Отдельная база без заранее посчитанного запаса умирает в день запуска. Общая живёт, если горячий ключ режут первым, а пакетную выгрузку не пускают в одну очередь с логином.