إنهاء Raft داخل العملية نفسها: من إجماع ZooKeeper إلى Lux Quasar الأصلي
كيف يستبدل Hanzo Datastore محرك NuRaft تحت ClickHouse Keeper بمحرك Lux Quasar، وهو إجماع بلا قائد لعصر ما بعد الكم يُربط مباشرة داخل خادم C++، مع الإبقاء على واجهة ZooKeeper التي يتحدث بها الاستنساخ من دون تغيير.

في هذه الصفحة
Hanzo Datastore ينسّق الجداول المستنسخة اليوم بالطريقة التي يعمل بها ClickHouse: Keeper متوافق مع ZooKeeper ومضمّن، يشغّل Raft داخل عملية الخادم. هذا يعمل. وهو أيضًا آخر مكوّن كلاسيكي قائم على قائد وسابق لعصر الكم في المكدس. هذه المقالة تشرح كيف ننهي ذلك، ولماذا عبارة "إنهاء ZK" لا تعني إعادة كتابة الاستنساخ.
فصل Keeper: الواجهة في مقابل المحرك
الغريزة عند سماع "استبدال ZooKeeper" هي تخيّل لمس كل Replicated*MergeTree. أنت لا تفعل ذلك، لأن ClickHouse Keeper أصلًا شيئان قابلان للفصل مضفوران في برنامج خفي واحد:
-
واجهة ZooKeeper — شجرة مفتاح وقيمة قابلة للترتيب الخطي، بعقد متسلسلة وإصدارات ومراقبات. في المصدر هذا هو
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 يقع تحته. المحرك قيمة تُستبدل، لا موضعًا ملحومًا في الواجهة.
إذن: أبقِ آلة الحالة، واستبدل المحرك. واجهة ZooKeeper التي يعتمد عليها الاستنساخ تبقى كما هي. ما يموت هو NuRaft، وانتخاب القائد، وشرط تشغيل نصاب Raft منفصل.
ما الذي يحل محلّه
يصبح المحرك Quasar، إجماع بلا قائد قائم على DAG لعصر ما بعد الكم من Lux. مهمة Quasar هنا مطابقة لمهمة Raft: الاتفاق على ترتيب السجل والإشارة حين يصبح المدخل نهائيًا. الفرق في كيفية الاتفاق: لا قائد واحد يُتجاوز عند تعطله، وتواقيع ML-DSA بدل التواقيع الكلاسيكية، ونهائية بعتبة ثقة بدل أغلبية ضمن دورة ثابتة.
والأهم أن Quasar ليس خدمة Go نستدعيها من الخارج. هو يُشحن كمحرك C أصلي بواجهة ثنائية جاهزة لـ C++ (libluxconsensus). خادم Datastore مكتوب بـ C++. لذا يرتبط المحرك داخل العملية نفسها: بلا عربة جانبية، وبلا قفزة شبكة إلى منسّق، وبلا لغة ثانية في وقت التشغيل.
الوصلة مثبتة، على آلة الحالة الحقيقية
هذا ليس مخططًا، ولم يعد نموذجًا. بنينا برنامجًا يربط مكتبة خادم ClickHouse الفعلية (libdbms.a) ويقود KeeperStateMachine<KeeperMemoryStorage> المستخدم في الإنتاج، وهو الصنف نفسه الذي ينسّق عبره ReplicatedMergeTree، بالكامل على Quasar، و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 الثنائية (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 | سجل DAG لدى Quasar |
| انتخاب قائد 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 حقيقي. ما يبقى هو الاستبدال داخل المحرك: إدخال خلفية إجماع Quasar داخل KeeperServer إلى جانب NuRaft، وجعلها الافتراض، ثم نقل بقية ما يفعله KeeperServer، من اللقطات والاستعادة وانتهاء الجلسة وإعادة التهيئة، إليها مع اخضرار مجموعة اختبارات Coordination كاملة. هذا عمل حقيقي، ولن نقول إن Raft اختفى يوم أثبتنا مسار التثبيت. نقول إن الوصلة تصمد أمام الأنواع الحقيقية، وإن نقطة الالتقاء هي KeeperStateMachine::commit، وهذا هو كل الموضوع.
حين يهبط هذا التغيير، يغادر contrib/NuRaft/ ومحرك Raft الشجرة. تبقى واجهة ZooKeeper ما دام الاستنساخ يتحدث بها. هكذا يُنهى آخر مكوّن سابق لعصر الكم من دون لمس MergeTree واحد.
- Datastore: github.com/hanzoai/datastore
- إجماع Lux: github.com/luxfi/consensus
اقرأ المزيد

نقدّم Hanzo Datastore: محرك تحليلات OLAP عمودي مهيّأ لعصر ما بعد الكم
مستودع أعمدة مشتق من ClickHouse، أُعيد بناؤه حول فصل التخزين عن الحوسبة على Hanzo S3، وحُسّن بتوجيه من قياسات التشغيل وفي وقت الربط حتى صارت ذاكرته نحو النصف، بعد نزع gRPC، مع مسار إلى تنسيق بلا قائد في عصر ما بعد الكم.

الموجّه هو السجل
ليس لدى Hanzo Cloud ملف OpenAPI مُودَع في المستودع. جدول المسارات الحي هو المصدر، ويُسقط بثلاث طرق، ويفشل اختبار التقابل البناء إذا انحرف أي إسقاط. 1,362 عملية عبر 963 مسارًا، تُولَّد لكل عملية عند وقت الطلب.

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.