PLGM: веб-интерфейс, разбор запросов и replay на нескольких MongoDB

29 октября 2026 года Даниэл Алмейда из Percona описал веб-интерфейс PLGM. Это Percona Load Generator for MongoDB Clusters: генератор нагрузки, который гоняет по MongoDB ваши коллекции и ваши запросы, а не случайный набор из чужого бенчмарка. В декабре утилита была консольной. Правили YAML, запускали бинарник, читали цифры в терминале. Прошёл почти год.

Новые версии MongoDB выходят со своими цифрами. Сняты они на чужой схеме. Индексы, размер документов, рабочий набор и смесь запросов у вас другие. Как версия поведёт себя на вашем приложении, видно только если прогнать вашу нагрузку.

И ломается обновление обычно не так, что сервер стал на 5% медленнее. Кусаются другие вещи.

  • Запрос, который раньше был нормальным, выбирает другой план.
  • Агрегация, которая сортировала в памяти, упирается в лимит или начинает писать промежуточные данные на диск.
  • Запрос, который трогал горстку документов, начинает читать коллекцию целиком.

Одной цифрой пропускной способности это не поймать. Нужно смотреть каждую форму запроса и то, что сервер реально сделал. Чтобы сравнение версий было честным, обе должны увидеть один и тот же трафик. Для этого в PLGM теперь запись и воспроизведение нагрузки, Record и Replay.

Веб-интерфейс на 127.0.0.1

Флаг --webui открывает панель в браузере. Слушает она только 127.0.0.1, в сеть интерфейс сам не торчит. Если утилита стоит на удалённом сервере, Алмейда предлагает туннель ssh -L 9999:127.0.0.1:9999 user@host и адрес http://localhost:9999. Сертификат для этого не нужен.

Первый экран спрашивает, что вы хотите сделать. Варианты: типовая нагрузка, своя схема и свои запросы, стресс-тест, запись нагрузки, воспроизведение записи, уже законченный прогон. У каждой задачи свои поля. Строка подключения, число воркеров и длительность переезжают, когда переключаетесь. Значения по умолчанию начинают задачу с чистого листа и соединение не трогают.

То, что раньше жило в config.yaml, теперь в браузере. Уровней три. Basic показывает то, без чего прогон обычно не стартует: подключение, воркеры, длительность, профиль нагрузки, описания коллекций и запросов, начальные данные, смесь операций. Advanced и Expert открывают остальное, уже по группам.

  • Connection: файлы TLS, размеры пула, прямое подключение, отладочные логи драйвера.
  • Workload execution: пауза между операциями, характер доступа, размеры пачек, таймауты, шардирование, повторы.
  • Seed и Deterministic: детерминированный трафик, seed, число операций на воркера, файлы исходов.
  • Data и queries: схема, выведенная из лога запросов.
  • Metrics и reporting: разбор после прогона и выгрузка CSV.
  • Query analysis: настройки нового разбора запросов.
  • Trends и history: сколько прогонов хранить, подписи, сохранённые прогоны.

Описания запросов и коллекций можно сохранить в интерфейсе и не заливать одни и те же файлы на каждый запуск. У настройки есть знак вопроса: что будет, если её включить и если выключить, плюс ключ в config.yaml и переменная окружения. Если на кнопке Launch что-то не сходится, текст ошибки встаёт под полем, и PLGM переводит взгляд туда.

Когда прогон пошёл, сверху карточки по типам операций. Можно выбрать все операции, Find, Insert, Update, Delete, Aggregate или Transaction. Карточки и графики переключаются на этот тип: пропускная способность, средняя латентность и хвост, p99 или p95.

Вкладка Latency Analysis показывает шкалу перцентилей (среднее, p95 или p99), распределение за весь прогон и тепловую карту: столбик на секунду, цвет по p99 этой секунды. Ползунок Time Machine возвращает к любой секунде. Выделение мышью на графике приближает диапазон, и цифры считаются уже по нему. Законченные прогоны лежат в Completed Runs, панель можно открыть позже.

Нагрузку можно сделать похожей на прод, а не на ровный молот из фиксированного числа потоков. Профили ramp, step, spike и синусоида меняют число воркеров по ходу прогона. Нужно посмотреть, что будет, если трафик на 10 секунд утроится: это spike. Think time добавляет паузу между операциями, по желанию с разбросом. В латентность пауза не входит, воркер ведёт себя как клиент, а не как плотный цикл.

Перекос доступа, zipfian или hotspot, шлёт большую часть запросов в небольшую группу горячих документов. Ровный доступ прячет давление на кэш и драку за одни и те же документы. Перекос их показывает. Схему можно вывести из строк slow query log mongod или документов system.profile: PLGM предложит коллекции, запросы и смесь операций, с которой есть с чего начать.

После прогона скачивается JSON-сводка или самодостаточный HTML-отчёт. Его можно приложить к задаче и отправить коллеге, не усаживая его за ту же панель.

Эффективность скана, не одна цифра латентности

После бенчмарка обычно остаётся список форм запросов и их латентность. Латентность говорит, что запрос медленный. Почему он медленный, из неё не следует. И она молчит про запрос, который быстрый только потому, что коллекция ещё маленькая.

Пример, который приводит Алмейда. Поиск по полю customer_email без индекса на тестовой коллекции возвращается за 4 мс, и на него никто не смотрит. Сервер при этом читает 50 000 документов, чтобы отдать 2. Пока коллекция маленькая, это терпимо. Когда данных станет больше или тот же запрос побежит из пары сотен клиентов одновременно, одна латентность это место не подсветит.

Поэтому после прогона PLGM вызывает explain по выборке запросов, которые реально исполнились. Быстрые тоже, не только те, что перешли порог медленных. Результат лежит на вкладке Query и Index Analysis, по строке на форму запроса. В одну общую оценку колонки не сваливают: иначе цифра прячет больше, чем показывает.

Эффективность скана (scan efficiency) идёт от 0 до 100, чем выше, тем лучше. Вопрос один: сколько работы сервер сделал на каждый возвращённый документ. В расчёт входят просмотренные документы на результат, ключи индекса на результат и был ли полный просмотр коллекции там, где его быть не должно. Ориентир, который приводят авторы:

  • 1 документ на результат даёт 100.
  • 10 документов дают около 67.
  • 100 дают около 33.
  • 1000 и больше дают 0.

Латентность в эту оценку не входит. Медленный запрос, который делает ровно нужную работу, всё равно получает высокий балл. Быстрый, который читает сильно больше, чем отдаёт, получает плохой. Мелочь не наказывают: если сервер просмотрел меньше 100 документов, флага нет, даже если соотношение на бумаге плохое. Полный просмотр, который вы сами попросили, find({}) или отчёт по всей коллекции, проблемой не считают. У агрегаций с $group считают документы на входе в группировку, а не дюжину строк на выходе. Иначе любой отчёт выглядел бы провалом.

Это их собственная прикидка, не официальная метрика MongoDB. В интерфейсе так и написано.

Латентность стоит рядом, отдельно. Среднее и максимум по всем операциям этой формы, плюс p95 и p99, когда выборок хватает. Если в настройках заданы цели (среднее, p95, p99), форма помечается как внутри цели или выше. Цели нет, выдумывать её PLGM не станет.

Дальше доля в нагрузке: сколько операций, сколько в секунду и какая часть суммарного времени ушла на эту форму. Запрос на 2 мс, который бежит 10 000 раз в секунду, может значить больше, чем отчёт на 300 мс два раза за прогон. Эта колонка как раз про то, куда уходит время.

Отдельная колонка про накладные расходы исполнения. Сортировка держалась в памяти или ушла на диск? По каждой выборке видны байты сортировки, число сбросов, объём сброшенных данных, лимит памяти и пик, как их отдал сервер. Сюда смотрят, когда новая версия меняет, как устроены сортировки и агрегации в памяти. Если конвейер уже близко к лимиту на текущей версии, лучше увидеть это до обновления, а не после.

Для $lookup, если explain отдал цифры, видно, сколько документов и ключей просмотрела присоединённая коллекция. Клик по строке открывает подробности: запрос с вымаранными значениями, как посчитан балл, что сделала каждая выборка explain и какой план выбрал сервер.

Если форма читает слишком много, PLGM предлагает индекс. Порядок ключей обычный: сначала поля равенства, потом сортировка, потом одно поле диапазона. Перед советом утилита читает индексы, которые на коллекции уже есть, и не предлагает создать то, что уже создано. К рекомендации прилагается, каким формам она поможет и какую долю нагрузки они занимают, почему так решили и насколько в этом уверены. Цена тоже написана: каждый индекс замедляет запись и занимает диск и память.

Проверка одна. Создать индекс на тестовой системе и прогнать ту же нагрузку ещё раз. Это подсказка для проверки, а не индекс, который ставят на прод в пятницу вечером.

Вкладка Trends хранит короткое резюме прогона и сравнивает с ним следующий. Эффективность, латентность и доля в нагрузке идут по отдельности. В их примере индекс по customer_email поднял эффективность update с 0 до 100, а среднюю латентность с 81 мс примерно до 1 мс. Если между прогонами сменился план, набор индексов или версия сервера, PLGM пишет это рядом. Мало данных с одной из сторон (мало операций, неполный explain, мало выборок), и сравнение помечается not comparable. Красную метку на шум не вешают.

По умолчанию хранятся последние пять прогонов, лимит можно поднять в интерфейсе. У прогона подпись с версией сервера, её можно переименовать. Закрепить можно до десяти, вроде «7.0 baseline» или «before the new index». Закреплённые при очистке не удаляются. Сравнивать можно с предыдущим, с закреплённым или с любым сохранённым. До восьми прогонов ставятся рядом: латентность, p95, эффективность и доля по каждой форме. Если два прогона сравнить нельзя, написано почему. Общий файл хранилища можно указать нескольким хостам. Либо выгрузить прогоны в JSON и отдать коллеге.

Разбор покрывает update и delete, не только find и агрегации. updateMany с плохим фильтром читает коллекцию целиком так же, как плохой find. Запись разбирают через explain, повторно саму запись PLGM не выполняет. Конвейеры с $out или $merge только планируются, не исполняются. Для бенчмарка это важно: анализ не должен второй раз переписать данные.

Границы авторы называют прямо. Explain идёт после конца прогона, не во время, поэтому на цифры бенчмарка он не влияет. В консольной версии этого разбора нет. Смотрят выборки, не каждую операцию: на форму не больше двух explain, один около медианы и один около p95, и есть бюджет, сколько форм вообще разбирать. Что за бюджетом, помечается unassessed, а не efficient. Нет цифры от MongoDB, на месте будет not measured, не ноль и не зелёная галочка. Ожидания блокировок, время сети и CPU утилита не считает.

Одна запись на несколько MongoDB

Два прогона можно сравнивать, только если серверы сделали одну и ту же работу. У случайной генерации каждый прогон получает другие значения, другие документы и другой размер ответа. Record и Replay закрывает именно это.

Запись складывает каждую отправленную операцию с точными значениями в файл. По замерам Percona на их тестах запись заметной задержки прогону не добавила. У каждой записи своё имя с меткой времени, новая не затирает старую. В режиме воспроизведения PLGM показывает записи, которые нашёл у себя, и выбирает самую свежую.

Файл можно и не хранить. В группе Seed и Deterministic включается детерминированный трафик: операции строятся от seed. Два прогона с теми же настройками шлют те же команды с теми же значениями, а заливка данных даёт одинаковые документы на любом экземпляре. У прогона печатается отпечаток трафика. По нему видно, что прогоны совпали, а не просто «похожи по графику».

Свой прод тоже можно снять. PLGM читает JSON-логи mongod начиная с 4.4 или выгрузку system.profile и превращает их в запись. Каждое клиентское соединение становится воркером, с исходным порядком и паузами. То, что воспроизвести точно нельзя, помечается.

Несколько версий сразу ставятся в Run on, пункт Several servers. Целей от 2 до 8, у каждой короткая подпись и свой URI. В примере это 7.0, 8.0 и release candidate. Если пользователи на серверах разные, логин и пароль кладут в URI, а поля Username и Password оставляют пустыми. Один replay или один детерминированный прогон идёт на каждую цель по очереди или на все сразу.

Перед первой операцией PLGM проверяет, что на всех целях одинаковое число документов и одинаковые индексы. У каждой цели свои статистика, файл исхода, разбор запросов и запись в трендах. Сравнение с первой целью делается само.

Пока прогон идёт, на одних графиках рисуется по линии на сервер. Ось времени считается в секундах с момента, когда этот сервер стартовал, а не по настенным часам. Линию можно спрятать переключателем. Тип операции выбирается один на все графики.

В примере каждый сервер 60 секунд гонял одну и ту же детерминированную нагрузку. Последовательность операций одна, поэтому более быстрый сервер за это время просто успевает больше. На Latency Analysis в таблице распределения строка на сервер и разница с первым. На тепловой карте полоса на сервер по той же оси времени.

Можно сохранить исход каждой операции: латентность, код ошибки, сколько документов вернулось или изменилось, по желанию контрольная сумма каждого find и aggregate. Дальше пункт Replay Comparison на вкладке Query и Index Analysis, или команда plgm compare-replays, ставит два исполнения операция к операции. По форме запроса видно разницу латентности, разное число результатов, ошибки только на одной стороне и операции, которые разошлись сильнее всего. Отчёт открывается в браузере, его можно скачать.

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

Запуск plgm –webui

Percona отдаёт архив plgm-linux-amd64.tar.gz из репозитория Percona-Lab/percona-load-generator-mongodb. Запуск такой:

tar -xzvf plgm-linux-amd64.tar.gz
./plgm --webui

Дальше выбираете задачу, вписываете строку подключения и жмёте Launch Workload. Если свои запросы писать рано, в репозитории лежат демо: resources/collections/query_analysis_demo.json и resources/queries/query_analysis_demo.json. Там коллекция orders. Часть запросов идёт по нормальным индексам, часть нарочно плохая: поиск по email клиента без индекса, сортировка, которой ни один индекс не подходит, update с полным просмотром. Плюс один отчёт, который должен прочитать коллекцию целиком. На этом наборе видны все три случая и подсказки индексов для плохих запросов.

Настоящая проверка другая. Своя схема, свои запросы, версия, на которой вы сейчас, и версия, на которую смотрите. Ответ на вопрос «обновляться ли» от этого полезнее, чем стенд вендора на чужих данных.

Если на машине только WordPress, nginx, PHP-FPM и MariaDB, PLGM эту базу не трогает. Ему нужен MongoDB. Туннель нужен, когда панель смотрят с ноутбука: интерфейс и так слушает только локальный адрес. Выставлять его в публичный nginx ради «посмотреть графики с телефона» не стоит. В записях лежат значения запросов. Индекс из подсказки сначала создают на копии и повторяют тот же прогон. Если цели для сравнения разъехались по числу документов или по индексам, PLGM останавливает сравнение ещё до первой операции, и красивого графика «кто быстрее» вы не получите. Консольный запуск без --webui разбор запросов не делает: explain после прогона есть только в веб-интерфейсе.

Официальные цифры новой версии MongoDB отвечают на вопрос, как она выглядит на стенде вендора. PLGM отвечает на другой: что эта версия сделает с вашими запросами, если оба сервера увидят одну и ту же работу.

Источники и ссылки


Комментарии загружаются…