Únete a la beta de Hanzo y consigue un 50 % de descuento en cualquier plan de pago

Acabar con Raft: del consenso de ZooKeeper a Lux Quasar, de forma nativa

Cómo Hanzo Datastore sustituye el motor NuRaft bajo ClickHouse Keeper por Lux Quasar — consenso poscuántico y sin líder, enlazado directamente en el servidor C++ — y conserva la API de ZooKeeper que habla la replicación.

En esta página

Hanzo Datastore coordina hoy las tablas replicadas igual que ClickHouse: un Keeper compatible con ZooKeeper, embebido, que ejecuta Raft dentro del proceso del servidor. Funciona. También es el último componente clásico, con líder y pre-cuántico, de la pila. Este artículo cuenta cómo lo eliminamos — y por qué «acabar con ZK» no significa reescribir la replicación.

Desentramar Keeper: la API frente al motor

El instinto, al oír «sustituir ZooKeeper», es imaginar que hay que tocar cada Replicated*MergeTree. No tienes que hacerlo, porque ClickHouse Keeper ya son dos cosas separables trenzadas en un solo demonio:

  1. La API de ZooKeeper — un árbol clave-valor linealizable, con nodos secuenciales, versiones y watches. En el fuente esto es KeeperStorage, envuelto por KeeperStateMachine (src/Coordination/KeeperStateMachine.h, class IKeeperStateMachine : public nuraft::state_machine). Este 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 toma un flujo de peticiones de escritura, logra que un quórum de nodos acuerde su orden y después llama a commit(log_idx, data) sobre la máquina de estados en ese orden acordado. Hoy eso es NuRaft: KeeperServer::launchRaftServer (src/Coordination/KeeperServer.cpp) construye 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, instantáneas). No sabe, ni le importa, que debajo esté Raft. El motor es un valor que puedes sustituir, no un punto soldado a la API.

Así que: conserva la máquina de estados, sustituye el motor. La API de ZooKeeper de la que depende la replicación sobrevive intacta. Mueren NuRaft, la elección de líder y la exigencia de ejecutar un quórum Raft aparte.

Qué lo sustituye

El motor pasa a ser Quasar — el consenso sin líder, basado en un DAG y poscuántico de Lux. El trabajo de Quasar aquí es idéntico al de Raft: acordar el orden de un registro y avisar cuando una entrada es final. La diferencia está en cómo se pone de acuerdo: no hay un líder único al que conmutar, las firmas son ML-DSA en lugar de las clásicas, y la finalidad es un umbral de confianza, no la mayoría de un término fijo.

Y lo decisivo: Quasar no es un servicio en Go al que lancemos un proceso. Trae un motor C nativo con una ABI lista para C++ (libluxconsensus). El servidor de Datastore es C++. El motor se enlaza en el mismo proceso — sin sidecar, sin salto de red hasta un coordinador, sin un segundo lenguaje en tiempo de ejecución.

La juntura está demostrada — contra la máquina de estados real

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

El flujo es fiel a cómo el despachador conduce NuRaft hoy — dos fases, preprocess cuando una petición se añade al registro y commit cuando el consenso fija 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

Ejecutado contra una secuencia de coordinación ReplicatedMergeTree de diez peticiones — registro de la réplica, incrementos del número de bloque, elección de líder — preprocesa cada petición, las ordena por Quasar, finaliza, hace commit en el KeeperStorage real y comprueba que el árbol resultante es exactamente el correcto, aplicado una vez cada elemento, en el orden del consenso:

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

Debajo queda la demostración mínima de la ABI de C (pkg/c/examples/cpp_consumer.cpp, make test-cpp), que enlaza libluxconsensus y finaliza un solo bloque — el mismo motor, sin la dependencia de ClickHouse. Las dos se pueden reproducir; el ejemplo de Datastore es opcional (-DENABLE_EXAMPLES=1 -DLUXCONSENSUS_DIR=...) y nunca entra en una compilación normal.

Ponlo sobre Keeper, uno a uno:

Keeper / NuRaft hoyNativo en Quasar
nuraft::raft_server ordena el registrolux_chain_t ordena el registro
KeeperLogStore / Changelogel registro 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 en is_accepted
firmas clásicas, líder de término fijofirmas ML-DSA, sin líder
un quórum ZK/Keeper aparte que operarconsenso en proceso, dentro del servidor

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

Por qué importa más allá de «poscuántico»

  • Sin líder. Raft tiene un único líder elegido por término; si cae, las escrituras se paran hasta la reelección. El acuerdo sin líder de Quasar quita esa dependencia de cabeza de línea.
  • Sin un segundo quórum que operar. La coordinación deja de ser un sistema aparte que aprovisionas, vigilas y del que haces copias de seguridad. Es el servidor.
  • Poscuántico por construcción. Las firmas que protegen el acuerdo son ML-DSA, no curvas clásicas, de modo que un adversario cuántico futuro no puede falsificar los mensajes de consenso.
  • Una sola historia de transporte. Encaja con el resto de la dirección — TCP/HTTP nativo ahora, ZAP a continuación, y las mismas primitivas de Lux que usan las cadenas.

Estado honesto

La arquitectura de arriba es real, y la prueba ya se ejecuta contra la KeeperStateMachine de producción — no contra un modelo —, compilada y en verde en un host Linux real. Lo que falta es el recambio dentro del motor: meter el backend de consenso Quasar dentro de KeeperServer, al lado de NuRaft, dejarlo por defecto y después llevar sobre él el resto de lo que hace KeeperServer — instantáneas, recuperación, caducidad de sesión, reconfiguración — con toda la batería de pruebas de Coordination en verde. Eso es trabajo de verdad, y no vamos a decirte que Raft ha desaparecido el día en que demostramos el camino de commit. Te decimos que la juntura aguanta con los tipos reales, que el punto de unión es KeeperStateMachine::commit, y que ahí se juega todo.

Cuando llegue, contrib/NuRaft/ y el motor Raft salen del árbol. La API de ZooKeeper se queda mientras la replicación la hable. Así se elimina el último componente pre-cuántico sin tocar un solo MergeTree.

Comentarios

Entra con tu Hanzo ID para comentar.

Cargando comentarios…

Leer más