menuTicketing

ما هي Ticketing؟


لماذا نحتاج القفل الموزّع

عندما يتعامل عدد من العمليات أو الخوادم مع نفس المورد (حساب، طلب، مخزون، مهمة دفعية، إلخ) في الوقت نفسه، ينشأ تنافس. الأقفال المتبادلة (mutex) أو الإشارات (semaphore) المدمجة في اللغة صالحة فقط داخل عملية واحدة — فإذا كانت هناك عدة عمليات، أو كانت موزّعة على عدة خوادم، فأنت بحاجة إلى آلية إقصاء متبادل منفصلة تتجاوز حدود العملية.

Ticketing هي خادم قفل ومجموعة من العملاء بُنيت خصيصًا لحل هذه المشكلة بالذات: الإقصاء المتبادل الموزّع عبر حدود العمليات والخوادم. يتصل العميل بخادم القفل عبر TCP، ويحجز مفتاحًا مسمّى، ويمرّ عبر المنطقة الحرجة، ثم يعيده.

الفلسفة الأساسية

⚡ خفيفة وسريعة

تستخدم Ticketing بروتوكول إطارات مكوّن من حقول ثنائية ثابتة العرض، وليس JSON. بعد توزيع op بحجم بايت واحد، تُقرأ عدد ثابت من البايتات حسب كل op كطول، ولا يُقرأ حتى فاصل السطر (\n) إلا للمفتاح الأخير فقط. التحليل هنا أقرب إلى مجرد قراءة إزاحات منه إلى تحليل فعلي، لذا فالحمل الإضافي أقل من تحليل JSON في كل طلب.

🔑 الأمان عبر رموز الحجز (Fencing Tokens)

في كل مرة يُحجز فيها قفل، يُصدر الخادم معه رمز حجز u64 يتزايد باطراد. إذا جُعل المورد المحمي يتحقق من اطراد هذا الرمز، فيمكنه رفض وصول عميل استيقظ متأخرًا بعد انتهاء lease ويحاول استخدام رمز أقل. والنتيجة أن خادم القفل نفسه لا يحتاج إلى أن يكون متسقًا تمامًا في كل لحظة للحفاظ على الأمان — وهذا مهم بشكل خاص أثناء فترات التبديل عند الأعطال (failover).

🔄 عنقود بلا توقف خدمة

يعمل خادم واحد بمفرده بشكل جيد (الوضع الفردي)، لكن إذا احتجت إلى تشغيل بلا توقف خدمة، شكّل عنقودًا بحدّ أدنى خادمين. لا ينتخب العنقود العقدة النشطة عبر توافق (كوروم)، بل بناءً على الأولوية — وتصبح بقية العقد عقدًا احتياطية تتلقى نسخًا مطابقة في الوقت الفعلي. إذا تعطلت العقدة النشطة (بما في ذلك إعادة التشغيل) أو أُغلقت بأمان (graceful)، تتولى عقدة احتياطية المهمة. راجع صفحة آلية العمل لمزيد من التفاصيل.

🌐 لا تتقيّد بلغة برمجة معينة

فوق نفس البروتوكول الثنائي، تُقدَّم عملاء رسميون بـ 8 لغات: Rust، Java/Kotlin، JavaScript/TypeScript، Python، C#، Go، Ruby، وC/C++. تعرض كل لغة الواجهة البرمجية بأسلوب طبيعي لبيئتها — فمثلًا تحصل اللغات ذات أسلوب async/await على واجهات غير متزامنة، بينما تحصل Go/Ruby/C على واجهات حاجبة مدعومة بخيط خلفي. يبقى البروتوكول متطابقًا بايتًا بايت عبر كل اللغات. راجع صفحة المكتبات لتعليمات التثبيت والأمثلة الخاصة بكل لغة.