Реактивная база данных
Наш реактивный BaaS на native SpacetimeDB — ~200k realtime-доставок/с, p95<20мс.
Реактивная база данных
Задача
Хотели удобство реактивного бэкенда — live-запросы, типизированный клиент и сервер — но однопоточный Node-рантайм упирается в ~25-30k realtime-доставок/с, и не хотели зависеть от арендованного бэкенда.
Что делали
Наш собственный реактивный BaaS с native SpacetimeDB как транзакционным/realtime-движком внутри: компилятор превращает нашу схему и функции в STDB-модуль, слой совместимости держит тот же клиентский и серверный API, который приложения уже используют (идеально — тот же клиент, другой URL), а worker ведёт actions и внешний I/O (Stripe, email, LLM, вебхуки); поверхность доказанно не имеет продовой зависимости от эталонной реализации, которая остаётся только оракулом для дифф-тестов.
Результат
Существующие приложения переезжают почти сменой URL, на движок, держащий ~200k realtime-доставок/с при p95<20мс — примерно 7x на бокс против старого рантайма, без потолка Node.
Dev-story статья
Реактивная база данных: как создавался проект
Каждое наше приложение хочет от бэкенда одного: live-запросов, типизированного клиента и серверных функций, без арендованного продукта посередине. У нас уже был реактивный BaaS на одном Node-потоке, и он работал — пока цифры fanout не перестали быть комфортными. Это та же реализация продукта заново, с native SpacetimeDB внутри, по одному жёсткому правилу: приложения не должны меняться.
Разделы
05
Модули
05
Стек
Rust + SpacetimeDB
Почему появился проект
Хотели удобство реактивного бэкенда — live-запросы, типизированный клиент и сервер — но однопоточный Node-рантайм упирается в ~25-30k realtime-доставок/с, и не хотели зависеть от арендованного бэкенда.
Каждое наше приложение хочет от бэкенда одного: live-запросов, типизированного клиента и серверных функций, без арендованного продукта посередине. У нас уже был реактивный BaaS на одном Node-потоке, и он работал — пока цифры fanout не перестали быть комфортными. Это та же реализация продукта заново, с native SpacetimeDB внутри, по одному жёсткому правилу: приложения не должны меняться.
Что было создано
Наш собственный реактивный BaaS с native SpacetimeDB как транзакционным/realtime-движком внутри: компилятор превращает нашу схему и функции в STDB-модуль, слой совместимости держит тот же клиентский и серверный API, который приложения уже используют (идеально — тот же клиент, другой URL), а worker ведёт actions и внешний I/O (Stripe, email, LLM, вебхуки); поверхность доказанно не имеет продовой зависимости от эталонной реализации, которая остаётся только оракулом для дифф-тестов.
Компилятор превращает нашу схему и функции в native SpacetimeDB-модуль (Rust), так что транзакции и реальное время крутятся на движке, сделанном именно для этого. Слой совместимости заново реализует тот же клиентский и серверный API, который приложения уже знают — идеал это та же клиентская библиотека, направленная на другой URL. Worker ведёт actions и внешний I/O (Stripe, email, LLM, вебхуки) вне транзакционного ядра, а дашборд показывает схему, данные и логи. Старый Node-стек остаётся рабочим фолбэком до доказанного cutover.
Основные модули и путь пользователя
Причина переписывания это число: старый Node-рантайм упирается в ~25-30k realtime-доставок/с, а native-движок держит ~200k при p95<20мс — примерно 7x на бокс. Весь дизайн существует, чтобы достичь этого потолка, не вернув Node-узкое место в путь совместимости.
Независимость считается тем, что надо доказать, а не заявить: машинный сканер обходит каждую продовую зависимость, а отдельный гейт гоняет корпус с физически удалённым эталонным пакетом. Гейт сначала красный — первая же квитанция показывает его падение до работы — и зелёным становится только когда продовая поверхность действительно не импортирует эталонную реализацию.
Совместимость проверяется дифференциально: минимальное эталонное приложение гоняется на обоих движках и выводы сравниваются, так что эталонная реализация становится тестовым оракулом, а не рантайм-зависимостью.
Wire-кодек у нас свой, не заимствованный из эталонного пакета, так что даже браузерный клиент перестаёт тянуть этот пакет в продакшн — вплоть до опознания ошибки, совпадающего с официальным поведением по сигналу.
Кодогенерация и новые проекты целятся в наши собственные пакеты; эталонный API остаётся только поверхностью совместимости и оракула. Перенос существующего приложения должен быть сменой URL, а корпус паритета (16/16 и 18/18 на релизных гейтах) это подкрепляет.
Архитектура и технологические решения
Сделано на Rust, SpacetimeDB, Realtime BaaS.
Rust SpacetimeDB-модуль для схемы и редьюсеров; компилятор из нашей схемы и функций в этот модуль; слой совместимости для клиентского и серверного API; свой wire-кодек; Node control-plane gateway (дашборд и деплой) для операций с малым трафиком; worker для actions и внешнего I/O; и паритетные фикстуры, бенчмарки и гейты независимости в CI.
Результат и выводы
Существующие приложения переезжают почти сменой URL, на движок, держащий ~200k realtime-доставок/с при p95<20мс — примерно 7x на бокс против старого рантайма, без потолка Node.
Наши приложения получают реактивный бэкенд, которым мы владеем целиком, на движке, держащем ~200k realtime-доставок/с при p95<20мс, с той же моделью live-запросов — и переносом, который должен быть сменой URL, подкреплённым зелёным гейтом независимости, а не обещанием.
Связанные статьи
Читать дальше
Связанные истории проектов
Эти проекты близки по техническим или продуктовым решениям и показывают, как тот же принцип работает в другом контексте.
Dev-storyCMS
Динамический headless-CMS на webedge-db — типы контента, медиа, роли и публичный read-API, питающий наши сайты и статьи.
Dev-storyMTP-RDMA инференс-кластер
Тензорный 27B на двух Mac по Thunderbolt RDMA — ~70 → 90–100 tok/s.
Dev-storyПлатформа календарных бронирований
Бронирование на Vue 3 — слоты, записи, письма и Meet-видеозвонки в один клик.
Есть похожая идея?
Обсудить проект