لماذا تعتبر ()lockForUpdate مهمة في أنظمة Laravel المالية والحرجة؟

لماذا تعتبر ()lockForUpdate مهمة في أنظمة Laravel المالية والحرجة؟

تتعامل الأنظمة الحديثة عادةً مع بيانات حساسة تحتاج إلى دقة عالية سواء عند تعديل رصيد مستخدم، أو حجز كمية من المخزون أثناء عملية الشراء، أو تحديث حالة طلب، أو معالجة المهام عبر الـ Queues. هذه العمليات لا تتحمّل أي تعارض أو سباق في الوصول. ومن خلال التجربة العملية في مثل هذه البيئات، اتّضح لي مدى أهمية استخدام lockForUpdate() لحماية السجلات التي تتعرض لتعديل متزامن.

عندما تحاول خدمات متعددة، أو Workers، أو طلبات مستخدمين الوصول إلى نفس السجل الحرج وتعديله في الوقت نفسه، يمكن لسباق بسيط أن يؤدي إلى مشاكل كبيرة مثل الخصم المكرر، أو إجمالي غير صحيح، أو حالات معاملات غير صالحة. يضمن القفل على مستوى الصف أن تنفَّذ كل عملية بشكل حصري ومن دون تأثير من أي عملية أخرى تعمل بالتوازي.

كيف تعمل ()lockForUpdate؟

عند استخدامها داخل DB::transaction() مثل:

$wallet = Wallet::where('user_id', $id)->lockForUpdate()->first();

يقوم Laravel (أو MySQL تحديداً) بقفل الصف المطلوب بشكل حصري، مما يعني:

  • لو حاول مستخدمان تعديل نفس المحفظة في نفس اللحظة،
    واحد فقط سينفّذ العملية، والثاني ينتظر.

  • لا يمكن لأي عملية أخرى تعديل هذا الصف لحين انتهاء الـ Transaction.

  • بهذا تتجنّب حالات السباق التي قد تجعل الرصيد بالسالب أو تؤدي لتحديثات غير صحيحة.

متى نستخدم كل نوع من القفل؟

  • ()lockForUpdate
    نستخدمه عندما نريد تعديل بيانات حساسة مثل الرصيد، الكميات، حالات الطلبات، أو العدّادات.

  • ()sharedLock
    نستخدمه عندما نريد قراءة بيانات بثبات بدون تعديلها، مع منع العمليات الأخرى من الكتابة أثناء القراءة.

لماذا يجب استخدامه دائمًا داخل Transaction؟

قاعدة مهمة جداً:

  • كل استعلام في MySQL = Transaction مستقلة (Auto-commit).

  • لذلك، إذا استخدمت القفل بدون Transaction،
    سيُحرّر القفل فوراً بعد انتهاء الاستعلام، ويعود خطر السباق من جديد.

لكن داخل DB::transaction():

  • يستمر القفل حتى تنفيذ Commit أو Rollback.

  • وهذا يضمن سلامة البيانات ومنع أي حالات تعارض.

تجربة واقعية

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

المشكلة تُسمّى Race Condition أو "السباق".
واستخدام lockForUpdate() داخل Transaction يضمن Data Integrity، لأنه يمنع أي عملية أخرى من قراءة أو تعديل نفس الصف حتى انتهاء العملية الحالية.

الخلاصة

lockForUpdate() ليست مجرد دالة… إنها جزء أساسي من الأمان المنطقي للتطبيق.
استخدامها بشكل صحيح داخل الـ Transaction يحافظ على سلامة البيانات ويمنع التعارض، خصوصاً في الأنظمة المالية أو الأنظمة الحساسة التي تعتمد على دقة عالية.