Когда говорят "обновить Tarantool", смешивают два независимых процесса, и путаница между ними - источник большинства аварий:
- Обновление бинарника (версии) - вы ставите новый пакет tarantool, останавливаете инстанс и стартуете его на новой версии. Сами данные (snapshot и WAL) при этом не трогаются - они читаются как есть.
- Обновление схемы (data layout) - это апгрейд системных спейсов (_space, _index, _user, _func, _cluster и прочих служебных _*-таблиц), чтобы база начала использовать новые возможности формата. Это делается ОДНОЙ командой box.schema.upgrade() уже после того, как все инстансы перешли на новый бинарник.
Механика rolling upgrade: почему без даунтаймаБинарный формат, клиент-серверный протокол (iproto) и протокол репликации совместимы между 2.x и 3.x. Именно эта совместимость и делает rolling upgrade возможным: старая и новая версии в одном кластере временно сосуществуют и реплицируются друг в друга.
Rolling upgrade ("перекатывание") опирается на избыточность кластера. Пока вы гасите один инстанс, его работу подхватывает другой: для шардированного стораджа - реплика того же replica set, для роутера - любой другой роутер. Идём по инстансам по одному, и в каждый момент времени кластер обслуживает запросы.
Порядок обновления (для vshard-кластера):
- Сначала роутеры - они stateless, у них нет своих данных, схему апгрейдить не нужно. Гасим и поднимаем по одному.
- Потом стораджи, по одному replica set за раз. Внутри набора - сначала read-only реплики, в конце мастер.
- Перезапускаем на новой версии read-only реплику (по одной), ждём box.info.status == running.
- Перезапускаем остальные read-only реплики.
- Делаем одну из обновлённых реплик новым мастером (переключение мастера - см. ниже), чтобы старый мастер тоже можно было перезапустить.
- Перезапускаем бывший мастер (теперь реплику) на новой версии.
- Только теперь на новом мастере выполняем box.schema.upgrade(). Изменения системных спейсов разъезжаются на остальные узлы обычной репликацией.

Rolling upgrade кластера и апгрейд схемы
Переключение мастера во время перекатывания
Чтобы перезапустить мастер, его роль надо временно отдать обновлённой реплике. Способ зависит от того, как устроен failover:
Raft (автоматические выборы лидера):
Код: Выделить всё
-- на кандидате (read-only реплике):
box.ctl.promote() -- запускает выборы и ждёт
-- на старом мастере:
box.cfg{ election_mode = 'voter' } -- он больше не претендует на лидерство
-- проверка на кандидате:
box.info.ro -- должно стать false
Код: Выделить всё
-- на текущем мастере:
box.cfg{ read_only = true }
-- на кандидате проверяем, что он догнал мастера:
-- box.info.vclock[master_id] == box.info.lsn (взятый на мастере)
Схема: upgrade и downgrade
Код: Выделить всё
box.schema.upgrade() -- поднять версию схемы до версии бинарника
box.schema.downgrade(version) -- откатить схему к указанной версии
box.schema.downgrade_versions() -- список версий, на которые можно откатиться
box.schema.downgrade_issues(ver) -- что мешает откату на ver (какие новые фичи задействованы)
Код: Выделить всё
tarantool> box.internal.schema_version and box.space._schema:get{'version'}
-- или просто посмотреть box.info.schema_version (в свежих 3.x)
Откат после точки невозврата (на каждом replica set):
Код: Выделить всё
box.schema.downgrade('2.11.0') -- на мастере, указываем исходную версию
box.snapshot() -- на КАЖДОМ инстансе набора
-- далее по одному рестартуем реплики на старом бинарнике,
-- переключаем мастера, рестартуем бывший мастер
Двойной трек: box.cfg (1.x/2.x) и декларативный 3.x
Сама последовательность rolling upgrade одинакова для обоих стилей конфигурации - меняется только то, КАК вы рестартуете инстанс на новой версии.
Классический box.cfg. Вы запускаете тот же init.lua/instance-файл новым бинарником. Никаких изменений в коде конфигурации не требуется, кроме совместимости приложения.
Декларативный 3.x (config.yaml + tt). Конфигурация лежит в YAML, инстансы поднимаются через tt. Обновление бинарника - это переустановка версии и rolling-перезапуск инстансов (tt restart по одному). Схема всё так же поднимается явным box.schema.upgrade() на лидере набора - декларативная конфигурация автоматически версию схемы НЕ двигает.
Модуль compat - мост между версиями. При мажорном апгрейде часть поведения меняется. Модуль compat позволяет временно оставить старое поведение опции и переключаться на новое по одной, тестируя постепенно, а не "всё сразу":
Код: Выделить всё
tarantool> require('compat').<option_name>
-- даёт 'old' / 'new' / 'default'; можно зафиксировать:
require('compat').<option_name> = 'old'
Частые заблуждения и грабли
- "Обновил пакет - схема обновилась сама". Нет. box.schema.upgrade() надо вызвать руками. Без него вы на новом бинарнике, но без новых фич формата.
- Запуск box.schema.upgrade() на каждом инстансе. Не нужно и опасно: вызываем только на мастере, остальное разъезжается репликацией.
- Upgrade схемы, когда часть узлов ещё на старой версии. Старые узлы могут упасть на новых записях системных спейсов. Сначала весь набор на целевую версию.
- Забыли box.snapshot() после upgrade/downgrade. Тогда после рестарта реплика восстанавливается из старых WAL и видит не то состояние. Снапшот фиксирует новое состояние схемы.
- Прыжок 1.6/1.7/1.9 -> 2.x напрямую без даунтайма. Невозможно без промежуточного шага через 1.10.
- Не сверили vclock при ручном переключении мастера. Можно потерять последние транзакции.
- Не выключили rebalancer/failover перед обновлением стораджей. vshard.storage.rebalancer_disable() и отключение failover - иначе кластер начнёт двигать бакеты прямо во время перекатывания.
На локальном инстансе (standalone, любой 2.11+ или 3.x):
- Запустите инстанс, посмотрите box.info.schema_version (или box.space._schema:get{'version'}).
- Выполните box.schema.downgrade_versions() - изучите, до каких версий можно откатиться.
- Выберите версию из списка и выполните box.schema.downgrade_issues('<version>') - посмотрите, какие фичи (если есть) мешают откату.
- Сделайте box.schema.downgrade('<version>'), затем box.snapshot(), снова проверьте box.info.schema_version. Верните обратно box.schema.upgrade().
Контрольные вопросы
- Почему box.schema.upgrade() выполняется только один раз и только на мастере, а не на каждом инстансе replica set?
- Что такое "точка невозврата" при апгрейде и как откатиться, если вы её уже прошли?
- Зачем перед рестартом инстанса на старой версии (при downgrade) обязателен box.snapshot()?
- В каком порядке обновляются роутеры и стораджи и почему именно так - что обеспечивает работу без даунтайма?