Raft abschaffen: vom ZooKeeper-Konsens zu Lux Quasar, nativ
Wie Hanzo Datastore die NuRaft-Engine unter ClickHouse Keeper durch Lux Quasar ersetzt — postquantenfest, Konsens ohne Leader, direkt in den C++-Server gelinkt — und die ZooKeeper-API behält, die die Replikation spricht.

Auf dieser Seite
Hanzo Datastore koordiniert replizierte Tabellen heute so, wie ClickHouse es tut: ein eingebetteter, ZooKeeper-kompatibler Keeper, der Raft im Serverprozess laufen lässt. Es funktioniert. Es ist auch die letzte klassische, leader-basierte, präquante Komponente im Stack. Dieser Beitrag erzählt, wie wir sie beseitigen — und warum „ZK töten“ nicht heißt, die Replikation neu zu schreiben.
Keeper entflechten: API gegen Engine
Der Reflex beim Hören von „ZooKeeper ersetzen“ ist, sich vorzustellen, jedes Replicated*MergeTree anfassen zu müssen. Das musst du nicht, denn ClickHouse Keeper sind schon zwei trennbare Dinge, in einen Daemon geflochten:
-
Die ZooKeeper-API — ein linearisierbarer Schlüssel-Wert-Baum mit sequenziellen Knoten, Versionen und Watches. Im Quelltext ist das
KeeperStorage, umhüllt vonKeeperStateMachine(src/Coordination/KeeperStateMachine.h,class IKeeperStateMachine : public nuraft::state_machine). Das ist der Vertrag. Jede replizierte Tabelle, jedesystem.zookeeper-Abfrage, jede Leader-Wahl spricht ihn. -
Die Konsens-Engine — das, was einen Strom von Schreibanfragen nimmt, ein Quorum von Knoten auf ihre Reihenfolge einigen lässt und dann
commit(log_idx, data)auf der Zustandsmaschine in dieser vereinbarten Reihenfolge aufruft. Heute ist das NuRaft:KeeperServer::launchRaftServer(src/Coordination/KeeperServer.cpp) baut einennuraft::raft_server, einenasio_service, einenKeeperStateManagerund einenKeeperLogStore.
Die saubere Beobachtung — die von Rich Hickey — ist, dass KeeperStateMachine schon gegen die generische Konsensschnittstelle geschrieben ist (nuraft::state_machine: pre_commit, commit, rollback, Snapshots). Sie weiß nicht und es ist ihr egal, dass Raft darunter liegt. Die Engine ist ein Wert, den du tauschen kannst, keine Stelle, die an die API geschweißt ist.
Also: behalt die Zustandsmaschine, tausch die Engine aus. Die ZooKeeper-API, von der die Replikation abhängt, bleibt unberührt. Was stirbt, sind NuRaft, die Leader-Wahl und die Pflicht, ein separates Raft-Quorum zu betreiben.
Was an ihre Stelle tritt
Die Engine wird Quasar — der leaderlose, DAG-basierte, postquanten Konsens von Lux. Quasars Aufgabe ist hier identisch mit der von Raft: sich auf die Reihenfolge eines Logs einigen und signalisieren, wann ein Eintrag final ist. Der Unterschied liegt darin, wie die Einigung entsteht — kein einzelner Leader, auf den umgeschaltet wird, ML-DSA-Signaturen statt klassischer, und eine Finalität über eine Konfidenzschwelle statt einer Mehrheit einer festen Amtszeit.
Entscheidend: Quasar ist kein Go-Dienst, zu dem wir einen Prozess starten. Es liefert eine native C-Engine mit einem C++-fertigen ABI (libluxconsensus). Der Datastore-Server ist C++. Die Engine linkt also in denselben Prozess — kein Sidecar, kein Netzsprung zu einem Koordinator, keine zweite Sprache zur Laufzeit.
Die Naht ist bewiesen — an der echten Zustandsmaschine
Das ist keine Skizze, und es ist kein Modell mehr. Wir haben ein Programm gebaut, das die echte ClickHouse-Serverbibliothek linkt (libdbms.a) und die produktive KeeperStateMachine<KeeperMemoryStorage> treibt — genau die Klasse, über die ReplicatedMergeTree koordiniert — vollständig auf Quasar, mit NuRaft außerhalb des Ordnungspfads. Es lebt im Datastore-Baum unter src/Coordination/examples/keeper_quasar_poc.cpp und wird auf einem echten Linux-Host gebaut und ausgeführt (aarch64, clang-21).
Der Ablauf folgt treu, wie der Dispatcher NuRaft heute treibt — zwei Phasen, preprocess, wenn eine Anfrage angehängt wird, und commit, wenn der Konsens ihre Reihenfolge festschreibt:
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
Gegen eine ReplicatedMergeTree-Koordinationsfolge von zehn Anfragen gelaufen — Replik-Registrierung, Blocknummern-Sprünge, Leader-Wahl — verarbeitet es jede Anfrage vor, ordnet sie über Quasar, finalisiert, committet in den echten KeeperStorage und prüft, dass der entstandene Baum exakt stimmt, jedes Element einmal, in Konsensreihenfolge:
== REAL Keeper state machine reached consensus on Quasar — zero Raft, zero ZK ensemble ==
Darunter sitzt die minimale C-ABI-Demonstration (pkg/c/examples/cpp_consumer.cpp, make test-cpp), die libluxconsensus linkt und einen einzelnen Block finalisiert — dieselbe Engine, ohne die ClickHouse-Abhängigkeit. Beide sind reproduzierbar; das Datastore-Beispiel ist optional (-DENABLE_EXAMPLES=1 -DLUXCONSENSUS_DIR=...) und nie Teil eines normalen Builds.
Leg es eins zu eins auf Keeper:
| Keeper / NuRaft heute | Quasar-nativ |
|---|---|
nuraft::raft_server ordnet das Log | lux_chain_t ordnet das Log |
KeeperLogStore / Changelog | Quasars DAG-Log |
| Raft-Leader-Wahl | leaderlose Konfidenzwahl |
state_machine->commit(idx, data) | derselbe Aufruf, ausgelöst bei is_accepted |
| klassische Signaturen, Leader mit fester Amtszeit | ML-DSA-Signaturen, kein Leader |
| separates ZK/Keeper-Quorum zum Betreiben | Konsens im Prozess, im Server |
KeeperStateMachine::commit ist die Nahtstelle. NuRaft ruft sie heute auf; der Quasar-Treiber ruft morgen dieselbe Methode auf. Die Zustandsmaschine kann den Unterschied nicht erkennen — und genau diese Eigenschaft macht daraus einen Tausch und keine Neufassung.
Warum das über „post-quantum“ hinaus zählt
- Kein Leader. Raft hat einen gewählten Leader pro Amtszeit; sein Ausfall hält Schreibvorgänge an, bis neu gewählt wird. Quasars Einigung ohne Leader nimmt diese Head-of-Line-Abhängigkeit weg.
- Kein zweites Quorum zum Betreiben. Koordination hört auf, ein separates System zu sein, das du bereitstellst, überwachst und sicherst. Sie ist der Server.
- Postquantisch durch die Konstruktion. Die Signaturen, die die Einigung schützen, sind ML-DSA, keine klassischen Kurven, sodass ein künftiger Quanten-Angreifer Konsensnachrichten nicht fälschen kann.
- Eine Transportgeschichte. Sie fügt sich in den Rest der Richtung — natives TCP/HTTP jetzt, ZAP als Nächstes, und dieselben Lux-Primitive, die die Chains nutzen.
Ehrlicher Stand
Die Architektur oben ist real, und der Beweis läuft jetzt gegen die produktive KeeperStateMachine — kein Modell —, gebaut und grün auf einem echten Linux-Host. Was bleibt, ist der Tausch in der Engine: das Quasar-Konsens-Backend innerhalb von KeeperServer neben NuRaft einführen, es zum Standard machen und dann den Rest dessen, was KeeperServer tut — Snapshots, Wiederherstellung, Sitzungsablauf, Rekonfiguration — darauf tragen, mit der ganzen Coordination-Testsuite grün. Das ist echte Arbeit, und wir werden dir nicht sagen, Raft sei weg an dem Tag, an dem wir den Commit-Pfad bewiesen haben. Wir sagen dir, dass die Naht gegen die echten Typen hält, dass die Nahtstelle KeeperStateMachine::commit ist, und dass dort das ganze Spiel liegt.
Wenn es landet, verlassen contrib/NuRaft/ und die Raft-Engine den Baum. Die ZooKeeper-API bleibt, solange die Replikation sie spricht. So beseitigst du die letzte präquante Komponente, ohne ein einziges MergeTree anzufassen.
- Datastore: github.com/hanzoai/datastore
- Lux-Konsens: github.com/luxfi/consensus
Weiterlesen

Wir stellen Hanzo Datastore vor: eine OLAP-Engine bereit für Post-Quanten-Kryptografie
Ein von ClickHouse abgeleitetes, spaltenorientiertes Warehouse, neu aufgebaut rund um getrennten Speicher und Compute auf Hanzo S3, profilgeführt und zur Linkzeit auf etwa den halben Speicher optimiert, ohne gRPC und mit einem Weg zur postquantischen Koordination ohne Leader.

Der Router ist das Register
Hanzo Cloud hat keine eingecheckte OpenAPI-Datei. Die lebende Routentabelle ist die Quelle, dreifach projiziert, und ein Bijektionstest lässt den Build scheitern, wenn eine Projektion abweicht. 1,362 Operationen über 963 Pfade, je Prozess zur Anfragezeit erzeugt.

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.