같은 프로세스 안에서 Raft를 끝낸다: ZooKeeper 합의에서 직접 이어 붙는 Lux Quasar로
이 글은 Hanzo Datastore가 ClickHouse Keeper 아래에 있는 NuRaft 엔진을 Lux Quasar로 어떻게 바꾸는지 설명합니다. 바뀌는 합의는 후양자이고 단일 리더가 없으며, 같은 C++ 서버 프로세스 안에 직접 연결되고, 복제가 이미 말하고 있는 ZooKeeper API는 호출 방식을 그대로 둔 채 남습니다.

이 페이지에서
Hanzo Datastore는 오늘 복제된 테이블을 ClickHouse가 하는 방식으로 조율합니다. 서버 프로세스 안에서 Raft를 돌리는, 내장된 ZooKeeper 호환 Keeper입니다. 동작합니다. 그리고 이것은 스택에 남은 마지막 고전적, 리더 기반, 양자 이전 구성 요소이기도 합니다. 이 글은 그것을 어떻게 끝내는지, 그리고 "ZK를 끝낸다"가 복제를 다시 쓰지 않는 이유입니다.
Keeper를 분리하다: API 대 엔진
"ZooKeeper를 교체한다"는 말을 들으면 모든 Replicated*MergeTree을 건드릴 생각을 하기 쉽습니다. 그럴 필요가 없습니다. ClickHouse Keeper는 이미 분리할 수 있는 두 가지가 하나의 데몬에 엮인 것이기 때문입니다.
-
ZooKeeper API — 순차 노드, 버전, 워치를 갖춘 선형화 가능한 키-값 트리입니다. 소스에서 이것은
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을 만듭니다.
깨끗한 관찰, Rich Hickey의 관찰은 KeeperStateMachine이 이미 일반적인 합의 인터페이스에 대해 쓰여 있다는 점입니다(nuraft::state_machine: pre_commit, commit, rollback, 스냅샷). 아래에 Raft가 있는지는 알지도 못하고 신경도 쓰지 않습니다. 엔진은 바꿀 수 있는 값이지, API에 용접된 자리가 아닙니다.
그래서 상태 기계는 두고, 엔진을 바꿉니다. 복제가 의존하는 ZooKeeper API는 손대지 않고 남습니다. 죽는 것은 NuRaft, 리더 선출, 그리고 별도의 Raft 정족수를 돌려야 하는 요구입니다.
무엇을 대신 넣나
엔진은 Quasar가 됩니다. Lux의, 리더가 없고 DAG에 기반한 후양자 합의입니다. 여기서 Quasar의 일은 Raft의 일과 같습니다. 로그의 순서에 합의하고, 항목이 확정되면 알립니다. 다른 점은 합의하는 방식입니다. 장애 조치할 단일 리더가 없고, 고전 서명 대신 ML-DSA 서명을 쓰며, 고정 임기의 과반 대신 신뢰 임계로 확정합니다.
결정적인 점은 Quasar가 셸로 불러 내는 Go 서비스가 아니라는 것입니다. C++용 ABI를 갖춘 네이티브 C 엔진을 제공합니다(libluxconsensus). Datastore 서버는 C++입니다. 그래서 엔진은 같은 프로세스 안으로 링크됩니다. 사이드카도 없고, 조율자로 가는 네트워크 홉도 없고, 실행 시의 둘째 언어도 없습니다.
이음매는 증명되었다, 실제 상태 기계에 대해
이것은 스케치가 아니고, 더 이상 모형도 아닙니다. ClickHouse 서버 라이브러리를 실제로 링크하고(libdbms.a) 프로덕션 KeeperStateMachine<KeeperMemoryStorage>을 구동하는 프로그램을 만들었습니다. ReplicatedMergeTree가 조율에 쓰는 바로 그 클래스이며, 순서 경로에는 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 | Quasar의 DAG 로그 |
| 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 호스트에서 빌드되어 통과합니다. 남은 것은 엔진 안의 교체입니다. NuRaft 옆에 KeeperServer의 안쪽으로 Quasar 합의 백엔드를 들이고 그것을 기본으로 만든 다음, KeeperServer가 하는 나머지인 스냅샷, 복구, 세션 만료, 재구성을 Coordination 테스트 전체가 녹색인 채로 올립니다. 이것은 실제 작업입니다. 커밋 경로를 증명한 날에 Raft가 사라졌다고 말하지 않겠습니다. 이음매가 실제 타입에 대해 버티고, 접합점이 KeeperStateMachine::commit이며, 그것이 게임의 전부라고 말합니다.
그것이 들어오면 contrib/NuRaft/과 Raft 엔진은 트리를 떠납니다. ZooKeeper API는 복제가 그것을 말하는 동안 남습니다. 마지막 양자 이전 구성 요소를 MergeTree 하나에도 손대지 않고 끝내는 방법이 이것입니다.
- Datastore: github.com/hanzoai/datastore
- Lux 합의: github.com/luxfi/consensus
더 읽기

오늘 공개하는 Hanzo Datastore: 포스트양자 시대의 분석에 대비한 열 지향 OLAP 엔진
ClickHouse에서 갈라져 나온 열 지향 웨어하우스입니다. Hanzo S3 위에서 저장과 계산을 나누어 다시 지었고, 프로파일 안내와 링크 시점 최적화로 상주 메모리를 대략 절반으로 줄였으며, gRPC를 걷어 내고 리더 없는 포스트양자 조율로 가는 길을 열었습니다.

라우터가 레지스트리다
Hanzo Cloud 에는 체크인된 OpenAPI 파일이 없다. 살아 있는 라우트 표가 원천이고, 세 갈래로 투영되며, 어느 투영이 어긋나면 전단사 시험이 빌드를 떨어뜨린다. 963 개 경로에 걸친 1,362 개 연산이, 요청 시각에 프로세스마다 만들어진다.

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.