تصميم LeetCode على نطاق واسع

يبدو LeetCode بسيطاً. يتصفح المستخدمون المسائل، ويكتبون الأكواد، ويتلقون النتائج.

الواقع مختلف. فخلف الشاشة يوجد نظام موزع. يجب أن يتعامل مع ملايين المستخدمين وآلاف عمليات إرسال الأكواد في كل ثانية. كما يجب أن يصمد أمام الارتفاعات الهائلة في حركة المرور أثناء المسابقات.

إليك كيفية تصميم منصة برمجة قابلة للتوسع.

الميزات الأساسية

  • تصفح والبحث عن المسائل حسب الصعوبة أو الوسوم (tags).
  • كتابة الأكواد بلغات متعددة.
  • إرسال الحلول وتلقي النتائج في ثوانٍ.
  • الانضمام إلى مسابقات أسبوعية مع لوحات صدارة مباشرة.

متطلبات النطاق (Scale)

  • 10 ملايين مستخدم مسجل.
  • مليونا مستخدم نشط يومياً.
  • 100 ألف مستخدم متزامن أثناء المسابقات.
  • 20 ألف عملية إرسال كود في الدقيقة.

بنية النظام (System Architecture)

لا يمكنك تشغيل تنفيذ الكود بشكل متزامن (synchronously). إذا قام مستخدم بإرسال كود، فلا ينبغي لـ API أن ينتظر النتيجة، لأن ذلك سيؤدي إلى انتهاء المهلة (timeouts).

بدلاً من ذلك، استخدم نهجاً غير متزامن (asynchronous):

  1. يقوم المستخدم بإرسال الكود عبر API.
  2. يضع النظام عملية الإرسال في طابور رسائل (Message Queue) مثل Kafka أو RabbitMQ.
  3. تقوم عمال تنفيذ الكود (Code Execution Workers) بسحب المهام من الطابور.
  4. يقوم العمال بتشغيل الكود في بيئة معزولة (sandbox).
  5. تقوم خدمة النتائج (Result Service) بتحديث الحالة.

تعتبر الـ Sandbox الجزء الأكثر أهمية. فتشغيل أكواد عشوائية على خوادمك أمر خطير، لذا يجب عليك استخدام بيئات معزولة.

تعتبر حاويات Docker الخيار الأفضل لأنها:

  • خفيفة الوزن.
  • سريعة التشغيل.
  • سهلة التوسع أفقياً (horizontally).
  • آمنة عندما تقوم بتقييد الوصول إلى المعالج (CPU) والذاكرة والشبكة.

استراتيجية تخزين البيانات

استخدم قواعد بيانات مختلفة لاحتياجات مختلفة:

  • قواعد البيانات العلائقية (PostgreSQL/MySQL): استخدمها للمستخدمين والمسابقات والتصنيفات، فهي تضمن سلامة البيانات (data integrity).
  • قواعد بيانات NoSQL (DynamoDB/MongoDB): استخدمها للمسائل وحالات الاختبار وسجلات الإرسال، فهي تتوسع بشكل أفضل مع نمو البيانات.
  • التخزين المؤقت (Caching) باستخدام Redis: استخدمه لتفاصيل المسائل ومعلومات المسابقات لتقليل الحمل على قاعدة البيانات.

لوحات الصدارة في الوقت الفعلي (Real-Time Leaderboards)

خلال المسابقات، تتغير التصنيفات كل ثانية. لا تقم بإعادة حساب لوحة الصدارة بالكامل في كل مرة يرسل فيها شخص ما كوداً.

بدلاً من ذلك:

  • قم بتحديث النتائج بشكل غير متزامن.
  • استخدم Redis Sorted Sets لإدارة التصنيفات.
  • قم بدفع التحديثات للمستخدمين عبر WebSockets.

الموثوقية والمقايضات (Reliability and Trade-offs)

في هذا التصميم، الأولوية هي للتوافر (availability). التأخير الطفيف في تحديث لوحة الصدارة أمر مقبول، لكن تعطل النظام أثناء المسابقة ليس كذلك. نحن نختار الاتساق النهائي (eventual consistency) للحفاظ على استمرارية عمل المنصة.

كيف ستصمم محرك تنفيذ الكود؟ هل ستستخدم Docker أم microVMs؟

Source: https://dev.to/himanshudevgupta/system-design-designing-leetcode-at-scale-45op

Optional learning community: https://t.me/GyaanSetuAi