الـ cron بيجدوِل المهام، بس ما بيقولّكش لما واحدة منها فشلت. الموضوع ده بيتحول لكارثة لمّا الـ backup يوقف أسبوعين وانت فاكره شغّال، وتكتشف يوم ما قاعدة البيانات تقع. الدليل ده بيحل المشكلة دي في 15 دقيقة بسكربت bash واحد و Healthchecks.io.
ليه الـ cron jobs بتفشل في صمت؟
خلّيني أبدأ بمثال حقيقي. عندك سيرفر VPS فيه cron بيعمل backup يومي الساعة 3 فجرًا. كتبته مرة واحدة، شغّلته، اتأكدت إنه بيعمل نسخة، ونسيت الموضوع. بعد 6 شهور، الـ script اتكسر لأن كلمة سر الـ database اتغيّرت وما حدّش حدّث ملف .env. الـ cron نفّذ المهمة كل يوم، فشل كل يوم، وما قال لحد. يوم ما احتجت الـ backup، لقيت آخر نسخة ناجحة من 6 شهور.
ده اللي بيحصل فعلاً: الـ cron بينفّذ السطر اللي إنت طالبه، ولو السطر رجّع exit code 1، الـ cron بيدوّن في /var/log/syslog وبس. مفيش إيميل (إلا لو معمول إعداد MTA)، مفيش تنبيه، مفيش لوحة. الـ cron بيديك جدولة من غير visibility.
المفهوم العلمي: Heartbeat Monitoring (Dead Man's Switch)
الحل الصحيح اسمه Heartbeat Monitoring، أو "مفتاح الرجل الميت". المبدأ بسيط: بدل ما الخدمة تسأل "هل أنت مصاب؟"، الخدمة بتستنى منك إشارة كل فترة. لو الإشارة ما جتش، معناها إن في مشكلة.
تخيّل إنك بتكلم صاحبك كل يوم الساعة 10 صباحًا. لو يوم ما كلّمكش، هتقلق. ده بالظبط اللي Healthchecks.io بيعمله: بتقوله "هيوصلك HTTP request من الـ job بتاعي كل 24 ساعة، مع تسامح 30 دقيقة". لو ما وصلش، بيبعت تنبيه.
الخطوات التنفيذية — 15 دقيقة من الصفر لإنتاج
- روح
healthchecks.ioواعمل حساب مجاني. الخطة المجانية بتغطّي حتى 20 check، وده بيكفي غالبية المشاريع الصغيرة والمتوسطة. - اعمل check جديد. حدّد الـ schedule (كرون expression زي
0 3 * * *) و Grace Time (المدة المسموحة قبل التنبيه). - خُد الـ ping URL اللي هيظهرلك. شكله
https://hc-ping.com/UUID. - وصّل الـ URL ده بـ notification channel: Telegram، Discord، Slack، إيميل، أو SMS.
- عدّل سكربت الـ cron بتاعك عشان يعمل ping بعد النجاح فقط.
السكربت الكامل — backup مع heartbeat
#!/usr/bin/env bash
set -euo pipefail
PING_URL="https://hc-ping.com/your-uuid-here"
BACKUP_DIR="/var/backups/postgres"
DATE=$(date +%Y-%m-%d)
# ping start signal — بيسجّل إن الـ job بدأ
curl -fsS -m 10 --retry 3 "$PING_URL/start" > /dev/null
# المهمة الفعلية
pg_dump -U postgres mydb | gzip > "$BACKUP_DIR/backup-$DATE.sql.gz"
# رفع على S3
aws s3 cp "$BACKUP_DIR/backup-$DATE.sql.gz" "s3://my-backups/"
# ping success — بس لو وصلنا لهنا من غير أخطاء (بفضل set -e)
curl -fsS -m 10 --retry 3 "$PING_URL" > /dev/null