Optimistic Concurrency vs Locks في SQL Server

 

🔒⚡ Optimistic Concurrency vs Locks في SQL Server

لما اتنين يعدّلوا نفس الـ Order في نفس الوقت… مين فيهم يكسب؟ 😄

تخيّل معايا إن عندنا متجر إلكتروني اسمه SuperShop 🛒

وعندنا Order بالشكل ده:

Order #1001
Status: Pending
Total: 1500 SAR

وفي نفس اللحظة حصل السيناريو ده:

👨‍💼 أحمد من فريق خدمة العملاء فتح الـ Order.

👩‍💼 منى من فريق العمليات فتحت نفس الـ Order.

الاتنين شايفين:

Status = Pending

أحمد قال:

العميل أكد الطلب، خليني أعمله Approved ✅

وفي نفس الوقت منى قالت:

العميل طلب إلغاء الطلب، خليني أعمله Cancelled ❌

أحمد ضغط Save الأول.

بقت الداتا:

Status = Approved

بعدها بثانيتين منى ضغطت Save.

بقت الداتا:

Status = Cancelled

وهنا السؤال الخطير 😅:

طب تعديل أحمد راح فين؟ 🤨

اتمسح.

مش بس اتمسح…

ده اتمسح من غير ما أحمد أو منى يعرفوا إن حصل Conflict أصلاً.

وده واحد من أشهر مشاكل الـ Concurrency واسمه:

💥 Lost Update

ومن هنا تبدأ حكايتنا مع:

  • SQL Server Locks 🔒
  • Pessimistic Concurrency
  • Optimistic Concurrency ⚡
  • rowversion
  • EF Core Concurrency Tokens
  • Blocking
  • Deadlocks
  • Snapshot Isolation

خلينا نمشي واحدة واحدة.


🧠 أولًا: يعني إيه Concurrency؟

Concurrency ببساطة معناها:

أكتر من عملية بتحاول تتعامل مع نفس الداتا في نفس الوقت.

ممكن يكون عندك:

User A
User B
API Request
Background Worker
Scheduled Job

وكلهم بيتعاملوا مع نفس الـ Database.

مثلًا:

Order 1001

ممكن يتفتح في نفس اللحظة من أكتر من Server.

خصوصًا لو عندك Architecture زي:

Load Balancer
|
-------------------------
| | |
API-1 API-2 API-3
\ | /
\ | /
SQL Server

هنا مفيش ضمان إن Request 1 وRequest 2 مش هيشتغلوا على نفس الـ Row في نفس اللحظة.

وده طبيعي جدًا.

المشكلة مش في وجود Concurrency.

المشكلة في:

إنت هتدير الـ Concurrency إزاي؟


🔒 هنا تدخل SQL Server Locks

SQL Server مش سايب الدنيا سداح مداح 😄

لما Transactions تتعامل مع الداتا، SQL Server بيستخدم Locks عشان يحافظ على سلامة البيانات.

يعني لو Transaction بتعدل Row، مش أي Transaction تانية تقدر تدخل تعدله براحتها في نفس اللحظة.

مثلًا أحمد بيعمل:

UPDATE Orders
SET Status = 'Approved'
WHERE Id = 1001;

SQL Server غالبًا هيحتاج يعمل:

Exclusive Lock

على الـ Resource اللي بيتعدل.

يعني ببساطة:

🚫 أنا حاليًا بعدّل هنا، محدش يعدّل معايا في نفس اللحظة.


🔐 أهم أنواع الـ Locks

خلينا نبسطهم.

1️⃣ Shared Lock — S Lock

بيستخدم غالبًا أثناء القراءة.

الفكرة:

أنا بقرأ الداتا بس.

أكتر من Transaction ممكن يكون عندهم Shared Locks في نفس الوقت.

يعني:

Ahmed → Read
Mona → Read
Sara → Read

تمام.

لكن لو حد عايز يعمل Write، هنا ممكن يحصل تعارض حسب الـ Isolation Level والسيناريو.


2️⃣ Exclusive Lock — X Lock

ده بتاع الكتابة ✍️

لو أحمد بيعدل:

UPDATE Orders
SET Status = 'Approved'
WHERE Id = 1001;

SQL Server محتاج Exclusive Lock.

معناه:

محدش تاني يقدر يعمل تعديل متعارض على نفس الـ Resource لحد ما أحمد يخلص.


3️⃣ Update Lock — U Lock

ده Lock وسيط SQL Server ممكن يستخدمه قبل التحويل إلى Exclusive Lock.

الهدف الأساسي منه تقليل احتمالية Deadlocks في بعض سيناريوهات:

Read → Then Update

يعني SQL Server بيقول:

أنا بقرأ، بس غالبًا ناوي أعدل بعد شوية.


🏢 والـ Lock بيتعمل على إيه؟

ممكن SQL Server يقفل على مستويات مختلفة:

Row
Page
Table

وأحيانًا Key / Range وغيره.

مثلًا:

Row Lock

يعني Row معين.

بينما:

Table Lock

يعني تأثير أوسع بكتير.

وعشان كده Locking Strategy ممكن تأثر جدًا على الـ Performance.


😎 طب كده Lost Update اتحلت؟

مش بالضرورة.

وهنا النقطة المهمة جدًا.

خلينا نرجع لأحمد ومنى.

الساعة 10:00 أحمد قرأ:

Order 1001
Status = Pending

وفي نفس اللحظة منى قرأت:

Order 1001
Status = Pending

بعدها أحمد قعد 5 دقايق بيتكلم مع العميل ☎️.

ومنى قعدت 3 دقايق بتراجع الطلب.

يعني الـ Transaction الأصلية مش مفتوحة طول الوقت.

التطبيق جاب الداتا من SQL Server وطلعها للمستخدم.

بعد شوية أحمد عمل:

Approved

وبعدين Save.

وبعده منى عملت:

Cancelled

وبعدين Save.

SQL Server وقت الـ UPDATE نفسه عمل Lock.

لكن المشكلة إن SQL Server ما يعرفش إن منى عدلت بناءً على نسخة قديمة من الـ Order.

💡 ودي نقطة محورية جدًا:

⚠️ Locks مش معناها تلقائيًا إنك محمي من كل Lost Updates.

SQL Server بيحمي العمليات اللي بتحصل دلوقتي.

لكن لو الـ User كان شايف نسخة قديمة من البيانات من خمس دقايق، الموضوع مختلف.

وهنا يظهر:

⚡ Optimistic Concurrency


🧠 يعني إيه Optimistic Concurrency؟

الفلسفة بسيطة جدًا.

بدل ما أقول:

أنا خايف إن حد يعدل الـ Order، فهفضل ماسكه Lock طول ما المستخدم فاتح الشاشة.

بنقول:

غالبًا محدش هيعدل نفس الـ Order في نفس الوقت.
خلّي الناس تشتغل براحتها، ولما حد يعمل Save نتأكد إن الداتا ما اتغيرتش من وقت ما قراها.

وده ليه اسم:

Optimistic

لأننا متفائلين إن Conflict مش هيحصل كتير 😄.

لكن لما يحصل…

نكتشفه.


🔒 العكس: Pessimistic Concurrency

Pessimistic معناها:

أنا متوقع إن Conflict ممكن يحصل، فهقفل الـ Resource قبل ما أشتغل عليه.

تخيلها كده:

أحمد دخل يعدل Order 1001.

حطينا لافتة:

🔒 Ahmed is editing this Order.

منى تيجي تعدل…

نقول لها:

⏳ استني أحمد يخلص.

ده Pessimistic Locking.


😅 طب ليه ما نعملش كده وخلاص؟

لأن عندك Web Application.

ممكن أحمد يفتح صفحة Edit الساعة:

10:00

ويروح يعمل قهوة ☕.

ويرجع:

10:20

هل معقول تمسك Database Transaction وLock لمدة 20 دقيقة؟

طبعًا لا 😄.

هتعمل كارثة.

ممكن يحصل:

Blocking
Connection exhaustion
Deadlocks
Poor throughput
Terrible user experience

عشان كده معظم Web Applications بتحب:

⚡ Optimistic Concurrency


🪄 الحل الجميل: rowversion

SQL Server عنده Data Type ممتاز جدًا اسمه:

rowversion

خلينا نعدل جدول Orders.

CREATE TABLE Orders
(
Id INT PRIMARY KEY,
Status VARCHAR(50),
Total DECIMAL(18,2),
RowVersion ROWVERSION
);

كل Row بقى عنده Version.

مثلًا:

Order 1001
Status = Pending
Total = 1500
RowVersion = 0x00000000000007D1

أحمد فتح الـ Order.

فأخذ:

Status = Pending
Version = 0x07D1

منى فتحت الـ Order.

برضه أخذت:

Status = Pending
Version = 0x07D1

الاتنين عندهم نفس النسخة.

تمام لحد هنا 👍.


👨‍💼 أحمد يعمل Save

أحمد غيّر:

Pending
Approved

بدل ما نعمل:

UPDATE Orders
SET Status = 'Approved'
WHERE Id = 1001;

نعمل:

UPDATE Orders
SET Status = 'Approved'
WHERE Id = 1001
AND RowVersion = @OriginalRowVersion;

يعني:

عدّل الـ Order لو الـ Version لسه هو نفس الـ Version اللي أحمد قرأه.

وكان:

@OriginalRowVersion = 0x07D1

SQL Server يلاقيه زي ما هو.

يعمل Update ✅.

وبمجرد الـ Update، الـ rowversion يتغير تلقائيًا.

بقى مثلًا:

Status = Approved
Version = 0x07D2

جميل جدًا.


👩‍💼 بعدها منى تعمل Save

منى لسه معاها:

Version = 0x07D1

فتبعت:

UPDATE Orders
SET Status = 'Cancelled'
WHERE Id = 1001
AND RowVersion = 0x07D1;

لكن SQL Server يبص على الداتا الحالية:

Current Version = 0x07D2

مش:

0x07D1

إذًا:

Rows affected = 0

🎉 وهنا اكتشفنا الـ Conflict.

بدل ما تعديل أحمد يضيع في صمت…

التطبيق يعرف إن:

🚨 يا منى، الـ Order اتغير من وقت ما فتحتي الشاشة.

وده هو:

⚡ Optimistic Concurrency Control


🤯 لاحظ الفرق

بدون Optimistic Concurrency:

Ahmed reads Pending
Mona reads Pending
Ahmed → Approved
Mona → Cancelled
Final Result:
Cancelled

تعديل أحمد ضاع 💀.

لكن باستخدام rowversion:

Ahmed reads Version 10
Mona reads Version 10
Ahmed updates Version 10
DB becomes Version 11
Mona tries Version 10
Conflict 🚨

ودي الفكرة كلها.


🕐 نقطة مهمة جدًا: rowversion مش Time

الاسم القديم للـ rowversion كان:

timestamp

وده سبب كمية لخبطة تاريخية عظيمة 😂.

لأن ناس كتير افتكرت إنه Date/Time.

لكن:

rowversion ≠ DateTime

مش معناه:

2026-09-03 15:30

هو Binary Value بيتغير مع Updates.

مثال:

0x0000000000000001
0x0000000000000002
0x0000000000000003

تقريبًا Conceptually.

فالأصح تستخدم:

rowversion

مش تعتمد عليه كتاريخ.


🚀 طب نعمل ده إزاي في EF Core؟

وهنا الموضوع بيبقى ألطف جدًا.

عندنا Entity:

public class Order
{
public int Id { get; set; }
public string Status { get; set; }
public decimal Total { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}

الـ:

[Timestamp]

بتقول لـ EF Core:

الحقل ده Concurrency Token.


🎯 EF Core بيعمل إيه ورا الستار؟

لو أحمد قرأ Order:

Id = 1001
Status = Pending
RowVersion = ABC

وبعدين غيّره:

order.Status = "Approved";
await dbContext.SaveChangesAsync();

EF Core مش هيعمل تقريبًا:

UPDATE Orders
SET Status = 'Approved'
WHERE Id = 1001;

لكن هيعمل حاجة Conceptually شبيهة بـ:

UPDATE Orders
SET Status = @Status
WHERE Id = @Id
AND RowVersion = @OriginalRowVersion;

🔥 هنا السحر.

لو الـ RowVersion اتغير:

0 Rows affected

EF Core يفهم إن فيه Concurrency Conflict.

فيرمي:

DbUpdateConcurrencyException

🛡️ فنعمل Handle للـ Exception

مثلًا:

try
{
await dbContext.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
throw new Exception(
"The order was modified by another user.");
}

لكن في Production طبعًا هنحتاج Handling أحسن.


🤔 لما يحصل Conflict نعمل إيه؟

دلوقتي منى حاولت تلغي الطلب.

لكن أحمد سبقها وعدله.

عندنا عدة استراتيجيات.


🥇 Database Wins

نقول لمنى:

النسخة اللي عندك قديمة. هجيبلك آخر نسخة من السيرفر.

مثلًا:

Your Version:
Pending
Current Database Version:
Approved

وبعدين تعمل Refresh.

ده:

Database Wins

🥈 Client Wins

ممكن في بعض الأنظمة تقول:

لأ، تعديل منى أهم. اعمل Refresh للـ Version وحاول تاني بتعديلها.

يعني عمليًا:

Ahmed → Approved
Mona → Cancelled

وفي النهاية:

Cancelled

بس الفرق إن الموضوع حصل بوعي مش Lost Update بالصدفة.


🥉 Merge

ودي ألطف Strategy في بعض التطبيقات.

مثال:

أحمد غير:

Status

ومنى غيرت:

CustomerNote

ليه نرمي تعديل واحد منهم؟

ممكن نعمل Merge:

Status = Approved
CustomerNote = Call customer tomorrow

وده مفيد في الأنظمة اللي فيها Forms كبيرة.


🖥️ الـ API ممكن يرجع إيه؟

مثلًا لو Conflict حصل، ممكن الـ API يرجع:

409 Conflict

وممكن الـ Response يكون:

{
"message": "The order has been modified by another user.",
"currentStatus": "Approved"
}

والـ Frontend يعرض:

⚠️ This order was modified by another user.
Current Status: Approved
[Reload]
[Review Changes]

وده UX محترم جدًا.


🔒 طب فين الـ Locks بقى؟

هنا أهم نقطة في المقال كله.

ناس كتير بتفكر:

طالما استخدمت Optimistic Concurrency يبقى SQL Server مش بيستخدم Locks.

❌ غلط.

Optimistic Concurrency مش معناه إن مفيش Locks.

وقت الـ UPDATE:

UPDATE Orders ...

SQL Server لسه محتاج يحمي عملية الكتابة.

يعني ممكن يعمل Exclusive Locks أثناء تنفيذ الـ Update.

الفرق الحقيقي هو:

Pessimistic

Lock
Work
Wait
Update
Unlock

بينما Optimistic:

Read
Work freely
Try Update
Check Version
Success ✅
or
Conflict 🚨

يعني إحنا مش بنلغي الـ Locking Engine بتاع SQL Server.

إحنا بنغير طريقة إدارة التعارض على مستوى التطبيق.


🚧 Blocking يعني إيه؟

نفترض أحمد بدأ Transaction:

BEGIN TRANSACTION;
UPDATE Orders
SET Status = 'Approved'
WHERE Id = 1001;

وماعملش:

COMMIT;

منى حاولت:

UPDATE Orders
SET Status = 'Cancelled'
WHERE Id = 1001;

ممكن تلاقي منى قاعدة مستنية:

Waiting...
Waiting...
Waiting...

ليه؟

لأن أحمد ماسك Lock.

ده اسمه:

⏳ Blocking

Blocking مش Error.

هو ببساطة:

Transaction مستنية Transaction تانية تسيب Resource.

لكن لو الموضوع طول…

الـ Performance يتدهور.


☠️ طب والـ Deadlock؟

دي حكاية ألطف شوية 😂.

نفترض عندنا:

Order 1001
Order 1002

Transaction أحمد:

Locks Order 1001
Wants Order 1002

Transaction منى:

Locks Order 1002
Wants Order 1001

فبقى:

Ahmed:
أنا مستني منى.
Mona:
وأنا مستنياك.

😂

ولا واحد هيتحرك.

ده:

💀 Deadlock

SQL Server يكتشفه ويختار Transaction يعمل لها Kill.

اسمها:

Deadlock Victim

ويرجع Error.


🔥 Blocking vs Deadlock vs Optimistic Conflict

خلينا نحفظهم بالشكل ده:

الحالة معناها
🔒 Blocking واحد شغال والتاني مستنيه
☠️ Deadlock الاتنين مستنيين بعض
⚡ Optimistic Conflict محدش استنى، لكن وقت Save اكتشفنا إن الداتا اتغيرت

ودي من أهم المقارنات اللي لازم تبقى واضحة.


🧪 طب والـ Isolation Levels؟

هنا ناس كتير بتخلط بين حاجتين مختلفتين:

Optimistic Concurrency

و:

Transaction Isolation

مش نفس الحاجة.

Isolation Level بيحدد:

Transaction تشوف إيه من Changes بتاعة Transactions تانية؟

عندك مثلًا:

READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SNAPSHOT
SERIALIZABLE

لكن Optimistic Concurrency بيجاوب سؤال مختلف:

الداتا اللي المستخدم بيعدل عليها لسه نفس النسخة اللي كان قراها؟


🌟 Snapshot Isolation

SQL Server عنده Mechanism تاني مهم اسمه:

Row Versioning

بيستخدمه مع حاجات زي:

SNAPSHOT ISOLATION

و:

READ COMMITTED SNAPSHOT

الفكرة إن الـ Readers يقدروا يشوفوا Versions من الداتا بدل ما يفضلوا مستنيين Writers.

مثلًا بدل:

Writer 🔒
Reader ⏳

ممكن يبقى:

Writer → Current Row
Reader → Previous Version

وده يقلل:

Read/Write Blocking

بشكل كبير.

لكن خلي بالك ⚠️:

Snapshot Isolation مش هو نفسه rowversion Concurrency Token.

الاتنين فيهم كلمة Version…

لكن بيحلوا مشاكل مختلفة.


🎯 Snapshot بيحل مشكلة إيه؟

غالبًا يساعد في:

Reader vs Writer Blocking

بينما rowversion عند الـ Application يساعد في:

User A vs User B Lost Update

فرق مهم جدًا.


🏦 طب هل Optimistic Concurrency مناسب لكل حاجة؟

لا.

ودي نقطة Architecturally مهمة جدًا.

نرجع للـ SuperShop.

لو بنتعامل مع:

Order Notes
Customer Address
Product Description
User Profile

Optimistic Concurrency ممتاز جدًا.

ليه؟

لأن احتمال إن اتنين يعدلوا نفس الـ Record في نفس الثانية قليل.

لكن تخيل عندك:

Product Stock = 1

وفيه 500 Customer بيحاولوا يشتروا آخر قطعة 🔥.

هنا Conflict مش Rare.

هنا عندك Hot Resource.

وممكن تحتاج Strategy مختلفة.


📦 مثال الـ Inventory

عندنا:

Stock = 1

بدل ما نعمل:

SELECT Stock

وبعدين في Application:

Stock--

وبعدين Update…

ممكن نستخدم Atomic Update:

UPDATE Products
SET Stock = Stock - 1
WHERE Id = 50
AND Stock > 0;

لو:

Rows affected = 1

تمام ✅.

لو:

Rows affected = 0

يبقى:

Out of Stock

وده في بعض الحالات أقوى وأبسط من إنك تعتمد بس على rowversion.


💰 طب والفلوس؟

تخيل:

Account Balance = 1000 SAR

هنا ماينفعش تتعامل مع الموضوع بعقلية:

User Wins / Client Wins وخلاص 😅

Financial Operations محتاجة:

Transactions
Atomicity
Correct Isolation
Constraints
Idempotency
Proper locking strategy

وممكن حسب السيناريو تحتاج Optimistic أو Pessimistic Concurrency أو Atomic Commands.

يعني مفيش:

One Solution Fits All

🧠 إمتى أختار Optimistic Concurrency؟

فكر فيها كده:

Optimistic ممتاز لما:

Conflicts are rare

زي:

  • User Profile 👤
  • Order Management 📦
  • CMS Articles 📝
  • Product Details 🛒
  • Customer Records
  • Admin Screens
  • CRM Data

🔒 وإمتى Pessimistic Locking يبقى منطقي؟

لما الـ Resource حساس جدًا واحتمال التنافس عليه عالي.

زي:

Limited Resource
Hot Row
Short critical operation

لكن بشرط:

الـ Transaction تبقى قصيرة جدًا.

مش User يفتح Form ويحجز Lock لحد ما يقرر يشرب شاي ولا قهوة 😂.


🧩 السيناريو كامل بتاعنا

تعالى نجمع قصة أحمد ومنى كلها.

البداية

Database:
Order 1001
Status = Pending
Version = 20

أحمد يقرأ:

Pending / V20

منى تقرأ:

Pending / V20

أحمد

يعمل:

Approved

ويبعت:

UPDATE Orders
SET Status = 'Approved'
WHERE Id = 1001
AND RowVersion = V20;

Success ✅.

الداتا تبقى:

Approved / V21

منى

تحاول:

Cancelled

وتبعت:

UPDATE Orders
SET Status = 'Cancelled'
WHERE Id = 1001
AND RowVersion = V20;

لكن:

Database = V21

فتكون النتيجة:

0 rows affected

EF Core يقول:

DbUpdateConcurrencyException

الـ API يرجع:

409 Conflict

الـ UI يقول:

⚠️ Order changed by another user.
Current Status:
Approved

منى تعمل Reload.

تشوف تعديل أحمد.

وبعدين تقرر:

هل فعلًا لازم Cancel؟

كده مفيش Update اتفقد في صمت.

🎉


💡 أهم Insight في الموضوع كله

لو عايز تحفظ المقال كله في جملة واحدة:

Locks بتحمي الداتا أثناء تنفيذ العمليات، لكن Optimistic Concurrency بتحميك من إنك تكتب فوق تعديل حد تاني اعتمادًا على نسخة قديمة من الداتا.

ودي نقطة فارقة جدًا.


🔥 Optimistic Concurrency مش بديل للـ Locks

ممكن النظام الواحد يستخدم:

SQL Locks
+
Transactions
+
Optimistic Concurrency
+
Snapshot Isolation

في نفس الوقت.

لأن كل Mechanism بيحل نوع مختلف من المشاكل.

مثلًا:

SQL Locks
→ حماية العملية الحالية
Transaction
→ Atomicity
rowversion
→ Detect stale updates
Snapshot
→ Reduce reader/writer blocking

مش منافسين لبعض.

هما Tools في صندوق الأدوات بتاع الـ Architect 🧰.


📊 الخلاصة السريعة

Concept الفكرة
🔒 Lock يمنع عمليات متعارضة من الوصول لنفس Resource في نفس اللحظة
⚡ Optimistic Concurrency سيب الناس تشتغل واكتشف التعارض وقت الحفظ
🛡️ Pessimistic Concurrency اقفل الـ Resource قبل ما يحصل Conflict
🧬 rowversion Version تلقائي للسطر يساعدنا نكتشف التعديل القديم
⏳ Blocking Transaction مستنية Transaction أخرى
☠️ Deadlock Transactions مستنيين بعض
📸 Snapshot Isolation Readers يشوفوا Versions لتقليل Blocking
💥 Lost Update تعديل شخص يتم الكتابة فوقه من شخص آخر

🎯 إمتى تستخدم إيه؟

لو عندك Web Application عادي:

ASP.NET Core
+
EF Core
+
SQL Server

وفيه CRUD Screens…

ابدأ غالبًا بـ:

Optimistic Concurrency
+
rowversion

خصوصًا للـ Entities اللي ممكن أكتر من User يعدلها.

لو عندك:

High Contention
Money
Inventory
Limited Resources
Critical workflows

ساعتها لازم تبص أعمق في:

Transactions
Isolation Levels
Atomic Updates
Locks
Idempotency
Business Constraints

🏁 الخاتمة

الموضوع في الآخر مش:

Optimistic Concurrency
VS
SQL Locks

بالشكل الحرفي.

الأصح إنك تبص عليهم كطبقات مختلفة من الحماية.

SQL Server بيقول:

🔒 وأنا بنفذ الـ Query دلوقتي، هاحمي الداتا من العمليات المتعارضة.

والـ Application باستخدام Optimistic Concurrency بيقول:

⚡ وقبل ما أكتب، هاتأكد إن النسخة اللي المستخدم عدل عليها لسه هي آخر نسخة.

وده اللي خلّى منى بدل ما تمسح تعديل أحمد من غير ما تدري…

تاخد رسالة واضحة:

⚠️ Someone modified this Order before you.

وساعتها القرار يرجع للـ Business Logic بدل ما الـ Database تقول:

آخر واحد ضغط Save يكسب 😄

وده بالضبط الفرق بين Application بيشتغل…

وApplication مصمم صح للـ Concurrency. 🚀

Comments

Popular posts from this blog

Best Practices for Storing and Loading JSON Objects from a Large SQL Server Table Using .NET Core

Maxpooling vs minpooling vs average pooling

Polling vs. SignalR: A Detailed Comparison