Interview Handbook by Sayed Elmahdy English

المحور 12 · الداتا وقواعد البيانات

Data Consistency و Transactions

Isolation anomalies، و MVCC، و Locks، و Replication، و Consistency models، و Distributed transactions.

17 سؤالJunior · 4Mid · 7Senior · 610 اختيارات

المفاهيم

هنفهم إيه؟ تخيل آخر مقعد يتحجز مرتين أو تعديلين يكتبوا فوق بعض: النجاح التقني لكل عملية لا يعني إن قاعدة العمل فضلت صحيحة. هنفهم anomalies والعزل والـ locks والـ والتزامن المتفائل، ثم نميز حدود محلية عن consistency بين replicas أو خدمات.

قبل ما تبدأ: transaction مجموعة تغييرات تُعتمد معاً أو تُلغى معاً؛ قاعدة لازم تفضل صحيحة، مثل ألا يقل المخزون عن صفر. اعرف أساسيات وtransaction في محور 10. لو المصطلحين جديدين، ابدأ بالكارت الأول قبل جداول مستويات العزل.

المصطلحات اللي تحتها خط منقّط تقدر تضغط عليها لمعرفة معناها، وترجع تكمل من نفس مكانك.

#Transaction مش كفاية لوحدهاA transaction alone is not enough

لو آخر قطعة في المخزن اتباعت لاتنين، المشكلة مش بالضرورة إن الحفظ فشل؛ ممكن كل نجحت، بس القرار اتبنى على قراية قديمة. الـ atomicity بتقول إن خطواتك تنجح مع بعض أو تتلغي مع بعض. الـ بتحدد تداخل خطواتك مع شغل غيرك. ابدأ بكتابة الـ : مثلاً Stock >= 0، وبعدها اختار آلية تضمنه.

الـ transaction زي ظرف فيه تعليمات تتنفذ مع بعض. لكن وجود ظرفين مقفولين مش معناه إن الاتنين ماحجزوش نفس الكرسي.
BEGIN TRANSACTION;                              -- (1)
UPDATE Products SET Stock = Stock - 1
WHERE Id = @id AND Stock >= 1;                   -- (2)
-- Application: require exactly one affected row before inserting the order.
-- Otherwise roll back and report unavailable stock.
INSERT INTO Orders (Id, ProductId) VALUES (@orderId, @id); -- (3)
COMMIT;                                         -- (4)
🧾 شرح الكود
  1. (1) بداية وحدة الحفظ المشتركة.
  2. (2) الشرط والتقليل في statement واحدة؛ التطبيق لازم يفحص عدد الصفوف.
  3. (3) ماتسجلش order لو الحجز ماحصلش.
  4. (4) الـ commit يثبت الاتنين؛ أي فشل قبله يستلزم rollback.

المثال fragment للتدفق، مش batch يتنفذ من غير فحص. استخدم parameters، وconstraint كدفاع إضافي. ماتحطش payment call جوه transaction طويلة؛ قاعدة البيانات ماتقدرش تعمل rollback للدفع الخارجي.

#Read anomalies: الزمن هو اللي بيوضح الفرقRead anomalies: follow the timeline

مثال خطوة بخطوة
  1. رصيد الحساب المؤكد 100. المعاملة A تكتب 80 ولم تؤكد بعد، والمعاملة B تقرأ 80 ثم A تتراجع؛ B استخدمت قيمة لم تصبح حقيقة مؤكدة. دي dirty read.
  2. في non-repeatable read، B تقرأ 100، ثم A تؤكد 80، ثم B تعيد قراءة نفس الصف فتراه 80. كل قيمة كانت مؤكدة وقت قراءتها، لكن القراءتين مختلفتان.
  3. في phantom read ركّز على مجموعة صفوف تطابق شرطًا: إعادة الاستعلام قد تضيف صفًا جديدًا أدخلته معاملة أخرى. ارسم الزمن وحدد هل المشكلة قيمة صف أم عضوية مجموعة.

الـ anomaly بتوصف تاريخ تنفيذ، مش اسم exception. في الأمثلة دي A و B مختلفة، والسهم ترتيب زمني.

AnomalyTimelineالمشكلة
Dirty readA: write price=80 → B: read 80 → A: rollbackB شافت قيمة عمرها ما اتعملها commit.
Non-repeatable readA: read price=100 → B: commit price=80 → A: read 80نفس الصف اتغير بين قرايتين.
Phantom readA: count pending=4 → B: insert pending, commit → A: count=5نفس الشرط رجّع مجموعة صفوف مختلفة.
-- SQL Server, session A
BEGIN TRANSACTION;
UPDATE Products SET Price = 80 WHERE Id = 1;   -- (1) not committed
-- Pause here; execute session B, then:
ROLLBACK;

-- SQL Server, session B
SELECT Price FROM Products WITH (NOLOCK) WHERE Id = 1; -- (2)
🧾 شرح الكود
  1. (1) تعديل مؤقت ممكن يتلغي.
  2. (2) NOLOCK بيسمح بقراية غير committed؛ وممكن كمان صفوف تتفوت أو تتقري مرتين. مش حل عام للأداء، ولسه فيه locks.

المثال بيعرض الغلط عمدًا. الحل بيبدأ بـ indexes وtransactions قصيرة واختيار مناسب، مش التضحية بصحة التقرير من غير اتفاق.

#Lost update و Write skewLost updates and write skew

Lost update: اتنين بيحسبوا قيمة جديدة من نفس القيمة القديمة، وآخر كتابة تمسح نتيجة الأولى. Write skew: كل واحد بيعدل صف مختلف، لكن الاتنين بيعتمدوا على شرط مشترك؛ مفيش تعارض على نفس الصف يمسكه rowversion.

الخطوةLost update: Stock = 10Write skew: دكتورين on call
1A و B يقروّا 10A و B يشوفوا دكتورين موجودين
2A يكتب 9 ويعمل commitA يلغي نوبتجيته ويعمل commit
3B يكتب 9 ويعمل commitB يلغي نوبتجيته ويعمل commit
النتيجةطلبين، بس قطعة واحدة اتخصمتمفيش دكتور، رغم شرط وجود واحد على الأقل
-- Each doctor runs this in a separate snapshot transaction.
SELECT COUNT(*) FROM Doctors WHERE OnCall = 1;       -- (1) both see 2
-- Application proceeds only if count > 1.
UPDATE Doctors SET OnCall = 0 WHERE Id = @myId;       -- (2) different rows
🧾 شرح الكود
  1. (1) snapshot ثابتة مش معناها إن القرار آمن من قرارات غيرك.
  2. (2) الصفين مختلفين، فالـ snapshot ممكن تسمح للاتنين ينجحوا.

استخدم Serializable مع إعادة عند التعارض، أو اقفل صف مشترك بيمثل النوبتجية قبل الفحص. قفل صف الدكتور نفسه بس مش كفاية. ماترفعش isolation لكل التطبيق من غير قياس؛ احمِ الـ المطلوبة.

#Isolation levels: المعيار والـ implementationIsolation levels: standard versus implementation

الجدول ده ضمانات standard للـ read anomalies التلاتة. «ممكن» مش معناها إنها لازم تحصل. Lost update وwrite skew محتاجين تحليل إضافي حسب الـ engine وطريقة الكتابة.

LevelDirtyNon-repeatablePhantom
Read Uncommittedممكنممكنممكن
Read Committedممنوعممكنممكن
Repeatable Readممنوعممنوعممكن
Serializableممنوعممنوعممنوع

SQL Server: الافتراضي Read Committed، و مقفولة افتراضيًا في SQL Server التقليدي. Azure SQL Database بتفعّل RCSI افتراضيًا. Repeatable Read بالقفل بتحمي الصفوف اللي اتقرت لكن مش كل الـ ranges. PostgreSQL: الافتراضي Read Committed بـ ؛ Read Uncommitted بتتصرف زيه. Repeatable Read هي snapshot وبتمنع phantoms كمان، لكن ممكن write skew. Serializable بيكتشف اعتماد خطر وقد يلغي .

-- PostgreSQL: run business checks and writes within this transaction.
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;  -- (1)
SELECT COUNT(*) FROM Doctors WHERE OnCall = TRUE;
-- Application checks the count, then updates its doctor.
COMMIT;                                         -- (2)
🧾 شرح الكود
  1. (1) يطلب نتيجة مكافئة لترتيب serial للـ transactions الناجحة.
  2. (2) النجاح مش مضمون؛ عند failure أعد الوحدة كلها بقرايات جديدة ومحاولات محدودة.
Serializable مش معناها thread واحد، ومش وعد إن مفيش retries. عزل transaction محلية كمان مش ضمان linearizability لكل النظام الموزع.

تفاصيل الـ engines: PostgreSQL isolation وSQL Server locking and versioning.

#Locks و Pessimistic concurrencyLocks and pessimistic concurrency

لما التعارض متوقع، احجز حق التعديل قبل ما تبني القرار. في Server الـ S lock للقراية وبيتوافق مع S تاني، والـ X للكتابة ومش بيتوافق مع S أو X على نفس المورد. الـ U update lock بيتوافق مع S لكن مش U تاني؛ بيقلل بعض بتاعة التحويل من قراية لكتابة، مش كل deadlocks.

Row وpage locks بيختلفوا في مساحة القفل وتكلفة تتبعه. SQL Server ممكن يعمل lock escalation لجدول أو partition حسب الإعدادات والضغط؛ مش قاعدة إن كل row locks تتحول page locks الأول. PostgreSQL ليه row وtable locking مختلف، ومش بيستخدم نفس سياسة escalation.

-- PostgreSQL: same connection and transaction for read and write.
BEGIN;
SELECT Stock FROM Products WHERE Id = 7 FOR UPDATE;       -- (1)
-- Application checks available stock while holding the lock.
UPDATE Products SET Stock = Stock - 1 WHERE Id = 7;
COMMIT;

-- SQL Server equivalent read-intent fragment inside an explicit transaction:
SELECT Stock FROM Products WITH (UPDLOCK, HOLDLOCK)
WHERE Id = @id;                                          -- (2)
🧾 شرح الكود
  1. (1) بيقفل الصف الموجود لحد نهاية ؛ المتنافس على تعديله يستنى. مش بيقفل صف مش موجود ولا range كاملة تلقائيًا.
  2. (2) UPDLOCK يطلب update locks وHOLDLOCK يدي semantics بتاعة Serializable للمرجع ده، ومنها range protection المناسبة. الـ index وشكل الـ query بيأثروا على مساحة القفل.

ماتستناش user input أو network وأنت ماسك lock. لو العملية مجرد تقليل stock بشرط، conditional UPDATE أبسط. ROWLOCK مش ضمان إن escalation مستحيلة.

#MVCC و RCSI و SNAPSHOTMVCC, RCSI and SNAPSHOT

مثال خطوة بخطوة
  1. في بداية القراءة السعر المؤكد 100. عملية أخرى تغيره إلى 120 بينما القارئ مازال يعمل؛ إصدار الصف القديم يسمح للقارئ برؤية نسخة متسقة حسب مستوى العزل.
  2. في Server مع ، استعلام أول قد يرى 100، وبعد تأكيد التعديل استعلام ثانٍ يرى 120؛ لأن كل statement لها نقطة نسخة منفصلة.
  3. مع SNAPSHOT في نفس ، نسخة المعاملة تتحدد عند أول وصول للبيانات، فتقرأ النسخة نفسها مع رؤية تعديلاتك أنت. إصدارات الصفوف تستهلك تخزينًا ولا تلغي تعارضات الكتابة.

القراية اللي بتوقف الكتابة بتعمل طوابير. بتحتفظ بإصدارات عشان القارئ يشوف نسخة مسموحة له بدل ما يستنى الكاتب في حالات كتير. مش معناها إن مفيش locks، ولا إنها بتحل write skew.

SQL Server approachنقطة النسخةالمقابل
Locking Read Committedقراية committed بالقفلblocking بين reader وwriter
Read Committed + RCSIبداية كل statementstatement تانية ممكن تشوف commit أحدث
SNAPSHOTsnapshot للـ transaction بتتحدد عند أول data accessversion storage وupdate conflicts محتملة
-- SQL Server: administrative setup, not per-request commands.
ALTER DATABASE Shop SET READ_COMMITTED_SNAPSHOT ON;       -- (1)
ALTER DATABASE Shop SET ALLOW_SNAPSHOT_ISOLATION ON;      -- (2)

-- On an application connection, after SNAPSHOT has been enabled:
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRANSACTION;
SELECT Total FROM Orders WHERE Id = @id;                 -- (3)
-- Another transaction may commit here.
SELECT Total FROM Orders WHERE Id = @id;
COMMIT;
🧾 شرح الكود
  1. (1) يغيّر implementation بتاعة Read Committed؛ تغيير إداري محتاج تخطيط للـ connections الموجودة.
  2. (2) يسمح للـ connections إنها تختار SNAPSHOT؛ ده اختيار منفصل.
  3. (3) القرايتين يشوفوا نفس snapshot مع رؤية تعديلات transaction نفسها.

راقب version retention والـ transactions الطويلة. MVCC بتدفع تكلفة storage وتنضيف بدل بعض تكلفة الانتظار. ماتخلطش SQL Server SNAPSHOT مع Read Committed اللي RCSI مفعّلة تحته.

#Optimistic concurrency و Atomic updatesOptimistic concurrency and atomic updates

في شاشة تعديل order، ماتقفلش الصف طول ما المستخدم بيفكر. ابعت version مع البيانات، وعند الحفظ اقبل التعديل بس لو النسخة لسه زي ما هي. في Server rowversion رقم binary بيتغير تلقائيًا؛ مش تاريخ ولا وقت.

// using System.ComponentModel.DataAnnotations;
// using Microsoft.EntityFrameworkCore;
public sealed class Order
{
    public int Id { get; set; }
    public string Note { get; set; } = "";
    [Timestamp] public byte[] Version { get; set; } = [];  // (1)
}

var order = await db.Orders.SingleAsync(o => o.Id == id, ct);
// submittedVersion came from the original edit response.
db.Entry(order).Property(o => o.Version).OriginalValue = submittedVersion; // (2)
order.Note = submittedNote;
try { await db.SaveChangesAsync(ct); }
catch (DbUpdateConcurrencyException)
{
    return Results.Conflict("Order changed; reload and review your edit."); // (3)
}
🧾 شرح الكود
  1. (1) SQL Server provider بيستخدمها concurrency مولّدة من قاعدة البيانات.
  2. (2) المهم نسخة المستخدم الأصلية، مش نسخة اتقرت بعد ما المستخدم بعت الحفظ.
  3. (3) بيضيف النسخة للـ WHERE؛ صفر صفوف متأثرة يعمل conflict، والتطبيق يطلب مراجعة بدل overwrite صامت.

لو هدفك خصم كمية، ممكن ماتحتاجش read أصلًا:

ArgumentOutOfRangeException.ThrowIfNegativeOrZero(qty);   // (1)
int changed = await db.Products
    .Where(p => p.Id == productId && p.Stock >= qty)
    .ExecuteUpdateAsync(s => s.SetProperty(p => p.Stock, p => p.Stock - qty), ct); // (2)
if (changed == 0) return Results.Conflict("Stock unavailable"); // (3)
🧾 شرح الكود
  1. (1) كمية سالبة كانت هتزود stock.
  2. (2) الحساب والشرط في SQL واحدة؛ مفيش stale read-modify-write في التطبيق.
  3. (3) افحص النتيجة؛ ده مش SaveChanges ومش بيطبق tracking tokens تلقائيًا.

لو معاه insert order استخدم مشتركة. ماتعيدش concurrency conflict بشكل أعمى، وماتعتمدش على token صف واحد لحماية على كذا صف. EF Core concurrency.

#Deadlocks: شخّص وأعد الوحدة كاملةDeadlocks: diagnose and retry the whole unit

الـ blocking انتظار في اتجاه واحد. الـ دايرة انتظار، والـ database بتختار victim وتلغي عشان الباقي يكمل.

Time    Transaction A                 Transaction B
1       X lock Product 1              X lock Order 7
2       Request Order 7: waits        Request Product 1: waits   (1)
3       Victim rolls back             Survivor proceeds         (2)
🧾 شرح الكود
  1. (1) كل واحد ماسك اللي التاني محتاجه.
  2. (2) الـ victim يعيد القرايات والكتابات مع بعض، مش آخر UPDATE بس.
  1. خد deadlock graph وحدد الموارد والـ queries.
  2. رتب الوصول للموارد بشكل ثابت، وقلل وقت transaction.
  3. راجع indexes والـ scans اللي بتوسع مساحة القفل.
  4. اعمل retry محدود مع backoff وjitter للأخطاء المؤقتة المعروفة.

مع execution strategy، لف الـ transaction الصريحة كلها جوه قابل للإعادة. أي side effect خارجي محتاج أو ؛ retry عشوائي لكل exception ممكن يكرر الدفع.

#Replication و Consistency modelsBonusReplication and consistency models

اختياري بس بيفرق: مش هيتسأل فيه في كل انترفيو، بس معرفته بتعمّق فهمك وبتميّزك عن غيرك.

بعد تعديل order على primary، القراية من replica متأخرة ممكن ترجع الحالة القديمة. Read-your-writes ضمان جلسة: العميل يشوف كتاباته السابقة. حققه بقراية primary، أو routing مربوط بـ position وانتظار replica تلحقه؛ فترة sticky ثابتة مجرد تخمين لو مفيش حد أقصى مضمون للـ lag.

Modelمعناهمثال
Linearizableكل عملية تبدو لحظية بين بدايتها ونهايتها، مع احترام الزمن الحقيقيقراية بدأت بعد اكتمال حجز تشوف أثره أو تغيير أحدث
Sequentialترتيب عالمي واحد يحترم ترتيب كل client، مش لازم الزمن الحقيقي بين clientsالكل يتفق على ترتيب، لكن write مكتملة ممكن لسه ماتبانش لعميل تاني
Causalالسبب قبل النتيجة؛ العمليات المستقلة ممكن تختلف في ترتيبهارد على تعديل order مايبانش قبل التعديل اللي سببه
Eventualلو الكتابة وقفت والتواصل رجع، النسخ تتقارب حسب conflict resolutionعداد مشاهدات يتأخر وبعدين يلحق
Client A: write Order=Paid → primary acknowledges           (1)
Client A: read replica → Order=Pending                      (2)
Fix: read primary, or wait until replica position >= token  (3)
🧾 شرح الكود
  1. (1) تأكيد primary مش بالضرورة تأكيد كل replicas.
  2. (2) lag كسرت read-your-writes.
  3. (3) الضمان مربوط بمصدر القراية أو ، مش اعتباطية.

: أثناء network partition مش ممكن تضمن linearizability وكل request للعقد السليمة يخلص بنجاح مع بعض. ده مش «اختار أي اتنين» طول الوقت، ومش نفس معنى C بتاعة . اختار ضمان لكل عملية، مع تكلفة وتواصل؛ ماتطلبش ترتيب عالمي لعداد تقريبي.

#Distributed transactions و Cache consistencyBonusDistributed transactions and cache consistency

اختياري بس بيفرق: مش هيتسأل فيه في كل انترفيو، بس معرفته بتعمّق فهمك وبتميّزك عن غيرك.

حفظ order وبعت event في نظام تاني عمليتين مستقلتين. 2PC بتطلب من المشاركين prepare وبعدين coordinator يقرر commit أو abort. بتدي atomic outcome للمشاركين المدعومين، لكن prepared participants ممكن يفضلوا حابسين موارد وهم مستنيين القرار، وفيه تكلفة تواصل وتشغيل ودعم محدود عبر خدمات خارجية.

Saga سلسلة local مع compensations: احجز مخزون، اخصم دفع، أكد الطلب؛ لو الدفع اترفض، حرر الحجز. compensation شغل بيزنس جديد ممكن يفشل أو يحتاج تدخل؛ مش rollback بيرجع الزمن، ومش عزل لباقي الناس عن الحالات الوسيطة.

Producer transaction:
  UPDATE Order + INSERT Outbox(MessageId, OrderId, Version)   (1)
Dispatcher:
  publish message → crash before marking sent → publish again (2)
Consumer transaction:
  INSERT Inbox(ConsumerId, MessageId) with UNIQUE constraint  (3)
  apply local effect → commit → acknowledge broker           (4)
🧾 شرح الكود
  1. (1) الـ بتقفل فجوة حفظ البيانات ونية النشر، في نفس database transaction.
  2. (2) التكرار طبيعي؛ مش exactly-once end-to-end.
  3. (3) الـ inbox تمنع نفس consumer من تطبيق الرسالة مرتين. فشل الإدخال كـ duplicate له مسار معروف؛ ماتتجاهلش كل database errors.
  4. (4) marker والأثر المحلي في نفس transaction. لو الأثر payment خارجي، استخدم key عند الجهة الخارجية كمان.

: حدّث وبعدين invalidate، مع للحد من مدة stale data. لكن reader ممكن يقرا نسخة قديمة قبل التحديث ويحطها في cache بعد invalidation. استخدم version-aware population أو coordination لو الضمان مطلوب؛ TTL لوحدها مش ضمان strong consistency. outbox تساعد توصيل invalidations في النهاية، ومش بتلغي نافذة التأخير. لما correctness مهمة، خُد القرار من المصدر الموثوق.

الاختيارإمتىالتكلفة
Local transactionداتا داخل حدود واحدةأبسط correctness
2PCمشاركين مدعومين واحتياج atomic commit واضحblocking وتنسيق
Saga + outbox/inboxخدمات مستقلة وworkflow طويل وتعويض ومراقبة

أسئلة الانترفيو

راجع فهمك

ليه الاختيار ده مناسب؟ إمتى ممكن ماينفعش؟ وإيه اللي هيتغير لو الحمل زاد أو ظهر فشل؟

12.1Does putting two statements in a transaction prevent every race condition?Junior

لأ. Atomicity بتجمع الحفظ، لكن هي اللي تحدد التداخل. اكتب زي عدم بيع stock سالب، واستخدم conditional update أو أو قفل مناسب بدل read وبعدها كتابة قيمة قديمة.

الإجابة النموذجية (English)

I separate atomicity from . A makes its changes commit or roll back together, but its reads can still become stale at common isolation levels. Two successful transactions can therefore violate a business rule. For stock, I prefer a conditional decrement and check the affected-row count. If several rows define the rule, I consider Serializable isolation or a shared lock boundary. I choose protection from the rather than assuming BEGIN TRANSACTION solves concurrency.

12.2What are dirty, non-repeatable and phantom reads?Junior

Dirty قراية كتابة لسه ماتعملهاش commit. Non-repeatable نفس الصف رجع بقيمة مختلفة. Phantom نفس شرط البحث رجّع مجموعة مختلفة، زي count يزيد بعد insert من تانية.

الإجابة النموذجية (English)

I describe them with two . A dirty read observes another transaction's uncommitted change, which may later roll back. A non-repeatable read sees a different committed value when reading the same row again. A phantom means repeating a predicate query produces a different set of rows, such as an extra pending order. These distinctions matter because protecting existing rows is not necessarily enough to protect the range of rows matching a condition.

12.3Why is NOLOCK not a safe general performance fix?Junior

ممكن يطلع بيانات ماحصلتش، أو صفوف ناقصة أو مكررة. وكمان مش معناه مفيش أي lock. راجع plan والـ indexes والـ blocking قبل إضعاف الضمان.

الإجابة النموذجية (English)

I would not add NOLOCK just to make a report faster. It permits dirty reads and can return missing or duplicated rows, so the result may never have represented a committed database state. It also does not remove all locking, including locking. I investigate the execution plan, indexes and duration first. If reader-writer blocking is the issue, I assess row-versioned reads with their storage and semantic trade-offs rather than silently accepting incorrect results.

12.4What is a lost update?Junior

A و B قروا stock بعشرة؛ كل واحد كتب تسعة. طلبين نقصوا قطعة واحدة. UPDATE يحسب من القيمة الحالية أو version check يمنع overwrite صامت.

الإجابة النموذجية (English)

A lost update happens when two callers read the same value, compute replacements separately, and the later write overwrites the earlier result. If stock starts at ten, two callers may both write nine after selling one item each. I avoid this with an atomic database expression, a concurrency- comparison, or a lock held across the decision. I also check business conditions such as sufficient stock, because an atomic decrement alone can still produce a negative quantity.

12.5How do SQL Server and PostgreSQL defaults differ?Mid

الاتنين Read Committed، لكن PostgreSQL بيستخدم snapshots لكل statement. Server التقليدي فيه مقفولة افتراضيًا؛ Azure SQL Database مختلفة. الاسم لوحده مش implementation.

الإجابة النموذجية (English)

I expect Read Committed in both engines, but I check how it is implemented and the actual database configuration. PostgreSQL uses a statement snapshot for Read Committed. Traditional Server defaults to locking reads unless READ_COMMITTED_SNAPSHOT is enabled; Azure SQL Database enables that option by default. PostgreSQL also treats Read Uncommitted as Read Committed. Those differences affect blocking and repeat reads, so I do not transfer assumptions between engines just because an -level name matches.

12.6What is the difference between RCSI and SNAPSHOT isolation?Mid

implementation لـ Read Committed بنسخة لكل statement. SNAPSHOT اختيار مستقل بنسخة للـ . الاتنين مش Serializable وممكن write skew.

الإجابة النموذجية (English)

In Server, changes Read Committed reads to use a committed snapshot for each statement. Two statements in one can therefore see different committed data. SNAPSHOT uses a transaction snapshot established at first data access, while still showing the transaction's own writes. It can raise update conflicts and requires separate enablement. I use the distinction when several reads must agree, and I still analyze multi-row because snapshot isolation is not equivalent to Serializable.

12.7How do you handle an EF Core rowversion conflict?Mid

خزن نسخة المستخدم الأصلية في OriginalValue. يحطها في WHERE، ولو صفر صفوف يرمي DbUpdateConcurrencyException. اختار reload أو merge بعد مراجعة، مش overwrite تلقائي.

الإجابة النموذجية (English)

I configure a concurrency and preserve the version originally shown to the user. includes that original token in the update or delete predicate. If no row matches, SaveChanges raises DbUpdateConcurrencyException. I then apply an explicit policy: reload, show the conflict, or merge fields after reviewing database values. I do not blindly replace the original version and retry, because that can discard someone else's edit. Server rowversion is a binary token, not a timestamp value.

12.8When would you choose pessimistic rather than optimistic concurrency?Mid

Optimistic مناسب لتعارض قليل وتعديل مستخدم طويل. Pessimistic مناسب لقرار قصير مع تنافس عالي. لازم القراية والتعديل في نفس ، ومن غير انتظار شبكة جوه القفل.

الإجابة النموذجية (English)

I prefer when conflicts are uncommon or an edit user interaction, since holding a database lock would be expensive. For a short decision under frequent contention, I may use SELECT FOR UPDATE in PostgreSQL or appropriate Server update locks. The protected read and write must share one and connection. I keep the lock scope short and plan for . For a single conditional counter change, one atomic UPDATE is often simpler than either approach.

12.9How would you prevent overselling the last product?Mid

كمية موجبة، وUPDATE بشرط Stock كافية، وعدد صفوف لازم واحد. لو فيه order insert خليه في نفس . اقرأ صفر صفوف كفشل حجز مش نجاح.

الإجابة النموذجية (English)

I validate that the requested quantity is positive and issue a conditional UPDATE that subtracts it only where the product has enough stock. I treat one affected row as a successful reservation and zero as failure. If creating the order must be atomic with reservation, both operations share a . This works across application instances because the database coordinates the write. I also add suitable constraints and a reservation identity if retries must not reserve the same order twice.

12.10How do blocking and deadlocks differ?Mid

Blocking انتظار ممكن يخلص لما الماسك يعمل commit. دايرة انتظار. خُد graph، رتب الموارد، قصّر ، وأعد victim كاملة بمحاولات محدودة.

الإجابة النموذجية (English)

Blocking means one waits for another to release a conflicting resource. A is a cycle of dependencies, so waiting alone cannot resolve it. The database aborts a victim. I inspect the deadlock graph, use a consistent access order, shorten transactions and improve indexes where scans widen locking. I retry the victim's entire transaction with fresh reads and bounded backoff, making sure replay cannot repeat an unprotected external side effect such as charging a card.

12.11What does lock escalation mean?Mid

Server ممكن يستبدل locks كتير بقفل أوسع عشان يقلل تكلفة التتبع. ده يوسع blocking. مش كل engines بتعمل كده، وROWLOCK مش ضمان ضده.

الإجابة النموذجية (English)

In Server, escalation can replace many fine-grained locks with a broader table or partition lock, reducing lock-management overhead while potentially blocking unrelated work. The exact scope depends on configuration and workload. I investigate large scans, size and indexing rather than relying on ROWLOCK as a guarantee. I also avoid generalizing this mechanism to PostgreSQL, which has a different locking implementation and does not follow SQL Server's escalation policy.

12.12Why can snapshot isolation allow write skew?Senior

دكتورين شافوا اتنين on call؛ كل واحد شال نفسه من صف مختلف. مفيش same-row conflict. Serializable أو قفل roster مشترك قبل الفحص يحمي شرط وجود واحد.

الإجابة النموذجية (English)

Snapshot gives a consistent view but does not necessarily protect every predicate used in a decision. Two doctors can each observe that two people are on call, then update different rows to leave. Neither overwrites the other, yet nobody remains. A rowversion on each doctor does not fix that. I use Serializable with whole- retries or serialize the decision through a shared roster row locked before checking the count. The protected boundary must match the business .

12.13How would you guarantee read-your-writes with replicas?Senior

اقرأ primary بعد الكتابة، أو استخدم position وانتظر replica تلحقه. توجيه ثابت لثانيتين مش ضمان لو lag طولت.

الإجابة النموذجية (English)

I first define the scope of the guarantee, usually a user session. The straightforward solution is to route that session's relevant reads to the primary. Another option is a commit-position and a replica that waits until it has applied at least that position before serving the read. I would not call a fixed two-second sticky window a guarantee unless lag has a proven bound. I also make sure caches do not reintroduce older results.

12.14How do linearizable, sequential and causal consistency differ?Senior

Linearizable بتحترم الزمن الحقيقي. Sequential ترتيب عالمي يحترم ترتيب كل client بس. Causal بتحترم السبب والنتيجة، ومش بتفرض ترتيب واحد على الكتابات المستقلة.

الإجابة النموذجية (English)

I think about the ordering each model promises. Linearizability makes each operation appear instantaneous and respects real-time completion across clients. Sequential consistency provides one order preserving each client's program order, but need not respect real time between clients. Causal consistency preserves cause-and-effect relationships while allowing independent operations to be observed in different orders. I choose the weakest guarantee that still supports the business rule, then check the , availability and session requirements instead of treating consistency as one switch.

12.15Why are sagas often chosen instead of two-phase commit?Senior

2PC محتاجة دعم وتنسيق وممكن participants تفضل prepared ومستنية. Saga local commits وتعويض، لكن الحالة الوسيطة ظاهرة والتعويض ممكن يفشل. مش بديل مجاني لـ .

الإجابة النموذجية (English)

Two-phase commit coordinates an atomic decision across supported participants, but prepared resources may remain held while the decision is unavailable. It also couples availability and requires infrastructure support many external lack. A saga instead commits local steps and performs compensating business actions when later steps fail. That supports independent services, but intermediate states become visible and compensation can fail. I prefer a local when the boundary permits it; a saga is a deliberate consistency trade-off, not a free replacement for .

12.16Do outbox and inbox patterns guarantee exactly-once processing?Senior

تمنع ضياع نية النشر مع commit، لكن dispatcher ممكن يكرر. Inbox مع unique key والأثر في نفس تحمي الأثر المحلي. الدفع الخارجي يحتاج key عند provider.

الإجابة النموذجية (English)

I describe the guarantee precisely. An atomically records the business change and publication intent, but the dispatcher can publish twice after a crash. An inbox deduplicates messages for a consumer when its unique marker and local effect commit together. That gives effectively-once local effects under the stated assumptions, not universal exactly-once execution. If processing calls a payment provider, the inbox alone is insufficient; I need provider-side and reconciliation for ambiguous outcomes.

12.17Why can update-then-invalidate still leave a cache stale?Senior

Reader تقرا القديم قبل update، والwriter تحدث وتمسح cache، وبعدها reader تحط القديم. تحد مدة البقاء، لكن مش تمنع السباق. قرار stock من DB.

الإجابة النموذجية (English)

A reader may fetch the old database value before a writer commits. The writer then commits and invalidates the cache, after which the reader populates that cache with its old value. limits retention but does not prevent the race. Depending on the requirement, I use version-aware writes, coordination or authoritative reads for critical decisions. An improves eventual delivery of invalidations, but it still leaves a delay window and does not make the cache strongly consistent.

اختبر نفسك

إجابات الاختبار وعلامات إكمال المحاور بتتحفظ على المتصفح ده، ومش بتتنقل لأجهزة تانية.

النتيجة: 0 / 0

A reader observes a price that another transaction later rolls back. Which anomaly?

القيمة اتقرت قبل commit وبعدها اتلغت.

SQL-standard Repeatable Read may still allow which read anomaly?

المعيار يسمح بـ phantoms؛ PostgreSQL أقوى في النقطة دي.

Two snapshot transactions turn off different doctors after both see two on call. What is this?

الشرط مشترك لكن الصفوف المعدلة مختلفة.

What snapshot boundary does SQL Server RCSI normally provide?

تحت Read Committed بنسخة لكل statement.

A conditional stock UPDATE affects zero rows. What should the application do?

صفر ممكن يعني المنتج مش موجود أو stock مش كافية؛ مفيش حجز ناجح.

Which value should EF compare when saving an edited form?

المقارنة تكشف التغيير اللي حصل من وقت فتح النموذج.

After a deadlock victim rollback, which unit should normally be retried?

القرارات السابقة مبنية على قرايات اتلغت.

A read starts after another client's completed write. Which model preserves that real-time order?

الـ real-time ordering هو الفرق الأساسي هنا.

An outbox dispatcher crashes after publishing but before marking sent. What is expected?

هيعيد النشر؛ consumer لازم تتعامل مع التكرار.

Does a cache TTL by itself guarantee reads always return the latest committed value?

تحد الاحتفاظ، مش بتنسق ترتيب القراءة والكتابة.