المفاهيم
هنفهم إيه؟ تخيل آخر مقعد يتحجز مرتين أو تعديلين يكتبوا فوق بعض: النجاح التقني لكل عملية لا يعني إن قاعدة العمل فضلت صحيحة. هنفهم anomalies والعزل والـ locks والـ والتزامن المتفائل، ثم نميز حدود محلية عن consistency بين replicas أو خدمات.
قبل ما تبدأ: transaction مجموعة تغييرات تُعتمد معاً أو تُلغى معاً؛ قاعدة لازم تفضل صحيحة، مثل ألا يقل المخزون عن صفر. اعرف أساسيات وtransaction في محور 10. لو المصطلحين جديدين، ابدأ بالكارت الأول قبل جداول مستويات العزل.
المصطلحات اللي تحتها خط منقّط تقدر تضغط عليها لمعرفة معناها، وترجع تكمل من نفس مكانك.
#Transaction مش كفاية لوحدهاA transaction alone is not enough
لو آخر قطعة في المخزن اتباعت لاتنين، المشكلة مش بالضرورة إن الحفظ فشل؛ ممكن كل نجحت، بس القرار اتبنى على قراية قديمة. الـ atomicity بتقول إن خطواتك تنجح مع بعض أو تتلغي مع بعض. الـ بتحدد تداخل خطواتك مع شغل غيرك. ابدأ بكتابة الـ : مثلاً Stock >= 0، وبعدها اختار آلية تضمنه.
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) بداية وحدة الحفظ المشتركة.
- (2) الشرط والتقليل في statement واحدة؛ التطبيق لازم يفحص عدد الصفوف.
- (3) ماتسجلش order لو الحجز ماحصلش.
- (4) الـ commit يثبت الاتنين؛ أي فشل قبله يستلزم rollback.
المثال fragment للتدفق، مش batch يتنفذ من غير فحص. استخدم parameters، وconstraint كدفاع إضافي. ماتحطش payment call جوه transaction طويلة؛ قاعدة البيانات ماتقدرش تعمل rollback للدفع الخارجي.
#Read anomalies: الزمن هو اللي بيوضح الفرقRead anomalies: follow the timeline
- رصيد الحساب المؤكد 100. المعاملة A تكتب 80 ولم تؤكد بعد، والمعاملة B تقرأ 80 ثم A تتراجع؛ B استخدمت قيمة لم تصبح حقيقة مؤكدة. دي dirty read.
- في non-repeatable read، B تقرأ 100، ثم A تؤكد 80، ثم B تعيد قراءة نفس الصف فتراه 80. كل قيمة كانت مؤكدة وقت قراءتها، لكن القراءتين مختلفتان.
- في phantom read ركّز على مجموعة صفوف تطابق شرطًا: إعادة الاستعلام قد تضيف صفًا جديدًا أدخلته معاملة أخرى. ارسم الزمن وحدد هل المشكلة قيمة صف أم عضوية مجموعة.
الـ anomaly بتوصف تاريخ تنفيذ، مش اسم exception. في الأمثلة دي A و B مختلفة، والسهم ترتيب زمني.
| Anomaly | Timeline | المشكلة |
|---|---|---|
| Dirty read | A: write price=80 → B: read 80 → A: rollback | B شافت قيمة عمرها ما اتعملها commit. |
| Non-repeatable read | A: read price=100 → B: commit price=80 → A: read 80 | نفس الصف اتغير بين قرايتين. |
| Phantom read | A: 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) تعديل مؤقت ممكن يتلغي.
- (2)
NOLOCKبيسمح بقراية غير committed؛ وممكن كمان صفوف تتفوت أو تتقري مرتين. مش حل عام للأداء، ولسه فيه locks.
المثال بيعرض الغلط عمدًا. الحل بيبدأ بـ indexes وtransactions قصيرة واختيار مناسب، مش التضحية بصحة التقرير من غير اتفاق.
#Lost update و Write skewLost updates and write skew
Lost update: اتنين بيحسبوا قيمة جديدة من نفس القيمة القديمة، وآخر كتابة تمسح نتيجة الأولى. Write skew: كل واحد بيعدل صف مختلف، لكن الاتنين بيعتمدوا على شرط مشترك؛ مفيش تعارض على نفس الصف يمسكه rowversion.
| الخطوة | Lost update: Stock = 10 | Write skew: دكتورين on call |
|---|---|---|
| 1 | A و B يقروّا 10 | A و B يشوفوا دكتورين موجودين |
| 2 | A يكتب 9 ويعمل commit | A يلغي نوبتجيته ويعمل commit |
| 3 | B يكتب 9 ويعمل commit | B يلغي نوبتجيته ويعمل 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) snapshot ثابتة مش معناها إن القرار آمن من قرارات غيرك.
- (2) الصفين مختلفين، فالـ snapshot ممكن تسمح للاتنين ينجحوا.
استخدم Serializable مع إعادة عند التعارض، أو اقفل صف مشترك بيمثل النوبتجية قبل الفحص. قفل صف الدكتور نفسه بس مش كفاية. ماترفعش isolation لكل التطبيق من غير قياس؛ احمِ الـ المطلوبة.
#Isolation levels: المعيار والـ implementationIsolation levels: standard versus implementation
الجدول ده ضمانات standard للـ read anomalies التلاتة. «ممكن» مش معناها إنها لازم تحصل. Lost update وwrite skew محتاجين تحليل إضافي حسب الـ engine وطريقة الكتابة.
| Level | Dirty | Non-repeatable | Phantom |
|---|---|---|---|
| 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) يطلب نتيجة مكافئة لترتيب serial للـ transactions الناجحة.
- (2) النجاح مش مضمون؛ عند failure أعد الوحدة كلها بقرايات جديدة ومحاولات محدودة.
تفاصيل الـ 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) بيقفل الصف الموجود لحد نهاية ؛ المتنافس على تعديله يستنى. مش بيقفل صف مش موجود ولا range كاملة تلقائيًا.
- (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
- في بداية القراءة السعر المؤكد 100. عملية أخرى تغيره إلى 120 بينما القارئ مازال يعمل؛ إصدار الصف القديم يسمح للقارئ برؤية نسخة متسقة حسب مستوى العزل.
- في Server مع ، استعلام أول قد يرى 100، وبعد تأكيد التعديل استعلام ثانٍ يرى 120؛ لأن كل statement لها نقطة نسخة منفصلة.
- مع SNAPSHOT في نفس ، نسخة المعاملة تتحدد عند أول وصول للبيانات، فتقرأ النسخة نفسها مع رؤية تعديلاتك أنت. إصدارات الصفوف تستهلك تخزينًا ولا تلغي تعارضات الكتابة.
القراية اللي بتوقف الكتابة بتعمل طوابير. بتحتفظ بإصدارات عشان القارئ يشوف نسخة مسموحة له بدل ما يستنى الكاتب في حالات كتير. مش معناها إن مفيش locks، ولا إنها بتحل write skew.
| SQL Server approach | نقطة النسخة | المقابل |
|---|---|---|
| Locking Read Committed | قراية committed بالقفل | blocking بين reader وwriter |
| Read Committed + RCSI | بداية كل statement | statement تانية ممكن تشوف commit أحدث |
| SNAPSHOT | snapshot للـ transaction بتتحدد عند أول data access | version 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) يغيّر implementation بتاعة Read Committed؛ تغيير إداري محتاج تخطيط للـ connections الموجودة.
- (2) يسمح للـ connections إنها تختار SNAPSHOT؛ ده اختيار منفصل.
- (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) SQL Server provider بيستخدمها concurrency مولّدة من قاعدة البيانات.
- (2) المهم نسخة المستخدم الأصلية، مش نسخة اتقرت بعد ما المستخدم بعت الحفظ.
- (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) كمية سالبة كانت هتزود stock.
- (2) الحساب والشرط في SQL واحدة؛ مفيش stale read-modify-write في التطبيق.
- (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) كل واحد ماسك اللي التاني محتاجه.
- (2) الـ victim يعيد القرايات والكتابات مع بعض، مش آخر UPDATE بس.
- خد deadlock graph وحدد الموارد والـ queries.
- رتب الوصول للموارد بشكل ثابت، وقلل وقت transaction.
- راجع indexes والـ scans اللي بتوسع مساحة القفل.
- اعمل 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) تأكيد primary مش بالضرورة تأكيد كل replicas.
- (2) lag كسرت read-your-writes.
- (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) الـ بتقفل فجوة حفظ البيانات ونية النشر، في نفس database transaction.
- (2) التكرار طبيعي؛ مش exactly-once end-to-end.
- (3) الـ inbox تمنع نفس consumer من تطبيق الرسالة مرتين. فشل الإدخال كـ duplicate له مسار معروف؛ ماتتجاهلش كل database errors.
- (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 وبعدها كتابة قيمة قديمة.
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 من تانية.
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 قبل إضعاف الضمان.
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 صامت.
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.
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.
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 تلقائي.
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 مناسب لقرار قصير مع تنافس عالي. لازم القراية والتعديل في نفس ، ومن غير انتظار شبكة جوه القفل.
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 خليه في نفس . اقرأ صفر صفوف كفشل حجز مش نجاح.
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 كاملة بمحاولات محدودة.
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 مش ضمان ضده.
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 مشترك قبل الفحص يحمي شرط وجود واحد.
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 طولت.
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 بتحترم السبب والنتيجة، ومش بتفرض ترتيب واحد على الكتابات المستقلة.
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 وتعويض، لكن الحالة الوسيطة ظاهرة والتعويض ممكن يفشل. مش بديل مجاني لـ .
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.
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.
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.
اختبر نفسك
إجابات الاختبار وعلامات إكمال المحاور بتتحفظ على المتصفح ده، ومش بتتنقل لأجهزة تانية.
A reader observes a price that another transaction later rolls back. Which anomaly?
SQL-standard Repeatable Read may still allow which read anomaly?
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?
A conditional stock UPDATE affects zero rows. What should the application do?
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?
An outbox dispatcher crashes after publishing but before marking sent. What is expected?
Does a cache TTL by itself guarantee reads always return the latest committed value?