أتمتة اختبار PostgreSQL Backup قبل ما تحتاجه في كارثة
هتخرج من المقال ده بأوتوميشن بسيط يجاوب على سؤال أهم من "هل اتعمل backup؟": هل النسخة دي ينفع نرجّع منها الإنتاج فعلًا؟
مستوى القارئ: متوسط
المشكلة باختصار
الطريقة الشائعة بتفشل في نقطة قاتلة: فريق التشغيل بيتأكد إن ملف الـ backup اتولد، لكن لا يتأكد إن الملف قابل للاسترجاع. الملف ممكن يكون ناقص، أو معمول بإصدار غير مناسب، أو فيه schema بتفشل وقت restore. اللي بيحصل فعلاً إن المشكلة تظهر يوم الكارثة، وده أسوأ توقيت للاكتشاف.
الافتراض إن عندك PostgreSQL production صغير أو متوسط، أقل من 200GB، وبتستخدم pg_dump أو dump archive يومي. لو عندك 50K زائر يوميًا، توقف قاعدة البيانات ساعة واحدة ممكن يضيع طلبات ودفع ودعم عملاء. لذلك أفضل طريقة هنا مش زيادة عدد النسخ فقط، بل اختبار نسخة واحدة يوميًا على قاعدة مؤقتة.
الفكرة بمثال واضح
ركز في المثال ده. عندك مطعم بيسجّل كل الطلبات في دفتر. كل ليلة بتصور الدفتر وتحفظ الصورة. ده جيد، لكنه لا يثبت إن الصورة مقروءة. الاختبار الحقيقي إنك تفتح الصورة، تقرأ آخر 10 طلبات، وتتأكد إن أرقام اليوم متطابقة. في PostgreSQL، الصورة هي ملف .dump، والقراءة هي pg_restore على قاعدة مؤقتة، وفحص آخر الطلبات هو smoke query.
المفهوم بدقة: أوتوميشن التحقق من الـ backup يعمل restore معزول، ثم ينفذ فحوصات صغيرة لكنها ممثلة. مثال: عدد المستخدمين أكبر من صفر، جدول الطلبات موجود، آخر طلب خلال آخر 48 ساعة، واستعلام API مهم يرجع نتيجة. كده أنت لا تختبر التخزين فقط، بل تختبر قابلية الرجوع.
السكربت العملي
احفظ السكربت التالي باسم verify_pg_backup.sh. عدّل أسماء المتغيرات حسب بيئتك. السكربت ينشئ قاعدة مؤقتة، يسترجع آخر dump، ينفذ فحصين، ثم يحذف القاعدة في النهاية.
#!/usr/bin/env bash
set -euo pipefail
BACKUP_FILE="/backups/app-latest.dump"
TEMP_DB="restore_check_$(date +%Y%m%d_%H%M%S)"
PG_URL="postgresql://postgres:postgres@localhost:5432/postgres"
SLACK_WEBHOOK_URL="${SLACK_WEBHOOK_URL:-}"
started_at=$(date +%s)
created=0
cleanup() {
if [ "$created" = "1" ]; then
dropdb "$TEMP_DB" --if-exists
fi
}
trap cleanup EXIT
createdb "$TEMP_DB"
created=1
pg_restore --dbname="$TEMP_DB" --clean --if-exists --no-owner "$BACKUP_FILE"
users_count=$(psql "$TEMP_DB" -tAc "select count(*) from users;")
recent_orders=$(psql "$TEMP_DB" -tAc "select count(*) from orders where created_at >= now() - interval '48 hours';")
if [ "$users_count" -lt 1 ]; then
status="FAIL: users table is empty after restore"
elif [ "$recent_orders" -lt 1 ]; then
status="WARN: no recent orders in restored backup"
else
elapsed=$(( $(date +%s) - started_at ))
status="PASS: restore completed in ${elapsed}s, users=${users_count}, recent_orders=${recent_orders}"
fi
printf '%s\n' "$status"
if [ -n "$SLACK_WEBHOOK_URL" ]; then
curl -sS -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"PostgreSQL backup verification: ${status}\"}" \
"$SLACK_WEBHOOK_URL" >/dev/null
fi
case "$status" in
PASS:*) exit 0 ;;
WARN:*) exit 2 ;;
*) exit 1 ;;
esac