العودة إلى المدونة
برمجيات خاصة

ما هو Cron Job؟ تشغيل المهام المجدولة بشكل موثوق

ما هو cron job وكيف تُقرأ عبارة cron؟ لماذا تتعطل المهام المجدولة بصمت مثل التقارير الليلية وإصدار الفواتير والنسخ الاحتياطي: التشغيل المزدوج على عدة خوادم، والمناطق الزمنية، والتشغيلات الفائتة، والمراقبة. عدم التكرار بمفتاح الفترة وعلاقة cron بالطوابير.

Cron JobZamanlanmış GörevBackendÖzel Yazılım

الـ cron job مهمة تعمل من تلقاء نفسها في وقت محدد أو على فترات منتظمة: أخذ نسخة احتياطية كل ليلة في الساعة 02:00، وإرسال تقرير المبيعات كل صباح، وتحديث حالات الشحن كل ساعة، وإصدار فواتير الاشتراكات في أول يوم من الشهر. يأتي اسمها من برنامج cron في يونكس، ويُعرَّف الموعد بعبارة من خمسة حقول بصيغة «الدقيقة الساعة اليوم الشهر يوم-الأسبوع». جدولة المهمة سهلة؛ الصعب هو ضمان ألا تعمل مرتين، وأن يُلاحَظ التشغيل الفائت، وألا تتعطل بصمت. نشرح في هذا المقال عبارة cron، وأكثر أسباب تعطل المهام المجدولة شيوعًا، والإعداد الآمن الذي نطبقه في مشاريع البرمجيات الخاصة.

كيف تُقرأ عبارة cron؟

الحقول الخمسة من اليسار إلى اليمين هي: الدقيقة (0-59)، والساعة (0-23)، ويوم الشهر (1-31)، والشهر (1-12)، ويوم الأسبوع (0-6، والأحد هو 0). النجمة تعني «كل»، والفاصلة تعطي قائمة، والشرطة نطاقًا، والشرطة المائلة خطوة. بعض الأمثلة:

  • 0 2 * * *: كل يوم في الساعة 02:00 (نسخ احتياطي ليلي، تقرير يومي).
  • */15 * * * *: كل 15 دقيقة (مزامنة حالة الشحن أو الدفع).
  • 0 9 * * 1-5: أيام العمل في الساعة 09:00 (ملخص الصباح).
  • 0 0 1 * *: منتصف الليل في أول يوم من كل شهر (الفواتير الشهرية، تجديد الاشتراكات).
  • 30 23 * * 0: كل أحد في الساعة 23:30 (تنظيف أسبوعي، أرشفة السجلات القديمة).

تُفسَّر عبارة cron وفق منطقة زمنية، وهي في الغالب ساعة الخادم. إذا كان الخادم يعمل بتوقيت UTC فإن «0 2 * * *» تعني الساعة 05:00 بتوقيت الرياض. لذلك تُكتب المنطقة الزمنية للمجدول صراحة ولا تُترك للإعداد الافتراضي؛ وفي الدول التي تطبق التوقيت الصيفي قد لا تعمل المهام المجدولة بين 02:00 و03:00 في يوم من أيام السنة أو تعمل مرتين.

أين تعمل المهمة المجدولة؟

  • crontab الخادم: الإعداد الكلاسيكي بخادم واحد. بسيط، لكن المهمة تُنسى عند تغيير الخادم ولا تُدار إصداراتها مع الكود.
  • مجدول داخل التطبيق: مكتبات مثل node-cron وQuartz وHangfire. يعيش مع الكود، لكن إذا عملت عدة نسخ من التطبيق تنطلق المهمة في كل نسخة.
  • مجدول المنصة: Kubernetes CronJob، والمشغلات المجدولة لدى مزودي السحابة، وVercel Cron. المنصة تطلق مرة واحدة، والعمل يبقى في كودك.
  • مهام متكررة في طابور: طوابير مثل BullMQ وSidekiq تحفظ تعريف المهمة المتكررة بنفسها وتوفر إعادة المحاولة وسجل التشغيل جاهزًا.

الخطأ الأكثر شيوعًا: المهمة تعمل مرتين

عندما يتوسع التطبيق إلى خادمين يتضاعف المجدول الداخلي أيضًا، فيُرسل التقرير الليلي مرتين. في التقرير هذا مزعج؛ أما في الفواتير أو أوامر الدفع فهو خطأ جسيم. لا يكفي أن تُطلق المهمة مرة واحدة؛ يجب أن يعطي العمل نفسه النتيجة ذاتها إذا نُفّذ مرتين. أمتن طريقة لذلك هي إعطاء كل تشغيل مفتاح فترة، مثل «الفواتير الشهرية، 2026-10». عند البدء تحاول المهمة إنشاء سجل بهذا المفتاح؛ فإذا كان السجل موجودًا فقد أخذت نسخة أخرى العمل وتخرج هذه النسخة. ويحفظ الجدول نفسه متى بدأت المهمة ومتى انتهت وهل فشلت. إنها الفكرة نفسها التي يقوم عليها معرف الحدث لدى مستقبلي الـ webhook، وقد شرحناها في مقال الـ webhook.

// 1st of every month at 03:00, Türkiye time: '0 3 1 * *' (tz: Europe/Istanbul)
export async function monthlyInvoicesJob(now = new Date()) {
  const period = formatInTimeZone(now, 'Europe/Istanbul', 'yyyy-MM');
  const runKey = 'monthly-invoices:' + period;

  // Unique index on job_runs.run_key: a second instance cannot get in.
  const run = await db.jobRuns.insertIfAbsent({
    runKey,
    startedAt: new Date(),
    status: 'running',
  });
  if (!run) return; // this period is already done or in progress

  try {
    const subs = await db.subscriptions.dueForBilling(period);
    for (const sub of subs) {
      // Invoices are unique per period + subscription: a half-done run restarts safely.
      await billing.createInvoiceOnce(sub.id, period);
    }
    await db.jobRuns.finish(runKey, 'succeeded');
  } catch (err) {
    await db.jobRuns.finish(runKey, 'failed');
    throw err; // let it reach the error tracker
  }
}
أخطر حالة للمهمة المجدولة ليست الفشل بل عدم التشغيل أصلًا: الفشل يُسجَّل ويُرى، أما المهمة التي لم تعمل فتبقى صامتة حتى يلاحظها أحد. كل مهمة حرجة تحتاج إلى مراقبة تجيب عن سؤال «متى كان آخر تشغيل ناجح؟».

التشغيلات الفائتة والمراقبة

إذا أُعيد تشغيل الخادم في موعد المهمة تمامًا، أو كان المجدول متوقفًا أثناء نشر إصدار جديد، فإن ذلك التشغيل يفوت. والـ cron الكلاسيكي لا يعوض التشغيلات الفائتة. لذلك تُحمى المهام الحرجة بطريقتين. أولًا، لا تعالج المهمة «الآن» فقط بل كل فترة لم تُعالج: مهمة الفواتير الشهرية تنجز عمل كل فترة بلا فواتير، لا الشهر الحالي وحده. ثانيًا، تُراقب من الخارج: كل تشغيل ناجح يرسل إشارة إلى عنوان مراقبة، وإذا لم تصل الإشارة في الوقت المتوقع يتلقى الفريق تنبيهًا. شرحنا وجهة الأخطاء والإشارات في مقال تتبع الأخطاء والتسجيل؛ وهذه المراقبة إلزامية لمهام النسخ الاحتياطي، والتفاصيل في مقال النسخ الاحتياطي واستعادة البيانات.

المهام الطويلة والطوابير

الـ cron مُطلِق وليس عاملًا. إصدار فواتير لعشرة آلاف مشترك في حلقة واحدة يترك الأمر غامضًا عند حدوث خطأ في المنتصف حول الفواتير التي صدرت، وقد يتصادم مع الإطلاق التالي. التصميم الصحيح أن يضع الـ cron العمل في طابور فقط، وأن يعالج عمال الطابور كل عنصر على حدة مع إعادة المحاولة. بهذا لا يوقف خطأ لدى مشترك واحد الباقين، ويتوزع الحمل على اليوم. وتنطبق القاعدة نفسها على المهام المعتمدة على خدمة خارجية مثل الفوترة الإلكترونية: عندما يبطئ الطرف الآخر ينتظر الطابور بدل أن تنتهي مهلة المهمة؛ تناولنا ذلك في مقال تكامل الفوترة الإلكترونية. أما وضع المهام في خدمة مستقلة أو في التطبيق نفسه فقرار معماري؛ راجع مقال الخدمات المصغرة أم التطبيق الموحد.

قائمة التحقق قبل التشغيل

  • هل كُتبت المنطقة الزمنية صراحة، وهل تم تجنب الساعات التي تقع عند تغيير التوقيت الصيفي؟
  • عند تشغيل عدة نسخ من التطبيق، هل ما زالت المهمة تُطلق مرة واحدة فقط؟
  • هل يعطي العمل النتيجة نفسها إذا نُفّذ مرتين (مفتاح الفترة، الفهرس الفريد)؟
  • هل تُعوَّض الفترة الفائتة في التشغيل التالي؟
  • هل يُراقب آخر تشغيل ناجح ويُطلق تنبيهًا عند التأخر؟
  • هل يُدار تعريف المهمة مع الكود ويُختبر في مسار CI/CD؟

الخلاصة

الـ cron job جزء صغير يؤدي بدل الإنسان الأعمال التي «لا يجوز نسيانها»؛ لكن صغر حجمه لا يبرر تركه بلا رقابة. المهمة المجدولة الآمنة تُعرَّف بمنطقة زمنية صريحة، وتعمل في نسخة واحدة، ولا تسبب ضررًا إذا عملت مرتين، وتعوض الفترات الفائتة، وتُبلغ أحدًا عندما لا تعمل. إذا أردت بناء مهام الفوترة والتقارير والمزامنة والنسخ الاحتياطي على هذه المبادئ، فاطلع على خدمة البرمجيات الخاصة أو اطلب عرض سعر مباشرة.

Let's Build Your Project

Get a free consultation for your website, mobile app, or corporate software project.

Get a Free QuoteExplore our Custom Software service