DDL is being applied before _bucket space is created on new replicasets
Проблема
Если добавить новый репликасет в кластер после выполнения кластерного CREATE TABLE, то на нём сломается схема данных, а именно системная таблица _bucket будет иметь id не такой как на остальном кластере.
Репро есть в тестах на этой ветке !1451
Пояснения
Системная таблица _bucket, в которой отслеживаются расположенные на текущем инстансе бакеты, создаётся при первой настройке шардирования по вызову RPC от губернатора. Губернатор настраивает шардирование после того как видит, что топология кластера изменилась (добавились живые инстансы). Об этом губернатор узнаёт из глобальных таблиц, которые реплицируются через рафт журнал.
При этом создание таблиц так же реализовано через рафт журнал, это происходит в момент применения соответсвтующей DdlCommit операции на инстансе добавляющемся в кластер.
В итоге наблюдаем примерно такую картину:
- replicaset r1: стартует
- governor: RPC proc_sharding -> r1
- r1[proc_sharding]: vshard.storage.cfg() =>
create table _bucket with id = 532
(max_id + 1) - user: CREATE TABLE customers
- governor: RPC proc_apply_schema_change -> r1
- r1[proc_apply_schema_change]
create table customers with id = 1025
(первый в диапазоне для пользовательских таблиц) - governor: INSERT ddl_commit INTO _raft_log
- replicaset r2: стартует
- r2[raft_main_loop] apply ddl_commit =>
create table customers with id = 1025
(первый в диапазоне для пользовательских таблиц) - governor: RPC proc_sharding -> r2
- r2[proc_sharding]: vshard.storage.cfg() =>
create table _bucket with id = 1026
(max_id + 1)
После этого кластер сломан, создать таблицу будет невозможно, репликасет r2 будет возвращать ошибку из proc_apply_schema_change из-за конфликта по айди таблицы.
Эта проблема в частности вызвала инцидент #1128 (closed)
Потенциальное решение
1. Создавать таблицу _bucket явно самим
В идеале нам стоит создавать таблицу _bucket в момент инициализации кластера на ровне с остальными системные таблицами pico*. Однако сделать это будет очень сложно. В vshard эта таблица создаётся внутри функции schema_init_0_1_15_0
, которую теоретически (но возможно и нет) можно вызывать напрямую как vshard.storage.internal.schema_bootstrap
. При чём вызывать эту функцию можно только на мастер репликах, что вызывает проблему, т.к. такого рода рафт операций у нас ещё нет. Есть похожий DdlCommit, но из коробки он не заработает, придётся допиливать.
Можно добавить специальную рафт операцию (которая пойдёт в bootstrap entries), применяя которую мастер должен будет вызвать vshard.storage.internal.schema_bootstrap
, а read-only реплика будет дожидаться репликации так же как при DdlCommit. При этом нужно будет добавить ещё какой-то костыль в raft snapshot, т.к. по умолчанию в нём не будет следов этой таблицы.
Это всё при условии, что у нас не возникнут проблемы с вызовом vshard.storage.internal.schema_bootstrap
напрямую.
2. Прокидывать id таблицы _bucket в вшард
Другой вариант: пропатчить вшард и добавить возможность передавать айди таблицы явно. Этот айди мы зафиксируем константой в коде пикодаты. Но это потребует делать патч в вшард, чего мы ещё не делали и делать не хотим.