لماذا اخترت خدمة تحكيم الأكواد؟

عند دراسة تصميم الأنظمة، من المفيد اختيار خدمة حقيقية تجمع بين عدة تحديات هندسية. خدمة تحكيم الأكواد — مثل تلك المستخدمة في منصات مثل LeetCode — تُعدّ مثالًا ممتازًا لعدة أسباب:

  1. خدمة متزامنة (Concurrent Service): مناسبة جدًا لدراسة مفاهيم تعدد الخيوط (Multithreading).
  2. تتطلب قرارات أمنية (Security Decisions): تحتاج إلى معالجة اعتبارات أمنية جوهرية عند تشغيل أكواد المستخدمين.
  3. تتضمن تصميمًا متعدد الخطوات (Multistep Design Thinking): تمرّ عبر مراحل متعددة من التحقق وحتى معالجة النتائج.

مخطط بيانات الإرسال (Submission Data Schema)

يحتوي المخطط USER_SUBMISSION على البيانات المطلوبة لكي تقوم الخدمة بتنفيذ خطوات المعالجة بالكامل:

FieldDescription
problem_idIdentifies the specific challenge being solved.
user_idUnique identifier for the user submitting the solution.
codesThe raw source code provided for evaluation.
languageThe specific programming language (e.g., C++, Java, Python).
testcasesThe set of inputs used to validate the submission.

سير عمل التنفيذ (Execution Workflow)

1. التحقق (Validation)

تبدأ خدمة التحكيم (JudgingService) بإجراء التحقق من البيانات المُرسلة. هذا ضروري للتأكد من أن الكود المُقدَّم ليس فارغًا، وأن حالات الاختبار (Test Cases) المطلوبة للتنفيذ موجودة، وتحديد لغة البرمجة المستخدمة.

2. توجيه التنفيذ (Execution Routing)

تُحدد الخدمة المُنفِّذ (Executor) المناسب لتشغيل الكود بناءً على اللغة البرمجية المُستخدمة:

  • على سبيل المثال: JavaExecutor لتنفيذ أكواد Java، أو PythonExecutor لتنفيذ أكواد Python.
  • إذا كانت لغة البرمجة غير مدعومة حاليًا، يتم رفع خطأ يفيد بأن "اللغة غير مدعومة بعد" (not supported yet).

3. معالجة النتائج (Result Processing)

تبدأ هذه المرحلة بإرسال إشعار (Notification) إلى أي خدمات أخرى تحتاج إلى الاستجابة النهائية لنتيجة الإرسال (Submission) مقابل عينات الاختبار.

تشمل النتائج المحتملة:

  • جميع الاختبارات ناجحة (All tests pass).
  • بعض حالات الاختبار لم تنجح (Partially failed test cases).
  • خطأ في وقت التشغيل (Runtime Error).
  • تجاوز الحد الزمني (Time Limit Exceeded).
  • تجاوز الحد الأقصى للذاكرة (Memory Limit Exceeded).

من بين هذه الخدمات واجهة المستخدم الرسومية (GUI) التي تحتاج إلى إبلاغ المستخدم بنتيجة إرساله.

مخطط تسلسل سير عمل الخدمة

sequenceDiagram
    participant User_GUI as المستخدم/الواجهة الرسومية (User/GUI)
    participant JS as خدمة_التحكيم (JudgingService)
    participant E as المُنفِّذ (Executer) (Java, Python, إلخ)
    participant N as خدمة_الإشعارات (Notification Service)
  
    User_GUI->>JS: 1. إرسال (Submit) USER_SUBMISSION
    activate JS
    JS->>JS: 1.1. إجراء_التحقق (Perform Validation) (فحص_الكود، اللغة، حالات_الاختبار)
    alt إذا_نجح_التحقق_واللغة_مدعومة (If Validation Passes & Language Supported)
        JS->>E: 2. توجيه_التنفيذ (Execution Routing) (تنفيذ_الكود_مقابل_حالات_الاختبار)
        activate E
        E-->>JS: نتيجة_التنفيذ (Execution Result) (مثل:_ناجح،_خطأ_في_وقت_التشغيل،_تجاوز_الحد_الزمني)
        deactivate E
        JS->>JS: 3. معالجة_النتيجة (Result Processing)
        JS->>N: إرسال_النتيجة_النهائية (Send final result)
        activate N
        N-->>User_GUI: عرض_نتيجة_الإرسال (Display Submission Result)
        deactivate N
    else إذا_كانت_اللغة_غير_مدعومة (If Language Not Supported)
        JS-->>User_GUI: رفع_خطأ_"غير_مدعومة_بعد" (Raise "not supported yet")
    end
    deactivate JS

ما التالي؟

تبقى جزئيتان مهمتان: أولاهما مشكلة الأمان (Security)، والتي يمكن التنبؤ بها بسهولة، وثانيهما مشاكل التزامن (Concurrency) وكيفية الاستفادة من تعدد الخيوط (Multithreading). إن شاء الله، أنوي مناقشة هذين الجزأين في مقال ثانٍ. كما سأناقش التطبيق الكامل (Full Implementation) للخدمة في مقال ثالث.

الهدف النهائي هو تقديم مجموعة من المقالات التي تبني وعيًا بقرارات هندسة الأنظمة (Architecture Decisions) وتوضح المفاهيم الأساسية في تصميم الأنظمة (System Design)، سواء على مستوى التصميم المنخفض (Low-Level Design) أو العالي (High-Level Design).

من واقع التجربة، فإن فهم هذه القرارات المعمارية مبكرًا يوفّر الكثير من الجهد لاحقًا. كما أن اتخاذ قرار لتغيير جزء في التصميم قبل الدخول في مرحلة الإنتاج أو أثناءه لن يكون قرارًا سهل التنفيذ.