Picodata: наблюдаемость и расширение кластера

Две стороны эксплуатации Picodata, проверенные на стенде: встроенный веб-интерфейс и экспорт метрик Prometheus включаются одним флагом, но кластерная в них только часть — остальное принадлежит опрошенному инстансу; и что происходит с кластером при добавлении узлов

Предыдущие статьи серии разбирали, что Picodata умеет. Эта — про две стороны эксплуатации, которые удалось проверить на стенде: что кластер сам рассказывает о себе и что происходит, когда его расширяют.

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

Пятая, заключительная статья серии. Всё ниже снято на стенде databases/picodata — кластер из четырёх инстансов, который по ходу дела вырастает до шести.

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

В статье

Наблюдаемость из коробки

Один флаг --http-listen поднимает сразу две вещи, и ни для одной не нужны плагины или отдельный экспортёр — проверено на кластере, где установленных плагинов ровно ноль.

Веб-интерфейс отдаётся на корне указанного порта: собственная страница со своими ассетами и конфигурацией. Полезен как быстрый взгляд на кластер, но полноценный мониторинг не заменяет.

Экспорт метрик в формате Prometheus — на /metrics. Сколько там семейств, зависит от того, что уже происходило в кластере: часть появляется только после первого события своего типа. На только что поднятом голом кластере — 140 на узле (на одном из четырёх — 139), после первых SQL-запросов добавляются pico_sql_query_total и pico_sql_query_duration, и выходит 142. Делятся они на три части (снимок одного узла свежего кластера):

  • pico_* (28 семейств, после первых запросов — 30) — то, что добавила сама Picodata: состояние Raft и инстансов, счётчики PostgreSQL-протокола, длительность и число распределённых запросов, кэш планов на роутере и на хранилищах, RPC-вызовы плагинов;
  • tnt_* (93) — ядро: память memtx, дисковый движок, сеть, процессор;
  • lj_* (19) — сборщик мусора LuaJIT.

Отдельного внимания заслуживают pico_pgproto_* — счётчики соединений, подготовленных выражений и порталов. Раз PostgreSQL-протокол здесь основной канал, наблюдать за ним стоит так же, как за пулом соединений в обычной СУБД.

Кластерная только часть метрик

Здесь легко ошибиться, и я ошибся сам, прежде чем перепроверил.

Часть метрик описывает кластер целиком. pico_instance_state перечисляет все инстансы с их состоянием и тиром, и картина одинакова, с какого узла её ни снимай. Соблазн сделать из этого вывод «достаточно опрашивать любой живой узел» велик — и он неверен.

Раскладка по памяти и движкам, сборщик мусора, роль в Raft и счётчики запросов относятся к тому инстансу, у которого вы забрали метрики. Проверяется это мгновенно — достаточно снять /metrics с разных узлов одного кластера:

:8081 — семейств 154, tnt_* 97, raft: default_1_1 Leader
:8082 — семейств 148, tnt_* 97, raft: default_2_1 Follower
:8083 — семейств 146, tnt_* 97, raft: default_2_2 Follower
:8084 — семейств 145, tnt_* 97, raft: default_1_2 Follower

Наборы различаются и по составу, и по значениям. Это разные измерения, а не разные представления одного. Документация Picodata требует того же — отдельный адрес сбора метрик под каждый инстанс, и пример конфигурации перечисляет все узлы как отдельные цели.

Заодно из этого снимка видно то, чего нет в SQL: роль узла в Raft. pico_raft_state, pico_raft_leader_id и pico_raft_term отдаются штатно, тогда как через SQL состояние лидера не получить. Практический вывод: административная консоль — инструмент разбора инцидента, а не мониторинга.

Что мониторить обязательно

Обычный набор — задержки, ошибки, насыщение — здесь тоже нужен, но есть вещь, специфичная для Picodata, и она важнее остальных.

Доступность репликасетов нужно отслеживать отдельно, и вот почему. Кластер переживает потерю одного узла в репликасете штатно: реплика подхватывает, запросы возвращают полный набор. А при потере обоих узлов репликасета запрос не падает — он возвращает неполный результат, без ошибки и без предупреждения:

count(*) = 133249   ← в кластере 200000

Ни исключения, ни признака, что часть данных недоступна. Приложение не включит запасной путь, отчёт построится, решение примется — на неполных данных. Механику подробно разбирает первая статья серии; здесь важно операционное следствие: нельзя выводить здоровье кластера из того, что запросы проходят. Смотреть надо на pico_instance_state — сколько инстансов в состоянии Online и все ли репликасеты имеют живых членов.

Ещё две вещи, которые стоит держать на виду:

  • Смена Raft-лидера. pico_raft_leader_id и pico_raft_term: рост терма без видимой причины — сигнал о нестабильности связности.
  • Кэш планов. pico_router_cache_* и pico_storage_cache_*: доля промахов показывает, насколько приложение параметризует запросы. Много промахов — значит планы строятся заново на каждый вызов.

Расширение кластера

Добавление узлов устроено приятно: новые инстансы подключаются к существующему кластеру, сами собираются в репликасет и получают вес — вручную ничего регистрировать не нужно.

default_1_1 | default_1 | ["Online",2]
default_1_2 | default_1 | ["Online",2]
default_2_1 | default_2 | ["Online",1]
default_2_2 | default_2 | ["Online",1]
default_3_1 | default_3 | ["Online",1]   ← новые
default_3_2 | default_3 | ["Online",1]

веса репликасетов: default_1 = 1 ready, default_2 = 1 ready, default_3 = 1 ready

Добавлять нужно парой — при факторе репликации 2 одиночный инстанс образует неполный репликасет и не получит бакетов вообще, ровно как три инстанса на старте.

Дальше бакеты переезжают на новый репликасет. На корректно инициализированном кластере это происходило сразу: при первой же проверке на новых узлах было по 1000 бакетов, и все три репликасета выровнялись — 3000 бакетов, по 1000 на каждый, данные целы.

Здесь стоит рассказать, чего эта проверка мне стоила, потому что вывод чуть не оказался противоположным. В части прогонов бакеты не двигались минутами, а в журнале стояло The cluster is balanced ok. Schedule next rebalancing after 3600.000000 seconds — и напрашивалось заключение, что расширение кластера операция отложенная. Заключение было бы неверным: те прогоны снимались либо с кластера, не закончившего инициализацию, либо — что обиднее — с прогонов, где новые узлы вообще не поднялись из-за ошибки в самом стенде, а пустой ответ узла я принимал за «ноль бакетов». После того как стенд научился ждать готовности кластера и отличать «узел молчит» от «бакетов ноль», перераспределение начиналось незамедлительно в каждом прогоне.

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

Формулировка «cluster is balanced ok» при пустом третьем репликасете здесь второй раз за серию оказывается ненадёжным индикатором: она означает «размещение соответствует текущему плану», а не «мощность используется равномерно».

Развёртывание

Штатных путей два, и оба задокументированы вендором: роли Ansible для установки на машины и оператор Kubernetes для кластерного окружения. На стенде проверялся вариант с контейнерами напрямую, поэтому про Ansible и оператор здесь — со ссылкой на документацию, а не по результатам измерения.

Что точно стоит знать про эксплуатацию до первого развёртывания:

  • Резервное копирование средствами PostgreSQL не работает. pg_dump падает на первом же запросе к системным каталогам — подробности в статье про границы совместимости. Механизмы у Picodata свои: SQL-команда BACKUP и отдельная команда picodata restore --path. Обе присутствуют в 26.1.3, и обе стендом не проверялись — попытка вызвать BACKUP на кластере, поднятом флагами командной строки, отвергается сообщением No config found for local backup (it was not found on instance start). Одного флага --backup-dir для этого недостаточно: резервное копирование настраивается через конфигурационный файл инстанса, а стенд запускает узлы флагами. Планировать восстановление нужно на этих механизмах, но проверять их придётся отдельно — здесь они не измерены.
  • Плагины версионированы вместе с кластером. Версия библиотеки плагина привязана к версии Picodata, значит обновление кластера означает пересборку плагинов — см. статью про плагины.
  • Кластерные лимиты придётся настраивать под нагрузку. Дефолты не рассчитаны на агрегацию по сотням тысяч строк; какие именно параметры и почему — в статье про распределённый SQL.

Чем платим

  • Мониторинг обходит все узлы. Один эндпоинт даёт срез кластера, но не заменяет опрос каждого инстанса.
  • Здоровье не выводится из успешности запросов. Потеря репликасета не проявляется как ошибка — только как неполный ответ.
  • Расширение требует дождаться переезда бакетов. Узлы принимаются в кластер сразу и сразу переходят в Online, но рабочими становятся, когда получат бакеты; проверять нужно именно это, а не статус инстанса.
  • Резервное копирование — не через привычный инструментарий PostgreSQL.
  • Взамен наблюдаемость действительно встроенная: ни экспортёра, ни агента, ни плагина — один флаг и обычный Prometheus.

Что дальше

На этом серия закончена: 1 — что такое Picodata, 2 — границы совместимости с PostgreSQL, 3 — распределённый SQL, 4 — плагины на Rust, 5 — эта статья.

Рядом стоит держать статью про Tarantool и Picodata — там та же идея вычислений рядом с данными показана в сравнении двух систем, и карту хранилищ, где Picodata стоит среди остальных подходов.

Обсуждение в Telegram

Присоединиться →

Комментарии