Autocommit و Savepoints و DDL
3 حاجات بتعمل مفاجآت في الشغل الحقيقي: إزاي تلغي جزء من ترانزاكشن من غير ما تلغيها كلها، والجمل اللي بتعمل COMMIT من وراك في MySQL، وليه Migration فشلت في النص بتسيب الداتابيز في PostgreSQL سليمة وفي MySQL نصها متعدّل.
- تستخدم Savepoints عشان تلغي جزء من الترانزاكشن
- تعرف الجمل اللي بتعمل Implicit Commit في MySQL
- تفهم Transactional DDL في PostgreSQL وليه بيفرق في الـ Migrations
- تعرف الحاجات اللي مينفعش تتعمل جوه ترانزاكشن
1. Savepoints
مثال: استيراد 1000 منتج في ترانزاكشن واحدة. لو منتج واحد فيه مشكلة، مش عايز تلغي الـ 999 التانيين:
BEGIN;
INSERT INTO products (sku, name) VALUES ('A-1', 'شاحن');
SAVEPOINT row_2;
INSERT INTO products (sku, name) VALUES ('A-1', 'سماعة'); -- ERROR: duplicate sku
ROLLBACK TO SAVEPOINT row_2; -- نرجع لقبل المنتج ده بس
SAVEPOINT row_3;
INSERT INTO products (sku, name) VALUES ('A-3', 'كابل');
RELEASE SAVEPOINT row_3; -- خلاص مش محتاجين العلامة
COMMIT; -- الشاحن والكابل اتثبتوا، والسماعة لأفي PostgreSQL ده الحل الوحيد إنك تكمّل بعد Error جوه ترانزاكشن، لأن من غيره الترانزاكشن كلها بتقفل (شفناه في الدرس الأول). وفي Laravel، كل DB::transaction جوه DB::transaction تانية بيتعمل Savepoint أوتوماتيك.
بس خد بالك: الـ Savepoints مش ببلاش. في PostgreSQL كل Savepoint بيعمل Subtransaction ليها رقم، ولو الترانزاكشن فيها أكتر من 64 Subtransaction شغالين، الأداء بيقع بشكل ملحوظ على السيرفر كله (مشكلة معروفة باسم Subtransaction SLRU overflow). يعني Savepoint لكل صف في Import بمليون صف = فكرة وحشة. قسّم الشغل لـ Batches بدل كده.
الـ Savepoint بيخليك تلغي جزء من الترانزاكشن. استخدمه للأخطاء المتوقعة، مش كـ try/catch حوالين كل صف.
2. الجمل اللي بتعمل COMMIT من وراك (MySQL)
في MySQL فيه جمل لو اتنفذت جوه ترانزاكشن، بتعمل COMMIT للترانزاكشن الحالية قبلها من غير ما تقولك. ده اسمه Implicit Commit:
| النوع | أمثلة |
|---|---|
| DDL | CREATE TABLE، ALTER TABLE، DROP TABLE، CREATE INDEX، TRUNCATE، RENAME TABLE |
| إدارة | CREATE USER، GRANT، ANALYZE TABLE، OPTIMIZE TABLE |
| تحكم في الترانزاكشن | START TRANSACTION / BEGIN جديد، SET autocommit = 1، LOCK TABLES |
START TRANSACTION; UPDATE orders SET status = 'cancelled' WHERE created_at < '2026-01-01';12,840 rows1ALTER TABLE orders ADD COLUMN cancel_note VARCHAR(255);OK · Implicit COMMIT2-- اكتشفنا إن الشرط غلط! ROLLBACK;OK · مفيش حاجة اترجعت3- 4
SELECT status FROM orders WHERE ...;cancelled
BEGIN; UPDATE orders SET status = 'cancelled' WHERE created_at < '2026-01-01';UPDATE 128401ALTER TABLE orders ADD COLUMN cancel_note text;ALTER TABLE2ROLLBACK;ROLLBACK3- 4
SELECT status FROM orders WHERE ...;pending
دوس ▶ شغّل، أو امشي خطوة خطوة بالأسهم.
3. الـ Migrations
ده بيفرق جداً في الـ Migrations. تخيّل Migration فيها 3 خطوات: تضيف عمود، وتنقل داتا ليه، وتمسح العمود القديم. والخطوة التانية فشلت:
| PostgreSQL | MySQL | |
|---|---|---|
| الـ Migration بتتنفذ في ترانزاكشن؟ | آه (Laravel بيعمل كده افتراضياً) | لأ، مينفعش |
| لو خطوة فشلت | كل الـ Migration بتتلغى، والجدول زي ما كان | الخطوة الأولى اتثبتت، والجدول نصه متعدّل |
| تعيد تشغيلها | عادي | بتفشل لأن العمود موجود، ولازم تصلح بإيدك |
وده سبب نصيحة مهمة لـ MySQL: Migration واحدة = تغيير واحد في الـ Schema، عشان لو فشلت يبقى سهل تعرف وقفت فين.
4. حاجات مينفعش تتعمل جوه ترانزاكشن
حتى في PostgreSQL، فيه أوامر بترفض تشتغل جوه BEGIN:
| الأمر | ليه |
|---|---|
CREATE INDEX CONCURRENTLY |
بيحتاج يستنى ترانزاكشنز تانية تخلص على مرحلتين |
VACUUM |
بيشتغل برة أي ترانزاكشن |
CREATE DATABASE / DROP DATABASE |
على مستوى السيرفر |
ALTER TYPE ... ADD VALUE (إصدارات قديمة) |
قبل PostgreSQL 12 مكانش مسموح جوه ترانزاكشن |
وفي Laravel على PostgreSQL، أي Migration فيها حاجة من دول لازم تقفل الترانزاكشن بتاعتها:
return new class extends Migration
{
public $withinTransaction = false; // الـ Migration دي مش هتتلف في ترانزاكشن
public function up(): void
{
DB::statement('CREATE INDEX CONCURRENTLY orders_user_created ON orders (user_id, created_at)');
}
};في MySQL الـ DDL بيقفل الترانزاكشن، والـ Migration اللي فشلت بتسيب الجدول نصه متعدّل. في PostgreSQL الـ DDL بيتلغي مع الـ ROLLBACK، ما عدا أوامر زي CONCURRENTLY.
5. أسئلة الإنترفيو
فكّر في إجابتك الأول، وبعدين افتح السؤال.
01يعني إيه Savepoint؟ وإمتى تستخدمه؟
علامة جوه الترانزاكشن تقدر ترجعلها بـ ROLLBACK TO SAVEPOINT من غير ما تلغي الترانزاكشن كلها. بتستخدمه لما جزء من الشغل ممكن يفشل وعايز تكمل الباقي، وفي PostgreSQL هو الطريقة الوحيدة تكمل بعد Error جوه ترانزاكشن. وفي Laravel، الترانزاكشن جوه ترانزاكشن بتبقى Savepoint.
02يعني إيه Implicit Commit في MySQL؟
جمل زي CREATE و ALTER و DROP و TRUNCATE و LOCK TABLES و START TRANSACTION بتعمل COMMIT للترانزاكشن الحالية قبل ما تتنفذ، من غير أي تنبيه. فأي ROLLBACK بعدها مش بيرجّع اللي اتعمل قبلها.
03ليه Migration فشلت في النص أخطر في MySQL من PostgreSQL؟
PostgreSQL بيدعم Transactional DDL، فالـ Migration كلها بتتلف في ترانزاكشن، ولو فشلت كل تغييراتها بتتلغى. في MySQL كل DDL بيتثبت لوحده، فالخطوات اللي نجحت بتفضل، والجدول يفضل نصه متعدّل، وإعادة تشغيل الـ Migration بتفشل.
04ليه Savepoint لكل صف في Import كبير فكرة وحشة في PostgreSQL؟
كل Savepoint بيعمل Subtransaction، ولو الترانزاكشن فيها أكتر من 64 Subtransaction، PostgreSQL بيدخل في Subtransaction overflow والأداء بيقع على السيرفر كله. الأحسن تقسم الـ Import لـ Batches في ترانزاكشنز منفصلة.