En finir avec Raft : du consensus ZooKeeper à Lux Quasar, nativement
Comment Hanzo Datastore remplace le moteur NuRaft sous ClickHouse Keeper par Lux Quasar — consensus post-quantique sans leader, lié directement dans le serveur C++ — tout en gardant l'API ZooKeeper que parle la réplication.

Sur cette page
Hanzo Datastore coordonne aujourd'hui les tables répliquées comme ClickHouse : un Keeper compatible ZooKeeper, embarqué, qui fait tourner Raft dans le processus du serveur. Cela fonctionne. C'est aussi le dernier composant classique, à leader et pré-quantique, de la pile. Cet article raconte comment nous le supprimons — et pourquoi « tuer ZK » ne veut pas dire réécrire la réplication.
Démêler Keeper : l'API face au moteur
Le réflexe, en entendant « remplacer ZooKeeper », est d'imaginer qu'il faut toucher chaque Replicated*MergeTree. Vous n'avez pas à le faire, parce que ClickHouse Keeper est déjà deux choses séparables tressées dans un seul démon :
-
L'API ZooKeeper — un arbre clé-valeur linéarisable, avec des nœuds séquentiels, des versions et des watches. Dans les sources, c'est
KeeperStorage, enveloppé parKeeperStateMachine(src/Coordination/KeeperStateMachine.h,class IKeeperStateMachine : public nuraft::state_machine). C'est le contrat. Chaque table répliquée, chaque requêtesystem.zookeeper, chaque élection de leader le parle. -
Le moteur de consensus — ce qui prend un flux de requêtes d'écriture, obtient qu'un quorum de nœuds s'accorde sur leur ordre, puis appelle
commit(log_idx, data)sur la machine à états dans cet ordre convenu. Aujourd'hui, c'est NuRaft :KeeperServer::launchRaftServer(src/Coordination/KeeperServer.cpp) construit unnuraft::raft_server, unasio_service, unKeeperStateManageret unKeeperLogStore.
L'observation nette — celle de Rich Hickey — est que KeeperStateMachine est déjà écrite contre l'interface de consensus générique (nuraft::state_machine : pre_commit, commit, rollback, instantanés). Elle ne sait pas, et ne se soucie pas, que Raft soit en dessous. Le moteur est une valeur que vous pouvez remplacer, pas un point soudé à l'API.
Donc : gardez la machine à états, remplacez le moteur. L'API ZooKeeper dont dépend la réplication survit intacte. Ce qui meurt, c'est NuRaft, l'élection de leader, et l'obligation de faire tourner un quorum Raft à part.
Ce qui le remplace
Le moteur devient Quasar — le consensus sans leader, fondé sur un DAG et post-quantique de Lux. Le travail de Quasar ici est identique à celui de Raft : s'accorder sur l'ordre d'un journal et signaler quand une entrée est finale. La différence est dans la manière de s'accorder — pas de leader unique vers lequel basculer, des signatures ML-DSA à la place des signatures classiques, et une finalité par seuil de confiance plutôt qu'une majorité d'un terme fixe.
Le point décisif : Quasar n'est pas un service Go vers lequel nous lançons un processus. Il livre un moteur C natif avec une ABI prête pour C++ (libluxconsensus). Le serveur Datastore est en C++. Le moteur se lie donc dans le même processus — pas de sidecar, pas de saut réseau vers un coordinateur, pas de second langage à l'exécution.
La couture est prouvée — contre la vraie machine à états
Ce n'est pas une esquisse, et ce n'est plus un modèle. Nous avons construit un programme qui lie la bibliothèque réelle du serveur ClickHouse (libdbms.a) et pilote la KeeperStateMachine<KeeperMemoryStorage> de production — la classe exacte par laquelle ReplicatedMergeTree se coordonne — entièrement sur Quasar, avec NuRaft hors du chemin d'ordonnancement. Il vit dans l'arbre Datastore, à src/Coordination/examples/keeper_quasar_poc.cpp, et il se compile et s'exécute sur un hôte Linux réel (aarch64, clang-21).
Le flux est fidèle à la façon dont le répartiteur pilote NuRaft aujourd'hui — deux phases, preprocess quand une requête est ajoutée au journal et commit quand le consensus fige son ordre :
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
Lancé contre une séquence de coordination ReplicatedMergeTree de dix requêtes — enregistrement de réplique, incréments du numéro de bloc, élection de leader — il prétraite chaque requête, les ordonne via Quasar, finalise, commit dans le KeeperStorage réel et vérifie que l'arbre résultant est exactement le bon, appliqué une fois chacun, dans l'ordre du consensus :
== REAL Keeper state machine reached consensus on Quasar — zero Raft, zero ZK ensemble ==
En dessous se trouve la démonstration minimale de l'ABI C (pkg/c/examples/cpp_consumer.cpp, make test-cpp), qui lie libluxconsensus et finalise un seul bloc — le même moteur, sans la dépendance ClickHouse. Les deux sont reproductibles ; l'exemple Datastore est optionnel (-DENABLE_EXAMPLES=1 -DLUXCONSENSUS_DIR=...) et ne fait jamais partie d'une compilation normale.
Posez-le sur Keeper, terme à terme :
| Keeper / NuRaft aujourd'hui | Natif Quasar |
|---|---|
nuraft::raft_server ordonne le journal | lux_chain_t ordonne le journal |
KeeperLogStore / Changelog | le journal DAG de Quasar |
| élection de leader Raft | vote de confiance sans leader |
state_machine->commit(idx, data) | le même appel, déclenché sur is_accepted |
| signatures classiques, leader à terme fixe | signatures ML-DSA, pas de leader |
| un quorum ZK/Keeper séparé à opérer | consensus dans le processus, dans le serveur |
KeeperStateMachine::commit est le point de jonction. NuRaft l'appelle aujourd'hui ; le pilote Quasar appelle la méthode identique demain. La machine à états ne peut pas faire la différence — et c'est exactement la propriété qui fait de ceci un remplacement, pas une réécriture.
Pourquoi cela compte au-delà du « post-quantique »
- Pas de leader. Raft a un seul leader élu par terme ; sa chute bloque les écritures jusqu'à la réélection. L'accord sans leader de Quasar retire cette dépendance de tête de ligne.
- Pas de second quorum à faire tourner. La coordination cesse d'être un système à part que vous provisionnez, surveillez et sauvegardez. C'est le serveur.
- Post-quantique par construction. Les signatures qui protègent l'accord sont ML-DSA, pas des courbes classiques, si bien qu'un adversaire quantique futur ne peut pas forger les messages de consensus.
- Une seule histoire de transport. Cela se compose avec le reste de la direction — TCP/HTTP natif maintenant, ZAP ensuite, et les mêmes primitives Lux que les chaînes utilisent.
Où nous en sommes, sans détour
L'architecture ci-dessus est réelle, et la preuve tourne déjà contre la KeeperStateMachine de production — pas un modèle —, compilée et au vert sur un hôte Linux réel. Ce qui reste, c'est le remplacement dans le moteur : introduire le backend de consensus Quasar à l'intérieur de KeeperServer, à côté de NuRaft, en faire le choix par défaut, puis y porter le reste de ce que fait KeeperServer — instantanés, reprise, expiration de session, reconfiguration — avec toute la suite de tests Coordination au vert. C'est du vrai travail, et nous n'allons pas vous dire que Raft a disparu le jour où nous avons prouvé le chemin de commit. Nous vous disons que la couture tient contre les vrais types, que le point de jonction est KeeperStateMachine::commit, et que toute la partie se joue là.
Quand cela atterrit, contrib/NuRaft/ et le moteur Raft quittent l'arbre. L'API ZooKeeper reste tant que la réplication la parle. C'est ainsi que l'on supprime le dernier composant pré-quantique sans toucher un seul MergeTree.
- Datastore : github.com/hanzoai/datastore
- Consensus Lux : github.com/luxfi/consensus
Lire la suite

Voici Hanzo Datastore : un moteur OLAP prêt pour le post-quantique
Un entrepôt en colonnes dérivé de ClickHouse, reconstruit autour de la séparation stockage et calcul sur Hanzo S3, optimisé par profils et à l'édition de liens jusqu'à environ la moitié de la mémoire, sans gRPC, avec une voie vers une coordination post-quantique sans leader.

Le routeur est le registre
Hanzo Cloud n'a aucun fichier OpenAPI versionné. La table de routes vivante est la source, projetée de trois façons, et un test de bijection fait échouer la compilation si une projection dérive. 1,362 opérations sur 963 paths, générées par processus au moment de la requête.

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.