Únete a la beta de Hanzo y obtén 50 % de descuento en cualquier plan de pago

Matar Raft: del consenso ZooKeeper al Lux Quasar nativo

Cómo Hanzo Datastore reemplaza el motor NuRaft que está debajo de ClickHouse Keeper por Lux Quasar — consenso poscuántico, sin líder, vinculado directo en el servidor C++ — y deja la API de ZooKeeper que la replicación sigue hablando.

En esta página

Hanzo Datastore coordina hoy las tablas replicadas como lo hace ClickHouse: un Keeper compatible con ZooKeeper, embebido, que corre Raft adentro del proceso del servidor. Funciona. También es el último componente clásico, con líder y previo a lo cuántico, que queda en el stack. Este post cuenta cómo lo matamos — y por qué «matar ZK» no quiere decir reescribir la replicación.

Desenredar Keeper: la API contra el motor

El reflejo, cuando se oye «reemplazar ZooKeeper», es pensar que hay que tocar cada Replicated*MergeTree. Ustedes no tienen que hacerlo, porque ClickHouse Keeper ya son dos piezas separables trenzadas en un solo demonio:

  1. La API de ZooKeeper — un árbol clave-valor linealizable, con nodos secuenciales, versiones y watches. En el código esto es KeeperStorage, envuelto por KeeperStateMachine (src/Coordination/KeeperStateMachine.h, class IKeeperStateMachine : public nuraft::state_machine). Ese es el contrato. Cada tabla replicada, cada consulta a system.zookeeper y cada elección de líder lo hablan.

  2. El motor de consenso — lo que agarra un flujo de solicitudes de escritura, consigue que un cuórum de nodos acuerde el orden y después llama a commit(log_idx, data) en la máquina de estados, en ese orden ya acordado. Hoy eso es NuRaft: KeeperServer::launchRaftServer (src/Coordination/KeeperServer.cpp) arma un nuraft::raft_server, un asio_service, un KeeperStateManager y un KeeperLogStore.

La observación limpia — la de Rich Hickey — es que KeeperStateMachine ya está escrita contra la interfaz de consenso genérica (nuraft::state_machine: pre_commit, commit, rollback, capturas). No sabe, ni le importa, que Raft esté abajo. El motor es un valor que ustedes pueden cambiar, no un punto soldado a la API.

Entonces: conserven la máquina de estados, reemplacen el motor. La API de ZooKeeper de la que depende la replicación queda intacta. Lo que muere es NuRaft, la elección de líder y la obligación de correr un cuórum Raft aparte.

Qué entra en su lugar

El motor pasa a ser Quasar — el consenso sin líder, basado en un DAG y poscuántico de Lux. El trabajo de Quasar acá es el mismo que el de Raft: ponerse de acuerdo en el orden de un log y avisar cuándo una entrada ya es final. La diferencia está en el cómo: no hay un líder único al que hacer failover, las firmas son ML-DSA en vez de las clásicas, y la finalidad es un umbral de confianza, no la mayoría de un término fijo.

Lo clave: Quasar no es un servicio en Go al que le lanzamos un proceso. Trae un motor C nativo con una ABI lista para C++ (libluxconsensus). El servidor de Datastore es C++. El motor se vincula en el mismo proceso — sin sidecar, sin salto de red a un coordinador, sin un segundo lenguaje en runtime.

La costura está probada — contra la máquina de estados real

Esto no es un borrador, y ya no es un modelo. Armamos un programa que vincula la biblioteca de verdad del servidor de ClickHouse (libdbms.a) y maneja la KeeperStateMachine<KeeperMemoryStorage> de producción — la clase exacta por la que se coordina ReplicatedMergeTree — entera sobre Quasar, con NuRaft fuera del camino de ordenamiento. Vive en el árbol de Datastore, en src/Coordination/examples/keeper_quasar_poc.cpp, y se compila y corre en un host Linux real (aarch64, clang-21).

El flujo es fiel a cómo el despachador maneja NuRaft hoy — dos fases, preprocess cuando una solicitud se agrega al log y commit cuando el consenso cierra su orden:

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

Corrido contra una secuencia de coordinación ReplicatedMergeTree de diez solicitudes — registro de la réplica, saltos del número de bloque, elección de líder — preprocesa cada solicitud, las ordena por Quasar, finaliza, hace commit en el KeeperStorage real y afirma que el árbol que queda es exactamente el correcto, aplicado una vez cada uno, en el orden del consenso:

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

Abajo está la demo mínima de la ABI de C (pkg/c/examples/cpp_consumer.cpp, make test-cpp), que vincula libluxconsensus y finaliza un solo bloque — el mismo motor, sin la dependencia de ClickHouse. Las dos se reproducen; el ejemplo de Datastore es opt-in (-DENABLE_EXAMPLES=1 -DLUXCONSENSUS_DIR=...) y nunca entra en un build normal.

Mírenlo sobre Keeper, uno a uno:

Keeper / NuRaft actualmenteQuasar nativo
nuraft::raft_server ordena el loglux_chain_t ordena el log
KeeperLogStore / Changelogel log DAG de Quasar
elección de líder de Raftvotación de confianza sin líder
state_machine->commit(idx, data)la misma llamada, disparada al cumplirse is_accepted
firmas clásicas, líder de término fijofirmas ML-DSA, sin líder
un cuórum ZK/Keeper aparte para operarconsenso en el proceso, adentro del servidor

KeeperStateMachine::commit es el punto de unión. NuRaft lo llama hoy; el driver de Quasar llama mañana al mismo método. La máquina de estados no puede notar la diferencia — y esa es justo la propiedad que hace de esto un recambio y no una reescritura.

Por qué esto importa más allá de «post-cuántico»

  • Sin líder. Raft tiene un solo líder electo por término; si se cae, las escrituras se frenan hasta la reelección. El acuerdo sin líder de Quasar saca esa dependencia de cabeza de línea.
  • Sin un segundo cuórum que correr. La coordinación deja de ser un sistema aparte que ustedes aprovisionan, monitorean y respaldan. Es el servidor.
  • Poscuántico de fábrica. Las firmas que protegen el acuerdo son ML-DSA, no curvas clásicas, así que un adversario cuántico de más adelante no puede falsificar mensajes de consenso.
  • Una sola historia de transporte. Se compone con el resto del rumbo — TCP/HTTP nativo ahora, ZAP después, y las mismas primitivas de Lux que usan las cadenas.

Dónde estamos de verdad

La arquitectura de arriba es real, y la prueba ya corre contra la KeeperStateMachine de producción — no contra un modelo —, compilada y pasando en un host Linux real. Lo que falta es el recambio adentro del motor: meter el backend de consenso Quasar dentro de KeeperServer, al lado de NuRaft, volverlo el predeterminado y después subir el resto de lo que hace KeeperServer — capturas, recuperación, vencimiento de sesión, reconfiguración — con toda la suite de pruebas de Coordination en verde. Eso es trabajo de verdad, y no les vamos a decir que Raft ya no está el día en que probamos el camino de commit. Les decimos que la costura aguanta contra los tipos reales, que el punto de unión es KeeperStateMachine::commit, y que ahí está todo el juego.

Cuando entre, contrib/NuRaft/ y el motor Raft se van del árbol. La API de ZooKeeper se queda mientras la replicación la hable. Así se mata el último componente previo a lo cuántico sin tocar un solo MergeTree.

Comentarios

Ingresa con tu Hanzo ID para comentar.

Cargando comentarios…

Leer más