Отказываемся от 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 — это уже две отделимые вещи, сплетённые в один демон:
-
API ZooKeeper — линеаризуемое дерево ключ-значение с последовательными узлами, версиями и watches. В исходниках это
KeeperStorage, обёрнутыйKeeperStateMachine(src/Coordination/KeeperStateMachine.h,class IKeeperStateMachine : public nuraft::state_machine). Это контракт. Каждая реплицируемая таблица, каждый запросsystem.zookeeper, каждые выборы лидера говорят на нём. -
Движок консенсуса — то, что берёт поток запросов на запись, добивается согласия кворума узлов об их порядке и затем вызывает
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 / Changelog | DAG-журнал 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.
- Datastore: github.com/hanzoai/datastore
- Консенсус Lux: github.com/luxfi/consensus
Читать далее

Представляем Hanzo Datastore: OLAP-движок, готовый к постквантовой эпохе
Колоночное хранилище на основе ClickHouse, пересобранное вокруг разделения хранения и вычислений на Hanzo S3, с профильной оптимизацией и оптимизацией на этапе компоновки примерно до половины памяти, без gRPC и с путём к постквантовой координации без лидера.

Маршрутизатор и есть реестр
В Hanzo Cloud нет зафиксированного файла OpenAPI. Живая таблица маршрутов — источник, спроецированный тремя способами, и тест биекции роняет сборку, если любая проекция расходится. 1,362 операции на 963 путях, порождаются на процесс в момент запроса.

Jin 2.0: The Next Generation of Multimodal AI
Announcing Jin 2.0, a major upgrade to our unified multimodal AI architecture with expanded capabilities and improved performance.