Шардинг
Страница Shards — как читать состояние шардов, добавлять и удалять шарды, переносить заказчиков между шардами.
Зачем нужны шарды
Каждая запись в Raft-группу проходит через её лидера. Пропускная способность одной группы ограничена скоростью записи на диск и вычислительные операции ее лидера. Шарды снимают это ограничение: у каждого шарда свой лидер, лидеры распределяются по нодам, и записи идут параллельно увеличивая пропускную способность и надежность кластера.
Шард — это Raft-группа данных плюс заказчики, которые ей принадлежат. Заказчик переносится на шард целиком:
| Данные | Где хранятся |
|---|---|
| Заказчик (customer) | На своём шарде |
| Команды (teams) | На шарде своего заказчика |
| Виртуальные ключи | На шарде своего заказчика |
| Бюджеты и rate limit'ы | На шарде своего владельца |
Поэтому списание по цепочке «виртуальный ключ → команда → заказчик» остаётся одной атомарной записью на одном шарде.
Кластерная конфигурация с поддержкой фактора репликации меньше чем количество узлов будет поддержана в будущих релизах Meridian.
Страница Shards
Страница Cluster → Shards (/workspace/cluster/shards). Данные обновляются каждые 5 секунд, кнопка обновления рядом с Add запрашивает их сразу.
Список шардов
Левая панель — таблица шардов.
| Колонка | Значение |
|---|---|
| ID | Идентификатор шарда. |
| State | Состояние шарда. См. Статусы шардов. |
| Customers | Число заказчиков на шарде. Для orphaned — прочерк. |
Щелчок по строке открывает шард в правой панели. Меню ⋯ в строке содержит действие Delete.
Карточка шарда
Правая панель — выбранный шард.
| Поле | Значение |
|---|---|
| ID | Идентификатор шарда. |
| State | Состояние шарда. |
| Quorum | yes (3/3 healthy voters) — кворум есть: живо 3 голосующих реплики из 3. no (…) — кворум потерян. unknown или — — данных о здоровье пока нет. |
| conf_ver | Версия конфигурации шарда. Растёт, когда на шард или с него переносятся заказчики. |
| next_replica_id | Идентификатор, который получит следующая реплика шарда. |
| created_at, updated_at | Время создания и последнего изменения. |
Ниже на карточке:
- Replicas — реплики шарда: Replica ID, Node, Role (
voterилиlearner). - Ownership — сколько на шарде заказчиков (Customers), команд (Teams) и виртуальных ключей (Virtual keys).
- Migrations — миграции, в которых участвует шард: направление (на шард или с шарда), заказчик, откуда и куда, фаза, возраст. Метка
stuckотмечает зависшую миграцию.
Добавление шарда
Нажмите Add над списком шардов.
Введите Shard ID, например shard1. Допустимы латинские буквы, цифры, ., _ и -, первый символ — буква или цифра, длина до 64 символов. Имя _meta зарезервировано.
Нажмите Add shard.
Что происходит дальше:
- Кластер сохраняет новую версию топологии. Шард размещается на активных нодах, но не больше
max_replicasиз правила размещения. - Лидер
_metaв фоне создаёт Raft-группу на выбранных нодах. - Когда версия становится Active, шард появляется в маршрутизации и готов принимать заказчиков.
Сразу после создания в поле Quorum может стоять —: группа ещё собирается. Через несколько секунд там появится число живых реплик.
Если шаг создания не удался, кластер откатывает объявление шарда новой версией топологии. Обе версии видны в журнале.
Типичные ошибки:
| Сообщение | Причина | Что делать |
|---|---|---|
Your license does not include multi-shard clustering… | Лицензия без sharding. | Обновите лицензию. |
The cluster topology changed since this page loaded… | Пока открыт диалог, версия топологии изменилась. | Повторите через несколько секунд. |
| Шард уже существует | Идентификатор занят. | Выберите другой ID. |
Удаление шарда
⋯ → Delete в строке шарда. Удаление отклоняется, если:
- это
shard0; - на шарде есть заказчики — сначала перенесите их на другие шарды;
- шард участвует в незавершённой миграции.
Удаление останавливает реплики на всех нодах и удаляет данные шарда. Вернуть удалённый шард можно только созданием нового и переносом заказчиков. Для удаления нужно право cluster.shard.delete.
Статусы шардов
Состояние шарда складывается из трёх независимых признаков.
Состояние в документе — колонка State
| State | Значение | Что делать |
|---|---|---|
active | Шард объявлен в Active-версии топологии и обслуживается. | Нормальное состояние. |
orphaned | Шард остался в базе данных, но отсутствует в Active-версии топологии. Обычно это след незавершённого удаления или неудачного создания. | Удалите шард через ⋯ → Delete — удаление дочищает оставшиеся данные. |
В документе топологии есть значение stopped, но оно зарезервировано и не применяется. Остановить шард нельзя.
Здоровье группы — поле Quorum
Здоровье пересчитывается каждые 5 секунд по данным gossip о живых нодах. Кворум — voters / 2 + 1.
| Здоровье | Как выглядит | Значение | Что делать |
|---|---|---|---|
healthy | yes (3/3 healthy voters) | Живы все голосующие реплики. | Нормальное состояние. |
degraded | yes (2/3 healthy voters), метка degraded на странице Status | Кворум есть, но часть реплик недоступна. Шард работает, запас отказоустойчивости снижен. | Верните недоступную ноду. Если нода потеряна, выведите её через Drain и Exclude и добавьте замену — реплики пересоберутся на живых нодах. |
quorum_lost | no (1/3 healthy voters), метка quorum lost | Живых голосующих реплик меньше кворума. Шард не принимает записи: запросы заказчиков этого шарда отклоняются. Ноды перестают принимать трафик (Not serving). | Срочно верните недоступные ноды. Авто-восстановление такой шард не трогает. Если ноды потеряны безвозвратно, используйте аварийные инструменты — см. Эксплуатация. |
unknown | unknown или — | Здоровье ещё не вычислено: нода только стартовала или группа только создана. | Подождите несколько секунд. Если состояние не меняется, проверьте, что gossip видит ноды на странице Status. |
Локальная реплика на ноде
Нода может не запустить свою реплику шарда. Такое состояние видно в поле local_replica ответа GET /api/cluster/v1/shards/{shard_id} и в local_shards ответа GET /api/cluster/v1/status.
| Состояние | Значение | Что делать |
|---|---|---|
running | Реплика работает. | Нормальное состояние. |
parked_store_missing | На ноде нет каталога данных реплики. Возможно, volume подмонтирован не туда. | Проверьте монтирование data_dir. Если данные действительно потеряны — выполните reseat. |
parked_store_unreadable | Данные реплики есть, но повреждены. | Выполните reseat. |
parked_identity_mismatch | Данные принадлежат другой ноде, шарду или кластеру. | Проверьте, что каталог подключён к правильной ноде. Затем выполните reseat. |
Reseat выдаёт реплике новый идентификатор, и кворум заново реплицирует её данные:
curl -X POST http://localhost:8080/api/cluster/v1/groups/shard1/replicas/node2/reseatОперация необратима: данные старой реплики отбрасываются.
Миграция заказчика между шардами
Миграция переносит заказчика на другой шард вместе с командами, виртуальными ключами, бюджетами и rate limit'ами.
Когда нужна:
- Балансировка. Заказчик с высокой нагрузкой переезжает на свежий шард.
- Изоляция. Важный заказчик получает отдельный шард.
- Удаление шарда. Перед удалением все заказчики переносятся на другие шарды.
Запуск миграции
Миграция запускается со страницы заказчика, а не со страницы Shards.
Откройте Governance → Customers и выберите заказчика.
В блоке Danger Zone найдите строку Shard. Там указан текущий шард заказчика. Нажмите Migrate.
В поле Target shard выберите шард назначения. В списке нет текущего шарда и шардов в состоянии orphaned.
Отметьте флажок I understand that in-flight requests may fail with 5xx errors while the migration runs и нажмите Migrate.
Кнопка Migrate недоступна, если у заказчика уже идёт миграция, нет подходящего шарда назначения или нет права cluster.shard.migrate.
Во время переноса бюджетов отдельные запросы заказчика могут завершиться ошибкой 5xx. Запускайте миграцию в период низкой нагрузки.
Отслеживание
Прогресс виден на странице Shards в блоке Migrations карточки шарда — исходного или шарда назначения. Строка показывает фазу и возраст миграции. Страница заказчика тоже показывает, что миграция идёт и в какой она фазе.
Пока миграция не завершена:
- изменения заказчика, его команд и виртуальных ключей отклоняются с кодом
409— повторите их после завершения; чтение работает; - исходный шард и шард назначения нельзя удалить;
- для этого заказчика нельзя запустить вторую миграцию. Разные заказчики мигрируют параллельно.
Фазы миграции
Перенос разбит на группы: по одной на каждую команду заказчика и одна на сам заказчик. Миграция переходит в следующую фазу, когда её достигли все группы.
| Фаза | Цвет | Что происходит |
|---|---|---|
planned | серый | Миграция создана и ждёт запуска. |
frozen | серый | На исходном шарде запрещены изменения бюджетов переносимых групп. |
snapshotted | серый | Состояние бюджетов сохранено в снимок. |
applied | серый | Снимок импортирован на шард назначения. |
published | серый | Назначение заказчика переключено на новый шард. Точка невозврата. |
committed | зелёный | Миграция завершена. На исходном шарде установлено перенаправление для поздних запросов. |
aborted | красный | Миграция отменена. Заказчик остался на исходном шарде. |
failed | красный | Миграция не завершилась и остановлена автоматически. Заказчик остался на исходном шарде. |
Метка stuck появляется, если миграция не завершилась за 30 минут.
Если миграция зависла или упала
Кластер сам повторяет незавершённые миграции: каждые 30 секунд он продолжает до 10 миграций, которые не продвигались дольше 30 секунд. Каждый шаг идемпотентен, повтор безопасен. Текст последней ошибки хранится в поле error_message миграции.
| Ситуация | Что происходит | Что делать |
|---|---|---|
| Ошибка шага, миграция младше 30 минут | Фаза не меняется, кластер повторяет шаг. | Подождите. Проверьте, что у обоих шардов есть кворум. |
Старше 30 минут в planned, frozen или snapshotted | Миграция автоматически отменяется и получает фазу failed. | Устраните причину и запустите миграцию заново. |
Старше 30 минут в applied или published | Автоматической отмены нет, миграция помечена stuck. | Восстановите кворум шардов и повторите миграцию через API retry. |
В интерфейсе нет кнопок отмены и повтора — используйте API:
# Повторить с текущей фазы
curl -X POST http://localhost:8080/api/cluster/v1/migrations/{migration_id}/retry
# Отменить (до snapshotted включительно)
curl -X POST http://localhost:8080/api/cluster/v1/migrations/{migration_id}/abort
# Аварийная отмена (до applied включительно)
curl -X POST "http://localhost:8080/api/cluster/v1/migrations/{migration_id}/abort?force=true"После published отменить миграцию нельзя. Чтобы вернуть заказчика, запустите обратную миграцию. История завершённых миграций хранится 30 дней.
API
Создание и удаление шарда требуют заголовка If-Match с текущей Latest-версией топологии: без него — 428, с устаревшей версией — 412. Параметр dry_run=true возвращает план без изменений. Миграции недоступны без PostgreSQL: сервер отвечает 501.
Полная спецификация — в справочнике API.