بنناجح
المرحلة 3: في الشغل · الدرس 4 من 4 · 25 دقيقة

أسئلة إنترفيو وملخص

أسئلة بمستوى إنترفيوهات الـ Backend في الشركات الكبيرة عن الترانزاكشنز، من Mid لحد Staff، وكل سؤال إجابته بتربط النظرية بالشغل الحقيقي. وفي الآخر ملخص للموضوع كله في كروت صغيرة.

في الدرس ده هتتعلم
  • تجاوب أسئلة الترانزاكشنز من Mid لحد Staff
  • تراجع الموضوع كله في ملخص واحد

1. أسئلة إنترفيو

فكّر في كل سؤال الأول، وبعدين افتح الإجابة.

Mid

عندك endpoint بيعمل أوردر ويخصم من المحفظة. ساعات بيتعمل أوردر من غير خصم. إيه الأسباب المحتملة؟

  • الجمل مش في ترانزاكشن واحدة: كل جملة Autocommit لوحدها، فلو حاجة وقعت أو رمت Exception بين الاتنين، الأوردر يفضل.
  • الترانزاكشن موجودة بس الإلغاء مش بيحصل: return false أو catch بيبلع الـ Exception جوه DB::transaction، فبيحصل COMMIT للي نجح.
  • MySQL: جملة فشلت، والكود كمّل، فاتثبت الباقي (MySQL بيلغي الجملة بس مش الترانزاكشن).
  • Connection مختلفة: المحفظة على Connection تانية، فهي بره الترانزاكشن.
  • DDL في النص في MySQL: عمل Implicit Commit.
  • الحل: ترانزاكشن واحدة بـ DB::transaction، وأي فشل = Exception، والخصم ذري بشرط balance >= total، و CHECK (balance >= 0).
Mid

ايه الفرق بين PostgreSQL و MySQL لما جملة تفشل جوه ترانزاكشن؟

  • PostgreSQL: الترانزاكشن كلها بتدخل حالة Aborted، وأي جملة بعدها ترفض بـ current transaction is aborted، والـ COMMIT بيتحول ROLLBACK. عشان تكمّل لازم Savepoint قبل الجملة اللي ممكن تفشل.
  • MySQL: الجملة اللي فشلت بس بتتلغى (Statement Rollback)، والترانزاكشن بتكمل، والـ COMMIT بيثبّت اللي نجح. ما عدا الـ Deadlock اللي بيلغي الترانزاكشن كلها.
  • النتيجة: الكود لازم يتعامل مع أي Error على إنه ROLLBACK كامل، وده اللي DB::transaction بيعمله.
Senior

اشرح إزاي الداتابيز بتضمن إن COMMIT مايضيعش لو الكهربا قطعت بعده بمللي ثانية.

  • Write-Ahead Logging: كل تعديل بيتوصف في سجل في Log متسلسل (WAL في PostgreSQL، Redo Log في InnoDB). والـ COMMIT مايترد عليهوش غير بعد ما سجل الـ COMMIT يتكتب ويتعمل له fsync على الديسك.
  • الصفحات نفسها بتفضل Dirty في الذاكرة وبتتكتب بعدين (No-Force)، في الـ Checkpoints.
  • بعد الـ Crash: الـ Recovery بيقرا الـ Log من آخر Checkpoint ويعيد التعديلات (Redo).
  • الأداء: الكتابة المتسلسلة + Group Commit (fsync واحد لكذا COMMIT).
  • الـ Torn Pages: Doublewrite Buffer في InnoDB، و full_page_writes في PostgreSQL.
  • والـ Durability ممكن تتضعف بإعدادات (innodb_flush_log_at_trx_commit = 2، synchronous_commit = off)، وفيها خسارة آخر ثانية.
Senior

ليه الـ ROLLBACK لترانزاكشن ضخمة سريع في PostgreSQL وبطيء في MySQL؟

  • InnoDB بيعدّل الصفوف مكانها ويحفظ القديم في الـ Undo Log. الـ ROLLBACK لازم يطبّق الـ Undo صف صف بالعكس، فالوقت بيتناسب مع حجم الترانزاكشن (وأحياناً أكتر)، والـ Locks ماسكة طول الوقت ده.
  • PostgreSQL مابيعدّلش القديم، بيعمل نسخ جديدة عليها رقم الترانزاكشن. الـ ROLLBACK بيعلّم الترانزاكشن Aborted في pg_xact وخلاص، والنسخ بقت Dead Tuples والـ VACUUM ينضفها.
  • يعني PostgreSQL بيأجّل التمن للـ VACUUM، و InnoDB بيدفعه وقت الـ ROLLBACK.
Senior

اشرح ARIES. وليه بيعمل Redo لتعديلات ترانزاكشن هيلغيها بعد كده؟

  • 3 مراحل: Analysis (من آخر Checkpoint: مين خلص ومين لأ، وأنهي صفحات Dirty)، و Redo (يعيد كل سجلات الـ Log على الصفحات اللي pageLSN بتاعها أقدم)، و Undo (يلغي الترانزاكشنز اللي ماخلصتش بالعكس، ويسجل كل خطوة كـ CLR).
  • Repeating History: الـ Redo بيرجّع الداتابيز بالظبط لحالتها لحظة الـ Crash، بما فيها تعديلات اللي هتتلغى. ده بيبسّط الـ Redo (طبّق كل حاجة) ويخلّي الـ Undo يشتغل على حالة معروفة ومتسقة، وبيدعم Row-level Locks و Logical Undo.
  • الـ CLRs بتخلّي الـ Recovery نفسه آمن لو وقع في النص.
  • الداتابيز محتاجة Undo و Redo لأنها بتستخدم Steal (صفحات ترانزاكشن ماخلصتش ممكن تتكتب) و No-Force (صفحات اللي خلصت مش لازم تتكتب وقت الـ COMMIT).
Senior

Endpoint بيعمل أوردر وبينادي Payment API جوه نفس الترانزاكشن. إيه المشاكل وإزاي تعيد تصميمه؟

  • المشاكل: الـ Locks والـ Connection محجوزين طول وقت الـ API، وتحت الضغط الـ Pool يخلص والموقع كله يبطأ. ولو الترانزاكشن عملت Retry بعد Deadlock، الدفع ممكن يتكرر. ولو الـ API نجح والترانزاكشن فشلت بعده، العميل دفع ومفيش أوردر.
  • التصميم: (1) ترانزاكشن قصيرة: احجز المخزون واعمل أوردر pending ومعاه Idempotency Key للدفع. (2) نادي الـ API بره الترانزاكشن بالـ Idempotency Key. (3) ترانزاكشن قصيرة تانية: paid أو failed + رجّع المخزون. (4) Job بيلف على الأوردرات الـ pending القديمة ويسأل الـ Payment Provider عن حالتها ويكمّلها.
  • والإيميلات والـ Events بعد الـ COMMIT (Outbox أو afterCommit).
Senior

اشرح مشكلة الـ Dual Write وحلها بالـ Outbox.

  • عملية بتكتب في الداتابيز وفي Queue (أو أي نظام تاني): مفيش ترتيب آمن، لأن أي واحد فيهم ممكن يفشل بعد ما التاني نجح.
  • Outbox: اكتب الـ Event في جدول outbox في نفس الترانزاكشن مع الداتا. Relay (Polling بـ SKIP LOCKED أو CDC زي Debezium على الـ WAL أو الـ Binlog) يقرا ويبعت للـ Queue ويعلّم اللي اتبعت.
  • ده بيدّي At-least-once: الـ Relay ممكن يبعت مرتين لو وقع بين الإرسال والتعليم.
  • فالـ Consumers لازم يبقوا Idempotent: event_id بيتسجل بـ Unique في نفس ترانزاكشن الشغل (Inbox)، والتكرار يتجاهل.
Staff

الفرق بين 2PC و Saga؟ وتختار إيه في نظام Microservices للأوردرات والدفع والشحن؟

  • 2PC: Coordinator و Prepare ثم Commit. Atomicity حقيقية، بس Blocking (لو الـ Coordinator وقع بعد الـ Prepare، الـ Locks تفضل)، وكل الأطراف لازم تدعمه، و Latency أعلى. نادراً ما بيتستخدم بين Microservices، وأغلب الـ Queues والـ APIs مابتدعموش.
  • Saga: سلسلة ترانزاكشنز محلية كل واحدة COMMIT لوحدها، و Compensations بالعكس لو حاجة فشلت. مفيش Locks طويلة، بس مفيش Isolation: الحالات الوسطانية (pending، reserved) ظاهرة للكل، ولازم تتصمم صراحة.
  • الاختيار: Saga (غالباً Orchestration عشان العملية فيها خطوات وتعويضات كتير وسهل تتابعها)، مع Outbox في كل Service عشان كل خطوة ترانزاكشن محلية + Event بشكل Atomic، و Idempotency في كل خطوة وكل تعويض، و Timeouts للخطوات اللي مابتردش.
Staff

ترانزاكشن واحدة مفتوحة من 6 ساعات على PostgreSQL. إيه اللي ممكن يحصل، وإزاي تمنعه يتكرر؟

  • Bloat: الـ VACUUM مش بيقدر يمسح أي نسخة صف أحدث من الـ Snapshot بتاعتها، في كل الجداول. الجداول والـ Indexes بتكبر والكويريز بتبطأ.
  • Locks: أي صف كتبت فيه مقفول، وأي ALTER TABLE هيستنى ويوقف كل الكويريز وراه.
  • Wraparound: على داتابيز عليها كتابة كتير، الـ Freeze متعطل، ولو age(datfrozenxid) قرب من 2 مليار، PostgreSQL يرفض الكتابة.
  • Replication: الـ Replicas (مع hot_standby_feedback) ممكن تمنع التنضيف هي كمان، أو العكس: كويريز على الـ Replica تتلغى.
  • المنع: idle_in_transaction_session_timeout، و transaction_timeout (PostgreSQL 17+)، و statement_timeout، ومراقبة pg_stat_activity (xact_start و state = 'idle in transaction') بـ Alert، و DB::transaction(closure) بدل اليدوي في الكود، والـ Batches للشغل الكبير.
Staff

امتى ينفع تضعّف الـ Durability عشان الأداء؟ وإزاي تعمل ده بأمان؟

  • ينفع للداتا اللي ممكن تتعاد أو خسارة آخر ثانية منها مقبولة: عدادات، و Analytics events، و Imports بتتعاد، و Caches في الداتابيز، وبيئات الـ Staging.
  • مينفعش للفلوس، والأوردرات، وأي حاجة اترد على المستخدم إنها تمت.
  • بأمان: في PostgreSQL على مستوى الترانزاكشن: SET LOCAL synchronous_commit = off في ترانزاكشنز العدادات بس، والفلوس تفضل محمية. وفي MySQL innodb_flush_log_at_trx_commit = 2 على السيرفر كله، فالأحسن تفصل الداتا دي في داتابيز أو سيرفر مختلف.
  • أبداً fsync = off في الإنتاج: دي مش “خسارة آخر ثانية”، دي احتمال إن الداتابيز ماتقومش.
  • وفكّر قبلها في حلول تانية: Group Commit شغال لوحده، و Batching للـ INSERTs، وديسك أسرع.

2. ملخص سريع

الأساس

  • Transaction = يا كله يا مفيش، ومحدش يشوفها قبل الـ COMMIT.
  • Autocommit = كل جملة ترانزاكشن لوحدها.
  • Error في PostgreSQL = الترانزاكشن كلها Aborted.
  • Error في MySQL = الجملة بس، والباقي ممكن يتثبت.

ACID

  • A: Undo Log (InnoDB) / تعليم Aborted (PostgreSQL).
  • C: مسؤوليتك، والـ Constraints بتساعد.
  • I: مستويات (موضوع الـ Concurrency).
  • D: WAL + fsync، وممكن تتضعف بالإعدادات.

DDL و Savepoints

  • MySQL: DDL = Implicit Commit.
  • PostgreSQL: DDL جوه الترانزاكشن، وبيتلغى معاها.
  • CREATE INDEX CONCURRENTLY بره الترانزاكشن.
  • Savepoint = تلغي جزء. متعملش واحد لكل صف.

Durability

  • الـ Log قبل الصفحة، والـ COMMIT بعد الـ fsync.
  • Group Commit = fsync واحد لكذا COMMIT.
  • Checkpoint = Recovery أقصر.
  • innodb_flush_log_at_trx_commit = 1 · sync_binlog = 1.
  • synchronous_commit = off آمن نسبياً · fsync = off لأ.

Recovery

  • Steal ← محتاج Undo · No-Force ← محتاج Redo.
  • ARIES: Analysis ← Redo (كل حاجة) ← Undo (اللي ماخلصش) + CLRs.
  • PostgreSQL: Redo بس.
  • نفس الـ Log = Replication و PITR و CDC.

في الشغل

  • جوه الترانزاكشن: داتابيز بس.
  • الشغل الكبير: Batches بالـ id، و Idempotent.
  • Timeouts: idle_in_transaction_session_timeout · transaction_timeout.
  • Dual Write ← Outbox + Idempotent Consumers.
  • بين Services ← Saga + Compensations.

Laravel

  • DB::transaction(closure) دايماً.
  • الإلغاء = ارمي Exception (مش return false).
  • المتداخلة = Savepoint.
  • Connection واحدة بس.
  • afterCommit · ShouldHandleEventsAfterCommit.
  • راقب بالـ Transaction Events.
علّم عليه لما تقدر تجاوب أسئلة الإنترفيو من غير ما تبص.