Присоединяйтесь к бете Hanzo и получите скидку 50% на любой платный тариф

Отказываемся от Raft: нативный консенсус Lux Quasar на смену ZooKeeper

Как Hanzo Datastore заменяет движок NuRaft под ClickHouse Keeper на Lux Quasar — постквантовый консенсус без лидера, влинкованный прямо в сервер на C++, — и сохраняет API ZooKeeper, на котором говорит репликация.

На этой странице

Hanzo Datastore координирует реплицируемые таблицы сегодня так же, как ClickHouse: встроенный, совместимый с ZooKeeper Keeper, который гоняет Raft внутри процесса сервера. Это работает. Это ещё и последний классический, с лидером и доквантовый компонент стека. Этот пост — о том, как мы его убираем, и почему «убить ZK» не значит переписать репликацию.

Расцепляем Keeper: API против движка

Рефлекс на слова «заменить ZooKeeper» — представить, что придётся трогать каждый Replicated*MergeTree. Не придётся, потому что ClickHouse Keeper — это уже две отделимые вещи, сплетённые в один демон:

  1. API ZooKeeper — линеаризуемое дерево ключ-значение с последовательными узлами, версиями и watches. В исходниках это KeeperStorage, обёрнутый KeeperStateMachine (src/Coordination/KeeperStateMachine.h, class IKeeperStateMachine : public nuraft::state_machine). Это контракт. Каждая реплицируемая таблица, каждый запрос system.zookeeper, каждые выборы лидера говорят на нём.

  2. Движок консенсуса — то, что берёт поток запросов на запись, добивается согласия кворума узлов об их порядке и затем вызывает commit(log_idx, data) на машине состояний в этом согласованном порядке. Сегодня это NuRaft: KeeperServer::launchRaftServer (src/Coordination/KeeperServer.cpp) собирает nuraft::raft_server, asio_service, KeeperStateManager и KeeperLogStore.

Чистое наблюдение Рича Хики в том, что KeeperStateMachine уже написана против общего интерфейса консенсуса (nuraft::state_machine: pre_commit, commit, rollback, снимки). Ей не известно и не важно, что под ней Raft. Движок — значение, которое можно подменить, а не место, приваренное к API.

Итак: оставьте машину состояний, замените движок. API ZooKeeper, от которого зависит репликация, остаётся нетронутым. Умирают NuRaft, выборы лидера и требование гонять отдельный кворум Raft.

Что встаёт вместо

Движком становится Quasar — консенсус без лидера, на DAG, постквантовый, из Lux. Работа Quasar здесь та же, что у Raft: договориться о порядке журнала и подать сигнал, когда запись финальна. Разница в том, как достигается согласие — нет единственного лидера, на которого переключаются, подписи ML-DSA вместо классических, и финальность по порогу уверенности, а не большинство фиксированного срока.

Решающее: Quasar — не Go-сервис, к которому мы порождаем процесс. Он поставляет нативный движок на C с ABI, готовым к C++ (libluxconsensus). Сервер Datastore — на C++. Движок линкуется в тот же процесс — без sidecar, без сетевого прыжка к координатору, без второго языка в рантайме.

Шов доказан — на настоящей машине состояний

Это не набросок и уже не модель. Мы собрали программу, которая линкует настоящую библиотеку сервера ClickHouse (libdbms.a) и ведёт промышленную KeeperStateMachine<KeeperMemoryStorage> — ровно тот класс, через который координируется ReplicatedMergeTree, — целиком на Quasar, а NuRaft нет на пути упорядочивания. Она живёт в дереве Datastore по пути src/Coordination/examples/keeper_quasar_poc.cpp и собирается и запускается на настоящем хосте Linux (aarch64, clang-21).

Поток верен тому, как диспетчер ведёт NuRaft сегодня — две фазы, preprocess, когда запрос дописывается, и commit, когда консенсус фиксирует его порядок:

state_machine->preprocess(rfs, /*lock_mutex*/ true);     // append-time, pre-consensus (≙ nuraft pre_commit)
auto buf = IKeeperStateMachine::getZooKeeperLogEntry(rfs); // the real ZooKeeper log entry
lux_chain_add_block(chain, &blk);   // the entry rides Quasar (the slot raft_server occupies)
lux_consensus_process_vote(...);    // validators agree — no leader
lux_consensus_is_accepted(chain, id, &accepted);
state_machine->commit(height, *buf); // finality → the real KeeperStorage mutates, in order

Прогнанная на последовательности координации ReplicatedMergeTree из десяти запросов — регистрация реплики, сдвиги номера блока, выборы лидера — она предобрабатывает каждый запрос, упорядочивает их через Quasar, финализирует, коммитит в настоящий KeeperStorage и проверяет, что получившееся дерево в точности верно, каждый элемент применён один раз, в порядке консенсуса:

== REAL Keeper state machine reached consensus on Quasar — zero Raft, zero ZK ensemble ==

Под ней — минимальная демонстрация C-ABI (pkg/c/examples/cpp_consumer.cpp, make test-cpp), которая линкует libluxconsensus и финализирует один блок — тот же движок, без зависимости от ClickHouse. Обе воспроизводимы; пример Datastore необязателен (-DENABLE_EXAMPLES=1 -DLUXCONSENSUS_DIR=...) и никогда не входит в обычную сборку.

Разложите это на Keeper один к одному:

Keeper / NuRaft сегодняНативно на Quasar
nuraft::raft_server упорядочивает журналlux_chain_t упорядочивает журнал
KeeperLogStore / ChangelogDAG-журнал Quasar
выборы лидера Raftголосование уверенности без лидера
state_machine->commit(idx, data)тот же вызов, по событию is_accepted
классические подписи, лидер фиксированного срокаподписи ML-DSA, без лидера
отдельный кворум ZK/Keeper, который надо вестиконсенсус в процессе, в сервере

KeeperStateMachine::commit — точка стыка. NuRaft вызывает её сегодня; драйвер Quasar вызовет тот же метод завтра. Машина состояний не отличит одно от другого — и именно это свойство делает замену заменой, а не переписыванием.

Почему это важно не только как «постквантовое»

  • Нет лидера. У Raft один избранный лидер на срок; его отказ останавливает записи до перевыборов. Согласие Quasar без лидера снимает эту зависимость головы очереди.
  • Нет второго кворума, который надо вести. Координация перестаёт быть отдельной системой, которую вы разворачиваете, мониторите и бэкапите. Это и есть сервер.
  • Постквантовость заложена в конструкцию. Подписи, которые защищают согласие, — это ML-DSA, не классические кривые, поэтому будущий квантовый противник не подделает сообщения консенсуса.
  • Одна история транспорта. Она складывается с остальным направлением — нативные TCP/HTTP сейчас, ZAP следом, и те же примитивы Lux, которыми пользуются цепочки.

Честный статус

Архитектура выше настоящая, и доказательство уже идёт против промышленной KeeperStateMachine — не модели, — собранное и зелёное на настоящем хосте Linux. Остаётся замена внутри движка: ввести бэкенд консенсуса Quasar внутри KeeperServer рядом с NuRaft, сделать его выбором по умолчанию и перенести на него остальное из того, что делает KeeperServer — снимки, восстановление, истечение сессии, реконфигурацию — с зелёным полным набором тестов Coordination. Это настоящая работа, и мы не скажем вам, что Raft исчез в тот день, когда доказали путь commit. Мы говорим, что шов держит настоящие типы, что точка стыка — KeeperStateMachine::commit, и что вся партия играется там.

Когда это встанет, contrib/NuRaft/ и движок Raft уйдут из дерева. API ZooKeeper останется, пока репликация на нём говорит. Так убирают последний доквантовый компонент, не тронув ни одного MergeTree.

Комментарии

Войдите с Hanzo ID, чтобы комментировать.

Загрузка комментариев…

Читать далее