تخيل بتسجل طلب شراء وبتقلل المخزون لو المخزون اتغير لكن تسجيل الطلب فشل هتلاقي البيانات بتحكي قصتين مختلفتين هنا بنحتاج Database Transaction علشان التغييرات المرتبطة تنجح مع بعض أو تتراجع مع بعض
معنى Begin و Commit و Rollback
بداية المعاملة بتحدد مجموعة التغييرات اللي هنتعامل معاها كوحدة واحدة ال Commit بتثبت التغييرات وال Rollback بتلغي التغييرات غير المثبتة داخل المعاملة حسب إمكانيات محرك قاعدة البيانات
المثال عن تغييرات قاعدة البيانات نفسها مش أي أثر خارجي البرنامج يعمله أثناء التنفيذ
مثال آمن للتجربة في الذاكرة
الكود التالي بيستخدم SQLite داخل الذاكرة فقط وبيجهز جدول تجريبي للمخزون هننقص الكمية ثم نتعمد حدوث خطأ ونشوف هل الكمية رجعت يستخدم المثال إضافة PDO SQLite في PHP
<?php
$db = new PDO('sqlite::memory:');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->exec('CREATE TABLE stock (sku TEXT PRIMARY KEY, quantity INTEGER NOT NULL)');
$db->exec("INSERT INTO stock VALUES ('book', 2)");
try {
$db->beginTransaction();
$db->exec("UPDATE stock SET quantity = quantity - 1 WHERE sku = 'book'");
throw new RuntimeException('Order could not be saved');
$db->commit();
} catch (Throwable $error) {
if ($db->inTransaction()) {
$db->rollBack();
}
echo "Rolled back" . PHP_EOL;
}
echo $db->query("SELECT quantity FROM stock WHERE sku = 'book'")->fetchColumn() . PHP_EOL;Rolled back
2الكمية فضلت 2 لأن التغيير اتراجع السطر اللي بيرمي الخطأ موجود للتجربة فقط في تطبيق حقيقي الفشل ممكن يحصل أثناء تسجيل الطلب أو التحقق من قاعدة مهمة
ولما كل الخطوات تنجح
المثال المستقل التالي بيثبت التغيير بدل التراجع عنه
<?php
$db = new PDO('sqlite::memory:');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->exec('CREATE TABLE stock (sku TEXT PRIMARY KEY, quantity INTEGER NOT NULL)');
$db->exec("INSERT INTO stock VALUES ('book', 2)");
try {
$db->beginTransaction();
$db->exec("UPDATE stock SET quantity = quantity - 1 WHERE sku = 'book'");
$db->commit();
} catch (Throwable $error) {
if ($db->inTransaction()) $db->rollBack();
throw $error;
}
echo $db->query("SELECT quantity FROM stock WHERE sku = 'book'")->fetchColumn() . PHP_EOL;1المعاملة مش كفاية لكل مشاكل التزامن
لو شخصان بيشتروا آخر نسخة في نفس الوقت فقراءة الكمية ثم تقليلها من غير حماية مناسبة ممكن تؤدي لقرارين مبنيين على نفس القيمة القديمة
ممكن تحتاج قفل الصف أو تحديث مشروط يتحقق إن الكمية تكفي مع فحص عدد الصفوف المتأثرة داخل المعاملة الطريقة المناسبة بتعتمد على قاعدة البيانات ومستوى العزل وحالة الاستخدام
الإيميل والملف مش بيرجعوا مع قاعدة البيانات
لو بعت رسالة بريد ثم حصل Rollback الرسالة مش هتختفي من صندوق المستلم علشان كده افصل الإجراءات الخارجية وخطط لتنفيذها بعد نجاح الحفظ
لو تسليم الإجراء بعد الحفظ مهم جدا ففكر في سجل مهام محفوظ داخل نفس المعاملة زي Outbox ثم عامل ينفذه مع التعامل مع إعادة المحاولة بدل افتراض إن خطوة بعد Commit مستحيل تفشل
خلي المعاملة قصيرة
متفتحش معاملة وتقعد تستنى رد مستخدم أو طلب شبكة طويل لأنها ممكن تحتفظ بأقفال وتأخر شغل تاني اجمع المعلومات غير الحساسة للتزامن قبلها ثم نفذ الجزء المرتبط بالبيانات في وقت قصير
إزاي تختبرها
جرب النجاح وفشل خطوة في المنتصف وتأكد إن كل الجداول المرتبطة رجعت لحالتها السابقة اختبر ده على قاعدة معزولة وراجع التزامن بمحرك مناسب قبل الاعتماد على الحل في مشروع حقيقي
متستخدمش مسح قاعدة المشروع كطريقة لتجربة المعاملات المثال هنا مقصود يكون في الذاكرة علشان التجربة مستقلة عن أي بيانات فعلية