labZero:~$ ESC
  • لا نتائج…
↑↓ تنقّل·Enter اذهب·⌘K

labZero:~$ cd ./notes / note-queue-job-idempoten...

الملاحظات
نسخة صفر 1 يونيو 2026

Job نجح — ثم نُفّذ مرة ثانية. من المسؤول؟

مهمة إنشاء فاتورة نُفّذت مرتين بعد failure جزئي — Job نجح في الكتابة وفشل في تحديث الحالة، فظنّ الطابور أنه فشل.

#Laravel #Queues #Architecture

// في هذا المقال

في نظام فوترة تلقائية، Job يُنشئ فاتورة ويرسل بريداً ويُحدّث invoice_status. في أحد الأيام وجدت فاتورتين لنفس الطلب وبريدين للعميل نفسه. راجعت logs: الـ Job نجح في إنشاء الفاتورة ثم فشل قبل تحديث الحالة بخطأ شبكة في إرسال البريد. الطابور اعتبره فاشلاً وأعاده. الإعادة أنشأت فاتورة ثانية.

الجوهر: المشكلة في غياب idempotency — الـ Job لم يكن آمناً للتنفيذ مرتين. كل job يعدّل بيانات يجب أن يسأل: «ماذا يحدث لو نُفّذت مرتين؟» الحل في هذه الحالة مفتاح تفرد على مستوى البيانات:

Invoice::firstOrCreate(
    ['order_id' => $this->orderId, 'period' => $this->period],
    ['amount' => $this->amount, 'status' => 'pending']
);

بهذا الشكل، التنفيذ الثاني يجد الفاتورة موجودة ولا يُنشئ نسخة. للبريد أضفت فحصاً: if ($invoice->wasRecentlyCreated) قبل الإرسال — فقط الفاتورة الجديدة تُرسل بريداً.

المبدأ الذي أطبقه الآن: كل Job يكتب بيانات يُصمَّم كأنه سيُنفَّذ مرتين. الفشل الجزئي في الطوابير ليس استثناءً نادراً — هو حالة طبيعية في أي نظام distributed. idempotency ليست feature إضافية، بل شرط لأي عملية تمرّ عبر queue في الإنتاج.

لديك مشكلة مشابهة؟ باب التعاون من هنا ‹