Optimistic Locking vs Pessimistic Locking in DotNet and EFCore

دور ال locking هنا هو الحفاظ على سلامة وامن البيانات ومنع التعارض اللي ممكن يحصل على البيانات لو في concurrent operations هتحصل عليها او concurrent users عاوزين ي access هذه البيانات
Optimistic Locking vs Pessimistic Locking in DotNet and EFCore
Optimistic Locking vs Pessimistic Locking in DotNet and EFCore

في هذه الصفحة

المقدمة

في البداية تعالى نتخيل مثال من الواقع اللي بنعيشه كل يوم عشان نبدا نفهم عنوان المقال بتاعنا.

تخيل صديقي العزيز انك في مطعم زحمة جدا وكل الناس اللي قاعدين على طاولات الطعام بيتنافسوا مين يلفت انتباه الراجل اللي بيجي ياخد الطلبات عشان ياخد باله ويجيلهم ويجيب لهم الاوردر بتاعهم طبعا هتلاقي كل الناس عاوزين يوصلو للراجل ده في نفس الوقت!

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


ايه هو الـ Locking

هنا بقى بيكون دور ال locking كأنه حارس بيتحكم في تدفق العملاء المتحمسين يعني بيتحكم في ال flow اعتبره شرطى المرور اللي بيضمن ان ميحصلش فوضي لو في عدة مستخدمين هيعملو access او قرأو و هيعدول نفس قطعة البيانات بشكل concurrently.

يبقى دور ال locking هنا هو الحفاظ على سلامة وامن البيانات ومنع التعارض اللي ممكن يحصل على البيانات لو في concurrent operations هتحصل عليها او concurrent users عاوزين ي access هذه البيانات.

يعني هنحمي الراجل بتاع المطعم وننظم عملية الوصول ل ايه لعمل الطلبات قبل مالناس تزهق وتقوم تضربه 😅


أنواع الـ Locking

استراتيجية ال locking دي بقى بتتقسم لنوعين وكل نوع منهم ليه اسلوبه في الحفاظ على امن وسلامة البيانات ومنع تعارض ال data النوع الأول اسمه Pessimistic Locking والنوع التاني اسمه Optimistic Locking.


الـ Pessimistic Locking

والنوع ده بيفترض الاسوء انه هيحصل فبيأمن نفسه بدري بدري وبنقول عليها concurrency control mechanism الية للتحكم في ال concurrency التزامن وبيتفرض انه هيحصل تعارض للداتا وهتكون غير متسقة يعني inconsistent لما عدد من المستخمدين او العمليات ت access الداتا في نفس الوقت .

النوع ده فكرته انه يعمل lock او يعمل قفل على الداتا وبيمنع وصول اي مستخدم تاني او عملية تانيه للداتا طالما في مستخدم او عملية شغاله دلوقتي على الداتا دي لحد ما العملية تخلص يقوم يفتح القفل فيجي مستخدم تاني و عملية تانيه ت access الداتا دي فيقفل القفل تاني ومدش يعرف يوصلها لحد ما العمليه اللي بتم تخلص يقوم يفتح القفل تاني وهكذا.

يعني بيحط قفل بشكل استباقي وده اللي بنقول عليه Acquiring Locks Upfront بتحط القفل مقدما.

خصائص الـ Pessimistic Locking

  1. الـ Acquiring Locks Upfront: زي ماقولنا بيحط القفل مسبقا قبل مايحصل اي عملية read او write على الدتا عشان يمنع العمليات المتزامنه التانيه من الوصل للبيانات بشكل متزامن concurrently.
  2. الـ Exclusive Access: يعني القفل هيسمح بس بالوصول الحصري للداتا يعني واحد بس اللي هيوصل والباقي هيستنو لحد مايخلص ويفتح القفل تاني ويدخل واحد بعده هيخش برده بشكل حصري وهكذا.
  3. الـ Potential for Waiting: طبعا لما هيحص lock على الدتا لحد ما العملية اللي شغاله تخلص ده معناته ان برده باقي العمليات اللي مستنيه برده هتكون عندها waiting time لانها منتظرة العملية تخلص .
  4. الـ Different Granularities: ال Pessimistic locking ممكن تعمله على مستوى table او على row او على مستوي database يعني بيشتغل على تفاصيل مختلفة.

Pessimistic Locking in DotNet and EF Core

تعالى نشوف مثال في ال .net في Entity Framework Core، مفيش دعم مباشر للتأمين المتشائم (Pessimistic Locking) كجزء من الإطار framework نفسه. لكن ممكن تنفيذ ال Pessimistic Locking باستخدام بعض الاستراتيجيات اليدوية.

  1. استخدام Transactions: تقدر تستخدم الـ Transactions مع ال lock على مستوى قاعدة البيانات. مثلاً، ممكن تستخدم Begin Transaction وتعمل قفل على مستوى ال records .
  2. تنفيذ الاستعلامات المباشرة: باستخدام ExecuteSqlRaw أو ExecuteSqlInterpolated، تقدر تنفذ استعلام SQL يقفل ال record .
using (var context = new YourDbContext())
{
	using (var transaction = context.Database.BeginTransaction())
	{
		var record = context.Records.FromSqlRaw("SELECT * FROM Records WITH (UPDLOCK, ROWLOCK) WHERE Id = {0}", id).FirstOrDefault();

		// تنفيذ التعديلات
		record.Property = newValue;

		context.SaveChanges();
		transaction.Commit();
	}
}

خلى بالك ان:

  • الـ Pessimistic Locking قد يؤثر على الأداء، خصوصًا في الأنظمة اللي فيها ضغط عالي من المستخدمين.
  • تأكد من مراجعة تصميم قاعدة البيانات والتعامل مع العمليات بشكل مناسب لتفادي ال Deadlocks.
  • كمان ممكن يحصل exceptions لو في conflicts بسبب ال lock اللي على ال records فلازم تاخد بالك وتتعامل مع ال exceptions دي لطريقة مناسبة .

الـ Optimistic Locking

وهي بتعتمد على افتراض أن الصراعات اللي هتحصل بين المستخدمين للوصل لنفس الدتا نادرة. في النوع ده من ال locking، مش  بنقفل السجلات ال records لما بن read الداتا ، بس بنتحقق من صحة البيانات عند حفظ التغييرات. ازاي الكلام ده ؟ وبنتحقق من ايه وليه ؟

عندنا 3 خطوات عشان نفهم إزاي بيشتغل Optimistic Locking.


ازاي الـ Optimistic Locking بيشتغل

  1. قراءة البيانات: المستخدم يقرأ البيانات من قاعدة البيانات بدون .lock
  2. تعديل البيانات: المستخدم يقوم بتعديل البيانات locally.
  3. التحقق عند الحفظ: عند محاولة حفظ البيانات، يتم التحقق إذا كانت البيانات قد تغيرت من قبل مستخدم آخر منذ أن تم تحميلها. إذا كانت البيانات قد تم تعديلها، فإن العملية تفشل، ويجب على المستخدم تحديث بياناته.

وهنفهم اكثر لما نشوف مثال.


Optimistic Locking in DotNet and EF Core

مثلا في ال EFCore  لما ناجي ننفذ ال Optimistic Locking بيكون في خاصية تُسمى "توقيت الإصدار" (Concurrency Token)  والخاصية دي بستخدمها مع ال entity  وبتمثل توقيت الاصدار بتمثل الاصدار بتاع كل record .

ازاي بقى بتستغل خاصية الاصدار دي : (Concurrency Token):

  1. بنعرف خاصية الاصدار من خلال اننا بنضيف لل entity  وبتكون من نوع byte [ ] او timestamp والخاصية دي بتسجل النسخة الحالية من ال record ده في قاعدة البيانات وكل مره بيتم تعديل السجل يعني بتحدث السجل ده.
public class Record 
{
	public int Id { get; set; }
	public string Property { get; set; }
	[Timestamp] // خاصية الإصدار
	public byte[] RowVersion { get; set; }
}
  1. التحقق عند الحفظ: لما المستخدم يحاول يحفظ التغييرات، EF Core هيتحقق تلقائيًا إذا كانت النسخة (RowVersion) اللي في قاعدة البيانات هي نفس النسخة اللي كان المستخدم عاملها read.

    لو النسختين مختلفتين يبقى في حد عدل على الداتا دي والتعديل بتاعه اتحفظ بس انت كنت عامل read  من الدتا القديمة اللي هي قبل ماالتعديل بتاع الراجل ده يتحفظ فانت دلوقتي مش معاك اخر تعديل فعملية التعديل بتاعك هتفشل لانك مش معاك اخر تحديث من الدتا فلازم تروح تجيب اخر تحديث وبعدين تعمل التعديل بتاعك وبرده وانت بتحفظ البيات بعد التعديل هيروح EF Core يتحقق من ال ROWVersion  لو طلعت مطابقه للنسخة اللي معاك هينفذ تعديل لو كانت غير مطابقه يبقى زي ماقلنا حد تاني نفذ تعديل على الداتا وتعديله اتنفذ قبلك فيجب عليك انك تاخد اخر تحديث وهكذا
using (var context = new YourDbContext())
{
	var record = context.Records.Find(id);

	// تنفيذ التعديلات
	record.Property = newValue;

	try 
	{
		context.SaveChanges();
	}
	catch(DbUpdateConcurrencyException)
	{
		// تم اكتشاف تعارض في البيانات
		// هنا ممكن نعيد تحميل البيانات أو تعرض رسالة للمستخدم
	}
}

باستخدام توقيت الإصدار، أنت بتضمن إن البيانات هتفضل سليمة ومافيش تعارضات بتحصل. وفي حالة حدوث تعارض، بتقدر تعالج الموقف بسهولة. وبالتالي، Optimistic Locking يعتبر طريقة فعالة في بيئات العمل المتعددة.


في الختام

  • الـ Pessimistic locking يوفر أمانًا أكبر ولكن قد يؤثر على الأداء.
  • الـ Optimistic Locking يوفر أداءً أفضل ولكن يتطلب إدارة جيدة لحالات التعارض.

إذا كان لديك تطبيق يواجه سيناريوهات مختلطة، قد تحتاج إلى استخدام كلا الطريقتين في أماكن مختلفة وفقًا لمتطلبات كل جزء من التطبيق.

اشترك الآن بنشرة اقرأ-تِك الأسبوعية

لا تدع أي شيء يفوتك. واحصل على أحدث المقالات المميزة مباشرة إلى بريدك الإلكتروني وبشكل مجاني!