PRODСуверенная европейская платформа BaaSОткрыть панель управления →

Производительность · 15 минута чтения

Как мы тестируем серверную часть: повторяемая методология

Affane Daylami · Fondateur · 8 июля 2026 г.

Вернуться в блог

Контрольная цифра без метода ничего не доказывает. «p95 менее X мс», «холодный старт менее Y мс» — это может написать любой желающий на маркетинговой странице. Что-то доказывает, так это метод: используемое оборудование, продолжительность теста, протокол измерения и возможность третьей стороны воспроизвести его идентично.

Этот текст на английском языке был создан автоматически на основе французского оригинала и еще не проверялся.
Эта страница была переведена автоматически. Английская версия является авторитетной.

Эта статья отвечает на конкретный вопрос — как протестировать серверный 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 (медиана)Половина запросов выполняется быстрее этого значенияПолностью скрывает хвост распределения
p951 из 20 запросов медленнееЗона, где появляются первые недовольные пользователи
p991 из 100 запросов медленнееНаиболее чувствителен к скоординированному пропуску, если протокол плохо разработан.
#
Проверено в коде

3 уровня тестов уже присутствуют в нашем репозитории.

Публикация методологии без реальных инструментов была бы просто еще одной формой театра. Папка benchmarks/ в репозитории Aurabase уже содержит 3-уровневый пакет, по своей структуре вдохновленный общедоступной методологией Supabase — инструменты существуют, измеренные и датированные результаты еще не существуют.

3
ТЕСТОВЫЕ УРОВНИ
Микро, загрузка HTTP, сравнение
3
ЭТАЛОННЫЕ ЯЩИКИ
аура-крипто, аура-дб-адаптеры, аура-ядро
8
К6 СКРИПТЫ
7 подключены к Makefile, 1 ожидает

Уровень 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).

libs/aura-crypto/benches/crypto_bench.rsrust
// Актуальная выписка из репозитория
let mut group = c.benchmark_group("jwt/hs256");

group.bench_function("generate", |b| {
    b.iter(|| jwt::generate_access_token(
        black_box(user_id), black_box(project_id),
        black_box("authenticated"), black_box(SECRET),
    ))
});

group.bench_function("validate", |b| {
    b.iter(|| jwt::validate_token(black_box(&token), black_box(SECRET)))
});

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 строк).

libs/aura-core/benches/core_bench.rsrust
group.throughput(Throughput::Bytes(
    serde_json::to_vec(&small).unwrap().len() as u64
));
group.bench_function("NatsRequest/small", |b| {
    b.iter(|| serde_json::to_vec(black_box(&small)).unwrap())
});

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 существует в репозитории, но еще не имеет цели, и эта статья документирует ее как есть, а не маскирует.

benchmarks/k6/scenarios/crud-read.jsjavascript
export const options = {
  scenarios: {
    crud_read: {
      executor: 'ramping-vus',
      startVUs: 10,
      stages: [
        { duration: '30s', target: 50 },
        { duration: '1m', target: 200 },
        { duration: '30s', target: 0 },
      ],
    },
  },
  thresholds: THRESHOLDS_READ,
}

Общая конфигурация определяет пороговые значения для каждого типа операции. Это критерии «пройден/не пройден», которые тест проверяет при каждом запуске, а не уже измеренные результаты:

Чтение (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 и пропускную способность в операциях в секунду.

benchmarks/comparison/direct_vs_api.pypython
def percentile(data, p):
    k = (len(data) - 1) * (p / 100)
    f = int(k)
    c = f + 1
    if c >= len(data):
        return data[f]
    return data[f] + (k - f) * (data[c] - data[f])

Второй сценарий (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/. Это именно тот рефлекс датированного раскрытия информации в одном воспроизводимом прогоне, который следующий раздел формализует в полном протоколе.

#
Методология

Протокол, который мы будем применять перед публикацией рисунка

Восемь обязательств, каждое из которых основано на практике, уже задокументированной признанным сторонним инструментом или проектом, а не придуманным для этого случая.

  1. Предварительный нагрев отдельно от измерения. Criterion.rs заполняет кэши ЦП/ОС раньше времени; pgbench категорически рекомендует никогда не верить пробежкам, продолжающимся всего несколько секунд.
  2. Фиксированная продолжительность, а не фиксированное количество итераций. Нагрузке требуется время для сходимости — в этом роль stages k6 и флага -T pgbench.
  3. Процентили, а не просто среднее значение — и активная бдительность в случае скоординированного упущения, если генератор нагрузки работает в замкнутом контуре.
  4. Подробно документированная среда: git commit тестируемой службы, версия PostgreSQL, спецификация оборудования, версия инструмента загрузки. PlanetScale документирует точные параметры TPCC (TABLES=20, SCALE=250, ~500 ГБ) именно по этой причине — без этих деталей никто не сможет воспроизвести прогон.
  5. Результаты с отметкой времени и версией, ни одного числа, выгравированного на маркетинговой странице без даты. Текущий инструментарий уже записан в устаревшем файле; необходимо будет распространить этот рефлекс на любые публично опубликованные измерения, при этом принимающий регион будет документирован, как и любая другая переменная среды (см. наше руководство по суверенитету хостинга ЕС, актуально, как только цифра зависит от данного региона).
  6. Сценарии и необработанные данные публикуются вместе с совокупным результатом, а не только с окончательным средним значением. PlanetScale даже предлагает читателям сообщить о методологической ошибке по специальному адресу – позиция, которую мы считаем здоровой и которую мы хотим возобновить.
  7. Объявлена ​​пропускная способность вместе с задержкой, а не только одно или другое. Система может иметь превосходную задержку при низкой нагрузке и снижать пропускную способность по мере увеличения параллелизма — именно это и призван выявить сценарий breakpoint нашего пакета k6 (масштабирование до сбоя) и то, что измеряется микротестом Criterion Throughput::Bytes на функциональном уровне.
  8. Значительный пробел перед объявлением об улучшении. Разница в несколько процентов между двумя прогонами может быть шумом измерений, а не реальным выигрышем — Criterion.rs вычисляет вероятность того, что наблюдаемая разница обусловлена ​​случайностью, прежде чем квалифицировать ее как регресс или улучшение. Отдельные цифры без этой проверки — всего лишь статистический анекдот.
#
Редакционная приверженность

Чего мы не будем делать

Этот список имеет такое же значение, как и положительный протокол выше.

  • Сравнение различных топологий (самостоятельный и управляемый экземпляр, холодный и предварительно нагретый экземпляр) без явного уведомления об этом.
  • Сохраните лучший результат из десяти, не упоминая остальные девять.
  • Публикуйте рисунок без даты, без сервисной версии, без сценария воспроизведения.
  • Опубликуйте повторно существующие маркетинговые данные, если они не связаны с этим протоколом.
  • Сравнивать себя с конкурентом по необработанным показателям производительности, если этот конкурент не публикует свою собственную методологию аналогичным образом — цифра против молчания — это не сравнение, это лозунг.
Конкретный пример, уже исправленный внутри

Цифра типа «холодный старт менее 1 мс» была распространена без подтверждения воспроизводимым эталоном. Теперь он рассматривается внутри компании как неподдерживаемый и не должен рассматриваться как измеренная характеристика продукта до тех пор, пока это не будет подтверждено каким-либо датированным измерением с использованием опубликованной методологии. Это именно то утверждение, что этот протокол существует для предотвращения повторения.

#
Повторяемый

Минимальный протокол для сравнительного анализа любого бэкэнда

Этот протокол не зависит от какого-либо конкретного инструмента Aurabase — вы можете применить его к своему собственному API уже сегодня.

  1. Установите загрузку перед инструмента: только чтение, запись, реалистичное сочетание для вашего приложения, а не общее соотношение, скопированное из другого проекта.
  2. Четко отделите фазу предварительного нагрева от фазы измерения.
  3. Запускайте тест достаточно долго — минуты, а не секунды.
  4. Измеряйте в процентилях (p50/p95/p99), а не только в среднем.
  5. Убедитесь, что ваш генератор нагрузки не находится в замкнутом контуре, или исправьте упущение координации в анализе.
  6. Изолируйте тестируемую среду — никаких шумных соседей и никаких конкурирующих фоновых задач.
  7. Публикуйте протестированную версию, дату, характеристики оборудования и сценарий, а не только конечный результат.

На голой базе Postgres этот протокол выполняет одну команду pgbench — 20 одновременных клиентов, распределенных по 4 потокам, в течение 5 минут, с отчетом о ходе выполнения каждые 10 секунд:

terminalbash
# Инициализируйте набор тестовых данных (коэффициент масштабирования >= количество клиентов)
pgbench -i -s 50 ma_base

# -c одновременные клиенты, -j потоки, -T продолжительность в секундах, -P интервал отчетов
pgbench -c 20 -j 4 -T 300 -P 10 ma_base
#
Инструменты

Справочные инструменты по уровням

Пять инструментов, каждый из которых подходит для своего уровня — ни один не заменяет другие.

Микрофон (функция)Критерий.рсЧистый процессор, статистика начальной загрузки
SQL-запросpgbenchТранзакция типа TPC-B, tps и задержка
HTTP/WS-загрузкаk6 (Графана)Процентили, пороговые значения «прошел/не прошел»
OLTP в масштабеsysbench + TPCC (методология телескопа)QPS, цена за производительность
Коррекция измеренийHdrГистограммаКомпенсирует скоординированное упущение
#
Часто задаваемые вопросы

Часто задаваемые вопросы

Почему Aurabase еще не публикует результаты тестов?+
Потому что на сегодняшний день никакие показатели производительности не были измерены в соответствии с опубликованным и воспроизводимым протоколом. Например, такая цифра, как «холодный старт менее 1 мс», была распространена без подкрепления воспроизводимыми контрольными показателями: сегодня она рассматривается как необоснованная и не должна рассматриваться как измеренная характеристика продукта. В этой статье документирован протокол, которому мы будем следовать перед публикацией результатов, именно для того, чтобы избежать повторения подобных утверждений.
Что такое процентиль p95 или p99 и почему не среднее значение?+
P95 — это время ответа, ниже которого обнаруживается 95% запросов, поэтому 1 из 20 запросов медленнее. P99 увеличивает этот порог до 1 запроса из 100. Среднее значение скрывает эти медленные запросы, поскольку разбавляет их массой быстрых запросов; процентили изолируют хвост распределения, который действительно замечают пользователи.
Что такое «скоординированное бездействие»?+
Это смещение измерений, описанное проектом HdrHistogram: когда инструмент загрузки ожидает ответа на запрос перед отправкой следующего (замкнутый цикл), сервисная пауза механически уменьшает количество медленных запросов, записанных во время этой паузы. Конечный результат может показать гораздо лучшую задержку, чем у реального пользователя.
Можем ли мы воспроизвести эти тесты самостоятельно?+
Описанный здесь протокол — процентили, отдельный предварительный нагрев, документированная среда, датированные результаты — применим к любому API с общедоступными инструментами (k6, pgbench, Criterion.rs, sysbench). Внутренние инструменты Aurabase (папка тестов/репозитория) в настоящее время используются для разработки и еще не упакованы как общедоступный пакет, доступный одним щелчком мыши. Создайте проект, чтобы протестировать собственную загрузку API Aurabase с помощью собственных скриптов k6.
В чем разница между измеренным процентилем и порогом SLA?+
Процентиль (p95, p99) — это статистика, рассчитанная постфактум на основе реальных измерений. Порог SLA (или порог k6, например p(95)<500) — это заранее заданный целевой показатель, который тест проверяет в режиме «прошел/не прошел». Смешение этих двух значений приводит к представлению недостигнутой цели как полученного результата – это именно то различие, которое этот протокол требует, чтобы оно было явным для каждой опубликованной цифры.
Зачем измерять пропускную способность в дополнение к задержке?+
Система может быстро реагировать при низкой нагрузке и наблюдать внезапное снижение ее задержки при превышении порога параллелизма — сама по себе задержка не показывает, где находится этот порог. Измерение пропускной способности (запросов или байтов в секунду) вместе с задержкой позволяет выявить этот переломный момент, для обнаружения которого специально разработан сценарий точки останова нашего пакета k6.

ГОТОВЫ К РАЗВЕРТЫВАНИЮ?

Ваш бэкэнд за пять минут.

Кредитная карта не требуется · 500 МБ бесплатно · 50 000 MAU