يا جماعة الخير، الله يمسيكم بالخير. اسمحولي اليوم أحكيلكم قصة صارت معي ومع فريقي قبل كم سنة، قصة عن ليالي طويلة من التوتر والقهوة البايتة، والفضل كله كان لشبكة عنكبوتية من المهام الخلفية (Background Tasks) اللي بنيناها بإيدينا واحنا مش حاسين.
بتذكر منيح، كان عنا نظام مالي ضخم، وفي نهاية كل شهر، كان المفروض النظام يشتغل تلقائياً، يسحب بيانات الفواتير من عشر أماكن مختلفة، يعمل عليها شوية حسابات معقدة، يولد تقرير PDF لكل عميل، ويرسله على إيميله. شغلانة مرتبة، صح؟ المشكلة إنه “التلقائية” هاي كانت عبارة عن مجموعة من سكربتات PHP و Python مربوطة ببعضها عن طريق الـ Cron Jobs على سيرفر لينكس. الحكي بيناتنا، كانت فوضى ما بعدها فوضى.
وفي ليلة من ليالي نهاية الشهر المشؤومة، الساعة 2 الفجر، بيصحيني تلفون من المدير المالي للشركة. صوته معصّب وبيحكيلي: “أبو عمر، وين التقارير؟ العملاء الكبار ما وصلهم إشي وبدهم فواتيرهم عشان يصرفوا الدفعات!”. قلبي وقع برجليّ. ركضت على اللابتوب، فتحت السيرفر، وبلشت رحلة العذاب. السكربت الأول شغال، الثاني شكله علّق، الثالث فشل بصمت ومحدش إله خبر، والرابع ما اشتغل أصلاً لأنه بيعتمد على نتيجة الثالث. بعد 3 ساعات من التنبيش في ملفات الـ logs والبحث اليدوي، اكتشفنا إنه API خارجي كنا بنسحب منه أسعار العملات رجّع قيمة غير متوقعة، فواحد من السكربتات “انجلط” ومات بهدوء، وبما إنه مات، كل اللي بعده ما اشتغلوا. ما كان في أي تنبيه، أي إشعار، أي شي يخبرنا بالكارثة اللي صارت. يومها حلفنا يمين، إنه هالمسخرة لازم تنتهي. ومن هنا بدأت رحلتنا مع عالم “تنسيق سير العمل”.
ما قبل العاصفة: عالم الـ Cron Jobs والفوضى الصامتة
لكل اللي بدأوا في عالم البرمجة، الـ Cron Job هو الصديق الأول اللي بنتعرف عليه لما بدنا نعمل أتمتة. هو أداة بسيطة وقوية جداً موجودة في كل أنظمة التشغيل الشبيهة بيونكس (لينكس وماك). بكل بساطة، بتعطيه أمر وبتقله: “نفذلي هالأمر كل يوم الساعة 5 الصبح”.
# مثال: تشغيل سكربت PHP كل يوم الساعة 5 صباحاً
0 5 * * * /usr/bin/php /var/www/html/scripts/generate-reports.php
وهذا إشي عظيم للمهام البسيطة والمنفصلة. لكن لما يصير عندك سير عمل (Workflow) معقد، زي قصة التقارير اللي حكيتها، بتبلش المشاكل تظهر:
- الفشل الصامت (Silent Failures): هاي أكبر مصيبة. إذا فشل السكربت لأي سبب، الـ Cron ما رح يبعتلك إيميل يحكيلك “الحقني يا أبو عمر، أنا فشلت!”. هو بيكمل حياته ولا كأن إشي صار، وإنت ما بتعرف بالمشكلة إلا لما العميل يتصل وهو معصّب.
- انعدام إدارة الاعتماديات (No Dependency Management): في قصتنا، كان لازم سكربت توليد البيانات يشتغل قبل سكربت توليد الـ PDF. مع الـ Cron، كنا “نتحايل” على هالموضوع. بنشغّل الأول الساعة 2:00، والثاني الساعة 2:15، على أمل إنه الأول يكون خلّص. بس شو بصير لو الأول أخذ وقت أطول من 15 دقيقة؟ بتصير الكارثة.
- صعوبة المراقبة (Lack of Visibility): لما يكون عندك 50 Cron Job على سيرفرات مختلفة، كيف بدك تعرف شو اللي شغال، وشو اللي خلص، وشو اللي فشل؟ الموضوع بصير كابوس، وبتحتاج تشيك على ملفات الـ logs في كل مكان.
- لا توجد إعادة محاولة تلقائية (No Automatic Retries): أحياناً بتفشل المهمة بسبب مشكلة مؤقتة في الشبكة. الـ Cron ما عنده منطق “أوه، فشلت المحاولة، خليني أجرب كمان مرة بعد دقيقة”. إذا فشلت، خلص، انتهى الموضوع حتى موعد التشغيل التالي.
– صعوبة التوسع (Scalability Issues): تخيل معي بدك تشغل 1000 تقرير بنفس الوقت. الـ Cron Job الواحد رح يشغلهم ورا بعض على نفس السيرفر، وهذا رح يأخذ وقت طويل ويستهلك كل موارد الجهاز. ما في طريقة سهلة لتوزيع الحمل على عدة أجهزة.
المنقذ: محركات تنسيق سير العمل (Workflow Orchestration)
بعد ليلة التقارير المشؤومة، قررنا نبحث عن حل جذري. وهنا تعرفنا على عالم جميل اسمه “Workflow Orchestration Engines”.
ببساطة، محرك تنسيق سير العمل هو نظام متخصص في تعريف وتشغيل ومراقبة سلاسل المهام المعقدة (اللي بنسميها DAGs – Directed Acyclic Graphs). فكر فيه كقائد الأوركسترا (المايسترو). إنت بتعطيه النوتة الموسيقية (الكود تبعك)، وهو بيضمن إنه كل عازف (كل مهمة) يعزف في الوقت الصح، وبالطريقة الصح، ولو واحد منهم نشّز، المايسترو بيعرف فوراً وبيتصرف.
كيف غيرت هذه المحركات حياتنا؟
لما تبنينا أداة مثل Apache Airflow أو Prefect، فجأة كل المشاكل اللي حكينا عنها صار إلها حلول جاهزة:
- الرؤية والشفافية (Visibility): صار عنا واجهة رسومية (Dashboard) بتفرجينا كل سير عمل، وكل مهمة فيه، وحالتها (شغالة، نجحت، فشلت، قيد إعادة المحاولة). بطلنا عميان، صرنا نشوف كل شي بوضوح.
- إدارة الاعتماديات بذكاء: بدل ما نعتمد على التوقيت، صرنا نعرّف الاعتماديات بشكل صريح في الكود. “يا مهمة B، لا تشتغلي إلا لما تنجح المهمة A”. الموضوع صار منطقي ومضمون.
- التنبيهات وإدارة الأخطاء: صار بإمكاننا نربط النظام مع Slack أو الإيميل. أي مهمة بتفشل، فوراً بيوصلنا تنبيه على قناة الفريق مع تفاصيل الخطأ. ودّعنا الفشل الصامت إلى الأبد.
- إعادة المحاولة التلقائية: بكل سهولة، صرنا نحدد لكل مهمة: “إذا فشلت، حاول كمان مرتين، وانتظر دقيقة بين كل محاولة”. 90% من المشاكل المؤقتة انحلت لحالها بدون تدخلنا.
- القابلية للتوسع: هذه الأدوات مصممة لتوزيع المهام على عدة “عمال” (Workers)، اللي ممكن يكونوا على سيرفرات مختلفة أو حتى في حاويات Docker. صرنا نقدر نشغل آلاف المهام بالتوازي بدون أي مشاكل.
من النظرية إلى التطبيق: لنبني سير عمل بسيط مع Prefect
الحكي النظري حلو، بس خلينا نشوف مثال عملي. رح أستخدم مكتبة Prefect لأنها سهلة جداً وواضحة للمبتدئين. تخيل إنه بدنا نعيد بناء نظام التقارير تبعنا.
أولاً، بنعرّف كل خطوة على إنها “مهمة” (Task) باستخدام ديكوريتور بسيط اسمه @task. بعدين بنجمعهم في “سير عمل” (Flow) باستخدام @flow.
import httpx
from prefect import task, flow
from typing import List
# المهمة الأولى: سحب بيانات العملاء
@task(retries=3, retry_delay_seconds=10)
def fetch_customer_data() -> List[dict]:
"""
مهمة لسحب بيانات العملاء من API وهمي.
ستتم إعادة المحاولة 3 مرات في حال الفشل.
"""
print("جاري سحب بيانات العملاء...")
# في الواقع، هنا ستتصل بقاعدة بيانات أو API حقيقي
customers = [
{"id": 1, "name": "شركة الأمل", "email": "hope@example.com"},
{"id": 2, "name": "مؤسسة النجاح", "email": "success@example.com"},
]
# محاكاة لخطأ مؤقت في الشبكة
# if random.random() > 0.5:
# raise httpx.RequestError("فشل الاتصال بالشبكة!")
print("تم سحب بيانات العملاء بنجاح.")
return customers
# المهمة الثانية: معالجة البيانات وتوليد محتوى التقرير
@task
def process_data_and_generate_content(customer: dict) -> str:
"""مهمة لتوليد محتوى التقرير لعميل واحد."""
print(f"جاري توليد محتوى التقرير للعميل: {customer['name']}")
report_content = f"تقرير الفواتير الشهري
مرحباً {customer['name']},
هذا هو تقريرك...
"
print(f"تم توليد المحتوى للعميل: {customer['name']}")
return report_content
# المهمة الثالثة: إرسال التقرير عبر الإيميل
@task
def send_report_email(customer: dict, content: str):
"""مهمة لإرسال الإيميل."""
print(f"جاري إرسال التقرير إلى {customer['email']}...")
# هنا تضع كود إرسال الإيميل الحقيقي
print(f"تم إرسال التقرير بنجاح إلى {customer['email']}.")
# سير العمل الرئيسي الذي يجمع كل المهام
@flow(name="End-of-Month Financial Reports", log_prints=True)
def financial_report_workflow():
"""
سير العمل الرئيسي لتوليد وإرسال التقارير المالية الشهرية.
"""
# الخطوة 1: سحب كل العملاء
customers = fetch_customer_data()
# الخطوة 2 و 3: لكل عميل، قم بتوليد المحتوى وإرسال الإيميل
# Prefect ذكي كفاية ليعرف أن هاتين المهمتين يمكن أن تعملا بالتوازي لكل عميل
for customer in customers:
content = process_data_and_generate_content(customer)
send_report_email(customer, content)
# لتشغيل سير العمل
if __name__ == "__main__":
financial_report_workflow()
شفتوا الجمال؟
- في مهمة
fetch_customer_data، حددنا بكل بساطةretries=3. الآن لو فشلت بسبب مشكلة شبكة، رح تحاول لحالها كمان 3 مرات. - الاعتماديات واضحة من الكود نفسه.
process_data_and_generate_contentما رح تشتغل إلا بعد ماfetch_customer_dataترجع نتيجة. - لو شغلنا هالكود، وفتحنا واجهة Prefect، رح نشوف سير العمل تبعنا مرسوم قدامنا، وكل مهمة بلون مختلف حسب حالتها.
- لو فشلت أي مهمة بعد كل المحاولات، رح يوصلني تنبيه على Slack فوراً.
نصيحة أبو عمر: الكود اللي بتكتبه داخل الـ
@taskهو نفس كود البايثون اللي بتكتبه كل يوم. لا تخاف من هذه الأدوات، هي مجرد “غلاف” ذكي حول الكود تبعك، بتعطيه قوة خارقة بدون ما تغير منطقه الأساسي.
نصائح من قلب الميدان (من خبرتي كأبو عمر)
بعد سنوات من استخدام هذه الأنظمة، اسمحولي أقدم لكم شوية نصائح عملية الله يرضى عليكم:
- ابدأ بسيطًا: مش ضروري تحول كل سكربتاتك دفعة واحدة. ابدأ بأكثر سير عمل بيعملك مشاكل، وحوله على نظام أوركسترا. لما تشوف الفائدة، رح تتحمس تكمل للباقي.
- اجعل مهامك “غبية” وقابلة لإعادة التشغيل (Idempotent): هاي أهم نصيحة. لازم تصمم مهمتك بحيث لو اشتغلت 5 مرات بنفس المدخلات، تكون النتيجة النهائية وحدة وما تخرب الدنيا. مثلاً، بدل ما تعمل “إضافة سجل”، اعمل “تأكد من وجود السجل، وأضفه إذا لم يكن موجوداً”. هذا بيخلي إعادة المحاولة آمنة.
- لا تهمل المراقبة والتنبيهات: الأداة بتعطيك الإمكانية، بس إنت اللي لازم تفعّلها. اربطها مع قناة Slack خاصة بالفريق، واكتب رسائل تنبيه واضحة ومفيدة.
- وثّق سير عملك: اكتب تعليقات في الكود، واستخدم أسماء واضحة للمهام وسير العمل. الشخص اللي رح يجي بعدك (اللي ممكن يكون إنت نفسك بعد 6 شهور) رح يدعيلك.
- اختر الأداة المناسبة: عالم الأوركسترا واسع. فيه Airflow وهو العملاق الكلاسيكي والمستقر. وفيه Prefect وهو الحديث والسهل للمطورين. وفيه Temporal و Dagster وغيرها. اقرأ عنهم وشوف شو الأنسب لتقنيات فريقك وحجم مشروعك.
الخلاصة: من الفوضى إلى التنسيق 🎼
الانتقال من فوضى الـ Cron Jobs إلى عالم محركات تنسيق سير العمل كان واحد من أفضل القرارات التقنية اللي أخذناها. حولنا نظام هش، غامض، ومصدر دائم للتوتر، إلى نظام قوي، شفاف، ويمكن الاعتماد عليه. بطلنا نصحى الفجر على تلفونات من مدراء معصبين، وصرنا نثق إنه مهامنا الخلفية بتشتغل زي الساعة.
إذا كنت إنت أو فريقك ما زلتوا بتعانوا من “جحيم المهام الفاشلة بصمت”، فأتمنى تكون هالمقالة فتحت عيونكم على الحل. لا تخافوا من التغيير، لأنه في الجانب الآخر في نوم هنيء وراحة بال. 😉
يلا، شدوا حيلكم، وإذا عندكم أي سؤال، أنا جاهز. بالتوفيق يا جماعة!