Эта статья отвечает на конкретный вопрос — как протестировать серверный API воспроизводимым способом — путем документирования протокола, который мы будем применять в Aurabase, прежде чем публиковать какие-либо показатели производительности. Не результаты: метод. Любые измерения, уже опубликованные где-либо на этом сайте (в частности, на нашей странице Performance), которые еще не основаны на этом протоколе, следует рассматривать как непроверенные до дальнейшего уведомления.
Самое необходимое
На сегодняшний день не существует результатов производительности Aurabase по опубликованному воспроизводимому протоколу — в этой статье документируется методология, которую мы будем применять для их получения, а не уже полученные результаты. Репозиторий уже содержит трехуровневый набор тестов : микротесты Criterion.rs на 3 ящиках, нагрузочные тесты k6 на 8 сценариях HTTP/WebSocket и скрипт Python для прямого сравнения Postgres и API с расчетом процентилей. Полный протокол — продолжительность измерения, процентили, а не средние значения, изоляция среды, раскрытие версии и даты — основан на проверенных внешних источниках: PostgreSQL, Criterion.rs, k6 (Grafana), HdrHistogram, PlanetScale и Convex. Любые заявления о производительности, уже опубликованные где-либо на этом сайте без привязки к этому протоколу, следует считать непроверенными.
Почему мы не публикуем голые цифры
Convex, издатель конкурирующего адаптивного бэкэнда, публично дистанцировался от того, что его техническая команда называет «войной гистограмм» между поставщиками баз данных. Его формула проста: «Это масштабирование театра, а не масштабирование» — масштабирование театра, а не реальное масштабирование (stack.convex.dev/on-competitive-benchmarks, технический блог Stack, по состоянию на 23 августа 2026 г.).
Его главный аргумент: тест, который сравнивает две системы с разными гарантиями согласованности, топологией или моделью ценообразования, часто не тестирует одно и то же, даже если он утверждает, что делает это — «тест на самом деле не тестирует одно и то же». У этого рефлекса есть название в отрасли: бенчмаркинг, публикующий цифру, выбранную из-за ее маркетингового эффекта, а не из-за ее методологической строгости.
Наш ответ – не отказываться от измерений: отказ публиковать цифры на неопределенный срок был бы столь же нечестным, как и публикация необоснованных цифр. Прежде чем заявлять, что мы что-либо измерили, нужно сначала задокументировать, как мы будем измерять, какими инструментами и при каких условиях. Это также то, что отличает полезное сравнение (например, наше сравнение Aurabase и Appwrite, которое документирует поддающиеся проверке архитектурные различия) от сравнения показателей производительности без общего протокола.
Это особенно важно для технического руководителя или технического директора, который должен отстаивать выбор серверной части в техническом комитете: цифра, которую невозможно связать с методом, не выдерживает первого, несколько настойчивого вопроса. Документированный протокол является самозащитным — вы можете показать сценарий, протестированную версию и при необходимости снова запустить тест перед кем-то.
Что делает большинство бэкэнд-тестов обманчивыми
Систематически возникают две ловушки: сравнение различных топологий без отчета об этом и измерение задержки таким образом, чтобы точно скрыть паузы, которые наиболее важны для пользователя.
По первому пункту PlanetScale явно документирует ограничение аппаратной четности: каждая сравниваемая среда должна работать на вычислительных ресурсах (виртуальных ЦП, ОЗУ), равных или превышающих эталонный экземпляр, в одном и том же облачном регионе (Planetscale.com/benchmarks, методология «Телескоп», по состоянию на 23 августа 2026 г.). Без этой дисциплины разница в задержке может просто отражать более крупную машину, а не более быструю архитектуру.
Тот же принцип применим к состоянию кэша и топологии сети. Только что запустившийся экземпляр (холодный кэш Postgres, пустой пул соединений, план запроса еще не кэширован) отвечает структурно медленнее, чем экземпляр, работавший в течение часа под стабильной нагрузкой. Запрос из того же региона, что и база данных, структурно отвечает быстрее, чем запрос между регионами. Два теста, в которых ни один из них не указан, просто несопоставимы, даже если они показывают идентичные единицы измерения.
Во втором пункте ловушка называется скоординированного пропуска. HdrHistogram, эталонный проект по измерению задержки, созданный Гилом Тене, объясняет это следующим образом: когда генератор нагрузки ожидает ответа на запрос перед отправкой следующего (замкнутый цикл), приостановка обслуживания автоматически снижает количество запросов, отправленных во время паузы, и, следовательно, количество записанных измерений с высокой задержкой (github.com/HdrHistogram/HdrHistogram, по состоянию на 23 августа 2026 г.). Проект дает конкретный и количественный пример: в гипотетической системе, которая измеряет задержку каждые 10 мс в течение 200 секунд, одной паузы в 100 секунд в середине теста достаточно, чтобы создать без коррекции гистограмму, на которой примерно 99,99% ответов укладываются в пределах 1 мс — даже несмотря на то, что за эту единственную паузу прошла половина реального времени.
Нагрузочный тест с обратной связью, который отправляет запрос только после получения предыдущего ответа, систематически недооценивает длинные паузы. Отображаемый p99 может быть лучше, чем реальность, которую испытывает реальный пользователь — не потому, что система быстрая, а потому, что протокол измерения «забыл» отправить запросы во время паузы.
Почему средний врёт: р50, р95, р99
Средняя задержка может показаться превосходной, если один из двадцати запросов занимает в пять раз больше времени. Именно это показывают процентили и то, что структурно скрывает среднее значение.
С механической точки зрения в процентиле нет ничего загадочного: отсортируйте все измеренные задержки в порядке возрастания, затем возьмите значение в соответствующей позиции. Из 1000 отсортированных запросов p50 — 500-е значение, p95 — 950-е, p99 — 990-е. Одного аномально медленного запроса среди 1000 достаточно, чтобы заставить p99 сдвинуться с места — именно его чувствительность к редким случаям делает его полезным, когда этот же изолированный запрос в среднем почти не влияет.
Контрольный знак: текстовый отчет, который pgbench — официальный инструмент тестирования PostgreSQL — отображает по умолчанию, дает среднее значение и стандартное отклонение , а не процентили (postgresql.org/docs/current/pgbench.html, по состоянию на 23 августа 2026 г.). Его официальная документация также предупреждает: «Никогда не верьте ни одному тесту, который длится всего несколько секунд» — никогда не верьте тесту, который работает всего несколько секунд, что относится как к продолжительности, так и к выбранному показателю.
k6, инструмент загрузки, который мы используем для уровня 2 нашего пакета, решает эту проблему с помощью пороговых значений, выраженных в процентилях: синтаксис p(95)<500 определяет критерий «пройден/не пройден» — 95% запросов должны отвечать в течение 500 мс — непосредственно в тестовой конфигурации (grafana.com/docs/k6, просмотрено 23 августа 2026 г.).
| р50 (медиана) | Половина запросов выполняется быстрее этого значения | Полностью скрывает хвост распределения |
|---|---|---|
| p95 | 1 из 20 запросов медленнее | Зона, где появляются первые недовольные пользователи |
| p99 | 1 из 100 запросов медленнее | Наиболее чувствителен к скоординированному пропуску, если протокол плохо разработан. |
3 уровня тестов уже присутствуют в нашем репозитории.
Публикация методологии без реальных инструментов была бы просто еще одной формой театра. Папка benchmarks/ в репозитории Aurabase уже содержит 3-уровневый пакет, по своей структуре вдохновленный общедоступной методологией Supabase — инструменты существуют, измеренные и датированные результаты еще не существуют.
Уровень 1 — Микро-бенчмарки Criterion.rs
Три ящика рабочей области Cargo имеют специальные тесты, связанные с процессором: aura-crypto (хеш Argon2, JWT HS256 — генерация, проверка и подпись для PostgREST, шифрование AES-GCM), aura-db-adapters (фильтры синтаксического анализа и select в формате PostgREST — eq., gte., in.(), внедрение отношений) и aura-core (сериализация JSON, разрешение schema_name, проверка UUID).
aura-db-adapters конкретно измеряет стоимость анализа запросов формата PostgREST — четыре случая для фильтров (simple_4, complex_10, or_group, in_large_50 с 50 значениями) и четыре для select (одиночные столбцы, *, одно встраивание отношения, пять встраиваний). Это своего рода затраты, невидимые в глобальном нагрузочном тесте: регрессия при анализе сложного фильтра or.(...) почти ничего не изменит в p95 малоиспользуемой конечной точки, но станет измеримой на конечной точке с высоким трафиком — отсюда и интерес к ее изоляции в микро-бенчмарке, а не полагаться исключительно на уровень 2.
aura-core использует другой подход: вместо измерения необработанного времени он измеряет пропускную способность (Throughput::Bytes) при сериализации JSON и десериализации внутренних сообщений NatsRequest/NatsResponse, которыми обмениваются шлюз и службы — с тремя реалистичными размерами полезной нагрузки (минимальный запрос, запрос с вложенным телом JSON, ответ в виде списка из 50 строк).
Criterion.rs не просто определяет время цикла. Сначала он запускает фазу прогрева для заполнения кэшей ЦП/ОС, обнаруживает выбросы с помощью модифицированной версии метода Тьюки (не исключая их из набора данных), рассчитывает доверительные интервалы путем начальной загрузки большого количества повторно дискретизированных выборок и обнаруживает снижение производительности между двумя запусками с помощью статистического теста Стьюдента с настраиваемым порогом шума — обычно ± 1% — для игнорирования статистически незначимых изменений (bheisler.github.io/criterion.rs/book/anaанализ.html, по состоянию на 23 августа 2026 г.).
При каждом запуске критерия создается подробный отчет в формате HTML в target/criterion/ — распределения, графики регрессии, сравнение с предыдущим запуском. Именно эти отношения, а не просто конечную линию, должна позволить возродить серьезная методология.
Уровень 2 — нагрузочное тестирование k6
Восемь сценариев k6 охватывают шлюз на стороне плоскости данных: health (базовая задержка), auth-flow (регистрация → вход → обновление → выход), crud-read и crud-write, storage (загрузка/выгрузка), realtime-ws, breakpoint (загрузка) увеличиваться до отказа) и supabase-compare. Семь подключены к выделенной цели Makefile — supabase-compare.js существует в репозитории, но еще не имеет цели, и эта статья документирует ее как есть, а не маскирует.
Общая конфигурация определяет пороговые значения для каждого типа операции. Это критерии «пройден/не пройден», которые тест проверяет при каждом запуске, а не уже измеренные результаты:
| Чтение (GET) | p95 < 500 мс · p99 < 1000 мс | Конфигурация k6 (benchmarks/k6/lib/config.js) |
|---|---|---|
| Написание (POST/PATCH) | p95 < 300 мс · p99 < 1000 мс | Конфигурация к6 |
| Аутентификация (вход/обновление) | p95 < 300 мс · p99 < 1000 мс | Конфигурация к6 |
| Хранение (загрузка/выгрузка) | p95 < 500 мс · p99 < 2000 мс | Конфигурация к6 |
| Частота ошибок, все сценарии | < 1 % | Конфигурация к6 |
README.md в папке benchmarks/ документирует порог чтения p95 в < 200ms («Supabase SLO»), в то время как порог, фактически применяемый в benchmarks/k6/lib/config.js — тот, который выполняется тестом, — это p(95)<500. Эти два файла являются производными друг от друга. Это конкретный пример, найденный при чтении исходного кода этой статьи, показывающий, почему протокол должен иметь единый версионный источник истины, а не документироваться в двух местах: без него даже команда, которая пытается быть строгой, в конечном итоге публикует противоречивые пороговые значения.
Уровень 3 — Прямое сравнение PostgreSQL и API
Скрипт Python (direct_vs_api.py) измеряет фактическую нагрузку на уровне шлюза + сервиса, сравнивая прямые запросы psycopg2 с HTTP-вызовами в одной и той же операции — список, однократное чтение по идентификатору, фильтрованное и отсортированное чтение. Каждое измерение следует за прогревом в 10 итераций перед циклом по времени, затем вычисляет среднее значение, p50, p95, p99 и пропускную способность в операциях в секунду.
Второй сценарий (aurabase_vs_supabase.py) применяет ту же логику прогрева и расчета процентилей для прямого сравнения с локальным экземпляром Supabase (Supabase CLI, localhost:54321 по умолчанию) — тот же компьютер, одна и та же локальная сеть для обоих, в точности та дисциплина четности среды, которую PlanetScale документирует для своих собственных сравнений.
Сценарий оркестровки (collect_baseline.sh, целевой bench-baseline из Makefile) соединяет три уровня — Criterion в 3 ящиках, подмножество сценариев k6 (health и crud-read сегодня, еще не все 8), затем сравнение Python — и записывает журналы, JSON и Criterion HTML-отчеты в папку с уникальной меткой времени: benchmarks/results/AAAAMMJJ_HHMMSS/. Это именно тот рефлекс датированного раскрытия информации в одном воспроизводимом прогоне, который следующий раздел формализует в полном протоколе.
Протокол, который мы будем применять перед публикацией рисунка
Восемь обязательств, каждое из которых основано на практике, уже задокументированной признанным сторонним инструментом или проектом, а не придуманным для этого случая.
- Предварительный нагрев отдельно от измерения. Criterion.rs заполняет кэши ЦП/ОС раньше времени;
pgbenchкатегорически рекомендует никогда не верить пробежкам, продолжающимся всего несколько секунд. - Фиксированная продолжительность, а не фиксированное количество итераций. Нагрузке требуется время для сходимости — в этом роль
stagesk6 и флага-Tpgbench. - Процентили, а не просто среднее значение — и активная бдительность в случае скоординированного упущения, если генератор нагрузки работает в замкнутом контуре.
- Подробно документированная среда: git commit тестируемой службы, версия PostgreSQL, спецификация оборудования, версия инструмента загрузки. PlanetScale документирует точные параметры TPCC (
TABLES=20,SCALE=250, ~500 ГБ) именно по этой причине — без этих деталей никто не сможет воспроизвести прогон. - Результаты с отметкой времени и версией, ни одного числа, выгравированного на маркетинговой странице без даты. Текущий инструментарий уже записан в устаревшем файле; необходимо будет распространить этот рефлекс на любые публично опубликованные измерения, при этом принимающий регион будет документирован, как и любая другая переменная среды (см. наше руководство по суверенитету хостинга ЕС, актуально, как только цифра зависит от данного региона).
- Сценарии и необработанные данные публикуются вместе с совокупным результатом, а не только с окончательным средним значением. PlanetScale даже предлагает читателям сообщить о методологической ошибке по специальному адресу – позиция, которую мы считаем здоровой и которую мы хотим возобновить.
- Объявлена пропускная способность вместе с задержкой, а не только одно или другое. Система может иметь превосходную задержку при низкой нагрузке и снижать пропускную способность по мере увеличения параллелизма — именно это и призван выявить сценарий
breakpointнашего пакета k6 (масштабирование до сбоя) и то, что измеряется микротестом CriterionThroughput::Bytesна функциональном уровне. - Значительный пробел перед объявлением об улучшении. Разница в несколько процентов между двумя прогонами может быть шумом измерений, а не реальным выигрышем — Criterion.rs вычисляет вероятность того, что наблюдаемая разница обусловлена случайностью, прежде чем квалифицировать ее как регресс или улучшение. Отдельные цифры без этой проверки — всего лишь статистический анекдот.
Чего мы не будем делать
Этот список имеет такое же значение, как и положительный протокол выше.
- Сравнение различных топологий (самостоятельный и управляемый экземпляр, холодный и предварительно нагретый экземпляр) без явного уведомления об этом.
- Сохраните лучший результат из десяти, не упоминая остальные девять.
- Публикуйте рисунок без даты, без сервисной версии, без сценария воспроизведения.
- Опубликуйте повторно существующие маркетинговые данные, если они не связаны с этим протоколом.
- Сравнивать себя с конкурентом по необработанным показателям производительности, если этот конкурент не публикует свою собственную методологию аналогичным образом — цифра против молчания — это не сравнение, это лозунг.
Цифра типа «холодный старт менее 1 мс» была распространена без подтверждения воспроизводимым эталоном. Теперь он рассматривается внутри компании как неподдерживаемый и не должен рассматриваться как измеренная характеристика продукта до тех пор, пока это не будет подтверждено каким-либо датированным измерением с использованием опубликованной методологии. Это именно то утверждение, что этот протокол существует для предотвращения повторения.
Минимальный протокол для сравнительного анализа любого бэкэнда
Этот протокол не зависит от какого-либо конкретного инструмента Aurabase — вы можете применить его к своему собственному API уже сегодня.
- Установите загрузку перед инструмента: только чтение, запись, реалистичное сочетание для вашего приложения, а не общее соотношение, скопированное из другого проекта.
- Четко отделите фазу предварительного нагрева от фазы измерения.
- Запускайте тест достаточно долго — минуты, а не секунды.
- Измеряйте в процентилях (p50/p95/p99), а не только в среднем.
- Убедитесь, что ваш генератор нагрузки не находится в замкнутом контуре, или исправьте упущение координации в анализе.
- Изолируйте тестируемую среду — никаких шумных соседей и никаких конкурирующих фоновых задач.
- Публикуйте протестированную версию, дату, характеристики оборудования и сценарий, а не только конечный результат.
На голой базе Postgres этот протокол выполняет одну команду pgbench — 20 одновременных клиентов, распределенных по 4 потокам, в течение 5 минут, с отчетом о ходе выполнения каждые 10 секунд:
Справочные инструменты по уровням
Пять инструментов, каждый из которых подходит для своего уровня — ни один не заменяет другие.
| Микрофон (функция) | Критерий.рс | Чистый процессор, статистика начальной загрузки |
|---|---|---|
| SQL-запрос | pgbench | Транзакция типа TPC-B, tps и задержка |
| HTTP/WS-загрузка | k6 (Графана) | Процентили, пороговые значения «прошел/не прошел» |
| OLTP в масштабе | sysbench + TPCC (методология телескопа) | QPS, цена за производительность |
| Коррекция измерений | HdrГистограмма | Компенсирует скоординированное упущение |