Почему Postgres с разделёнными storage и compute — правильная база данных для AI-агентов
Десятилетиями базу данных провижинил человек: вы подбирали размер инстанса, ждали, платили за него независимо от того, был он нагружен или нет, и делали это редко и аккуратно. AI-агенты переворачивают каждую часть этого процесса — особенно аккуратность.
Агент, который строит приложение, хочет получить базу данных так же, как файл: создать на месте, тут же на ней итерировать, выбросить, если она оказалась неправильной. И всё это ему нужно делать неаккуратно — без человека, взвешивающего, безопасна ли миграция, обратим ли backfill, стоит ли поднимать ещё одну базу. Вся ценность агента в том, что он действует; база, вокруг которой приходится ходить на цыпочках, тормозит цикл.
Поэтому настоящий вопрос не «как дать агенту базу данных». Он звучит так: как сделать операции с базой настолько дешёвыми и обратимыми, чтобы агент мог свободно создавать их и итерировать на лету и сам исправлять свои ошибки? Это вопрос архитектуры, и ответ — перестать привязывать данные к запущенному процессу.
Это не умозрительные рассуждения. Когда Databricks приобрела Neon в 2025 году, главной цифрой стало то, что более 80% баз данных на платформе создавались AI-агентами, а не людьми — по сравнению с ~30% годом ранее, причём агенты создавали тысячи баз в день. Модель провижининга, рассчитанная на долгоживущие инстансы в человеческом темпе, для такой нагрузки не годится.
Этот пост — архитектурная аргументация: как на самом деле выглядит агентная нагрузка на базы данных, почему очевидные решения проваливаются и как три примитива — branching по принципу копирования при записи за O(1), compute с масштабированием до нуля и ветвление по LSN — чисто ложатся на то, что делают агенты. Сопутствующие посты подробно разбирают два из этих механизмов (масштабирование до нуля, путешествие во времени); этот же — о том, почему это правильные примитивы.
Как выглядит агентная нагрузка на базы данных
У отношения агента к базе данных есть пять свойств, которых нет у человека:
- Многочисленность. Один прогон агента может потребовать сразу несколько баз (на приложение, на эксперимент, на тест). В масштабах платформы это тысячи баз, создаваемых программой на машинной скорости.
- Мгновенность. Агент обращается с базой как с файлом — он рассчитывает создать её и тут же использовать, а не завести тикет и ждать минуты.
- Почти всегда простаивают. Подавляющее большинство — это эксперименты, недостроенные сборки или приложения, ждущие своего первого пользователя. Совокупная утилизация низкая; длинный хвост огромен.
- Требуется изоляция. Ошибка агента в базе одного приложения не должна задеть базу другого. Мультитенантность «по фильтру» недостаточна, когда тенант — это бесконтрольная программа.
- Склонность к ошибкам и необратимость. Агенты выполняют миграции и
UPDATEуверенно и временами катастрофически. Данным нужен undo.
Картинка, которую стоит держать в голове: веер из множества баз данных, почти все они серые.
Почему очевидные решения проваливаются
Один инстанс на приложение. Изоляция корректная, но провижининг медленный (секунды и минуты), и каждый инстанс несёт постоянный пол по затратам — RAM, процесс, storage — вне зависимости от того, подключён к нему кто-то или нет. Умножьте это на простаивающий длинный хвост, и счёт будет в основном за базы, которыми никто не пользуется. На агентном масштабе это нежизнеспособно.
Одна большая общая база данных, изолированная по схемам/RLS. Дёшево и быстро, но это отдаёт ту самую изоляцию, которая вам нужнее всего: плохая миграция или зарвавшийся запрос в схеме одного приложения теперь становятся событием с общим радиусом поражения, а «откатить данные этого приложения» означает хирургически распутывать их из данных всех остальных. Приемлемо, когда в цикле есть человек; неприемлемо, когда актор — автономный агент.
Противоречие реальное: вам нужна изоляция уровня инстанса при стоимости и скорости уровня файла. Это возможно, только если перестать привязывать данные базы к запущенному процессу.
Три примитива, которые подходят
Разделите storage и compute — данные живут в версионируемом page store, а процесс Postgres не имеет состояния и читает страницы по запросу — и из этого выпадают три примитива, каждый из которых отвечает на одно свойство нагрузки:
| Свойство нагрузки | Примитив | Почему подходит |
|---|---|---|
| Мгновенность, многочисленность | Ветка с копированием при записи за O(1) | Новая база данных — это указатель в метаданных, а не копия; создаётся за доли секунды независимо от размера. Агент создаёт её как файл. |
| Почти всегда простаивают | Compute с масштабированием до нуля | Убираем процесс, когда никто не подключён; данные остаются в page store. Простаивающие базы стоят ~ничего. |
| Изоляция, undo, экспериментирование | Ветвление по LSN | Форкаем точное состояние в момент времени, копируя при записи. Дешёвые изолированные копии, мгновенный откат, параллельные попытки. |
1. Ветка с копированием при записи → мгновенность, многочисленность
Создание базы данных — это branching таймлайна: дочерняя ветка разделяет неизменённые страницы родителя и пишет страницу только тогда, когда расходится с ним. Ни один байт не копируется, поэтому создание имеет сложность O(1) по метаданным — одинаково, держит родитель килобайт или сотню гигабайт. Агент может за время, нужное на запись файла, развернуть полностью изолированный Postgres, и сделать это тысячу раз без того, чтобы счёт за storage рос вместе с количеством.
2. Масштабирование до нуля → почти всегда простаивают — значит бесплатно
Поскольку данные не привязаны к процессу, процесс можно удалить. Когда никто не подключён, compute масштабируется до нуля; следующее подключение прозрачно его будит. Простаивающий длинный хвост — большая часть парка — стоит только storage, а не запущенных инстансов. Стоимость становится пропорциональна фактически обслуженным запросам, а не развёрнутым базам. (Как пробуждение делается невидимым — разбор wire-протокола, single-flight wake, парковка соединений — в посте про масштабирование до нуля.)
3. Ветвление по LSN → изоляция, undo, экспериментирование
Тот же примитив ветки, но привязанный к конкретной позиции в WAL, — самый релевантный для агентов. Он даёт вам:
- Обратимость. Помечаем чекпоинт на каждую генерацию; восстановление форкает состояние на этом LSN и направляет приложение туда — код и данные откатываются вместе. Уверенная-но-неправильная миграция агента превращается в undo в один клик. (Подробный разбор — в посте про путешествие во времени.)
- Безопасное экспериментирование. Агент может форкнуть базу, попробовать разрушительную миграцию или backfill данных, проверить и оставить или выбросить ветку — не трогая реальные данные.
- Параллельные попытки. Форкаем по одной ветке на подход из одного и того же сида и запускаем их бок о бок; CoW означает, что N веток не стоят как N копий.
Выигрыш: агент, которому не нужно быть аккуратным
По отдельности это выигрыш в стоимости и несколько фич. Вместе они меняют то, что агенту позволено делать.
Сделайте создание базы операцией за O(1), а её простой — бесплатным, и агент перестаёт экономить базы данных: он поднимает по одной на приложение, на эксперимент, на превью, прямо посреди задачи, без всяких расчётов стоимости или латентности на пути. Сделайте каждое состояние обратимым через ветвление по LSN — и агент перестаёт быть консервативным в отношении мутаций: он может выполнить миграцию, в которой не уверен, сделать backfill таблицы, попытаться провести разрушительный рефакторинг, потому что цена ошибки — выброшенная ветка или откат в один клик, а не инцидент с потерей данных.
Вот что это открывает. Агент, которому приходится быть аккуратным с базой данных, — это агент, который либо стопорится (останавливается, чтобы спросить человека «это безопасно?»), либо ломает то, что не может откатить. Агент на дешёвом, мгновенном, обратимом storage может делать то, в чём агенты действительно хороши — действовать, наблюдать, исправлять — с данными ровно так же, как он уже делает с кодом: создавать на лету, итерировать на месте, форкать, чтобы попробовать второй подход, откатывать тот, что не сработал. Никаких хождений на цыпочках.
Именно это и делает передачу жизненного цикла базы данных агенту безопасной в принципе. Гарантия не в том, что «агент аккуратен», а в том, что «ошибки агента дёшево откатить». Это очень разные ставки, и к автономности масштабируется только вторая.
Как это компонует Adorable
Примитивы — Neon-овские; композиция для мультитенантного билдера приложений — наша:
- Один общий тенант Neon обслуживает всю платформу — единый page store и WAL-сервис. Ветвление дёшево именно потому, что всё разделяет storage.
- Таймлайн на проект — это база данных этого проекта; схемы и роли на каждое приложение изолируют приложения, которые делят ветку проекта.
- Connection-прокси маршрутизирует по имени базы (
proj_{id}), масштабирует compute-Deploymentкаждого проекта 0↔1 и паркует соединения на время пробуждения — машинерия масштабирования до нуля. - Чекпоинты + ветвление по LSN дают каждому проекту путешествие во времени по коду и данным, привязанное к тем же git-коммитам, которые производит агент.
Изоляция живёт на уровне таймлайна + роли + Kubernetes-namespace, а не на уровне тенанта — именно это позволяет тысячам проектных баз дёшево разделять один storage-бэкенд, оставаясь при этом отгороженными друг от друга.
Кульминация — в модели затрат
Проследите за деньгами. При подходе «инстанс на приложение» стоимость ≈ количеству развёрнутых баз данных, навсегда. При разделённом storage + масштабировании до нуля стоимость ≈ фактически обслуженным запросам плюс тонкая линия storage — а копирование при записи держит storage сублинейным по количеству веток. Для парка, который по большей части простаивает и почти полностью создан агентами, это не маленькие различия в постоянном множителе; это разные асимптоты. Это единственная модель, в которой «дать каждому приложению и каждому эксперименту свою настоящую базу данных» экономически вменяемо.
В этом вся ставка, и именно поэтому индустрия сходится здесь: база данных для AI-агентов — это не инстанс поменьше или побыстрее, а тот, в котором данные отвязаны от процесса, так что создание мгновенно, простой бесплатен, а любая точка истории — в одной ветке от вас. Что есть одно и то же, сказанное тремя способами: агент получает возможность обращаться с базой данных как с кодом — создавать на лету, итерировать на месте и откатывать без страха.