آبجیکٹ ریپوزٹری - آپ کے ہوم پروجیکٹس کے لیے NET ان میموری ریپوزٹری پیٹرن

تمام ڈیٹا کو میموری میں کیوں رکھا جائے؟

ویب سائٹ یا بیک اینڈ ڈیٹا کو ذخیرہ کرنے کے لیے، زیادہ تر سمجھدار لوگوں کی پہلی خواہش ایس کیو ایل ڈیٹا بیس کا انتخاب کرنا ہوگی۔ 

لیکن کبھی کبھی ذہن میں یہ خیال آتا ہے کہ ڈیٹا ماڈل SQL کے لیے موزوں نہیں ہے: مثال کے طور پر، تلاش یا سماجی گراف بناتے وقت، آپ کو اشیاء کے درمیان پیچیدہ تعلقات تلاش کرنے کی ضرورت ہوتی ہے۔ 

بدترین صورتحال تب ہوتی ہے جب آپ کسی ٹیم میں کام کرتے ہیں اور کوئی ساتھی یہ نہیں جانتا کہ فوری سوالات کیسے تیار کیے جائیں۔ آپ نے N+1 کے مسائل کو حل کرنے اور اضافی اشاریہ جات بنانے میں کتنا وقت صرف کیا تاکہ مرکزی صفحہ پر SELECT مناسب وقت میں مکمل ہو؟

ایک اور مقبول طریقہ NoSQL ہے۔ کئی سال پہلے اس موضوع کے بارے میں بہت زیادہ تشہیر تھی - کسی بھی آسان موقع کے لیے انہوں نے MongoDB کو تعینات کیا اور json دستاویزات کی شکل میں جوابات سے خوش تھے۔ (ویسے، دستاویزات میں سرکلر لنکس کی وجہ سے آپ کو کتنی بیساکھییں ڈالنی پڑیں؟).

میں ایک اور متبادل طریقہ آزمانے کا مشورہ دیتا ہوں - کیوں نہ تمام ڈیٹا کو ایپلیکیشن میموری میں ذخیرہ کرنے کی کوشش کریں، اسے وقتاً فوقتاً بے ترتیب اسٹوریج (فائل، ریموٹ ڈیٹا بیس) میں محفوظ کرتے رہیں؟ 

میموری سستی ہو گئی ہے، اور زیادہ تر چھوٹے اور درمیانے سائز کے پروجیکٹس کے لیے کوئی بھی ممکنہ ڈیٹا 1 GB میموری میں فٹ ہو جائے گا۔ (مثال کے طور پر، میرا پسندیدہ ہوم پروجیکٹ ہے۔ مالی ٹریکرجو ڈیڑھ سال کے لیے روزانہ کے اعدادوشمار اور میرے اخراجات، بیلنس اور لین دین کی تاریخ رکھتا ہے، صرف 45 MB میموری استعمال کرتا ہے۔)

پیشہ:

  • ڈیٹا تک رسائی آسان ہو جاتی ہے - آپ کو سوالات، سست لوڈنگ، ORM خصوصیات کے بارے میں فکر کرنے کی ضرورت نہیں ہے، آپ عام C# اشیاء کے ساتھ کام کرتے ہیں۔
  • مختلف دھاگوں سے رسائی کے ساتھ کوئی مسئلہ نہیں ہے۔
  • بہت تیز - کوئی نیٹ ورک کی درخواستیں نہیں، سوال کی زبان میں کوڈ کا ترجمہ نہیں، اشیاء کی (de) سیریلائزیشن کی ضرورت نہیں؛
  • ڈیٹا کو کسی بھی شکل میں ذخیرہ کرنا قابل قبول ہے - چاہے وہ ڈسک پر XML میں ہو، یا SQL سرور میں، یا Azure Table Storage میں۔

Cons:

  • افقی اسکیلنگ ختم ہو گئی ہے، اور اس کے نتیجے میں، صفر ڈاؤن ٹائم تعیناتی نہیں کی جا سکتی ہے۔
  • اگر ایپلیکیشن کریش ہو جاتی ہے، تو آپ جزوی طور پر ڈیٹا کھو سکتے ہیں۔ (لیکن ہماری درخواست کبھی کریش نہیں ہوتی، ٹھیک ہے؟)

یہ کس طرح کام کرتا ہے؟

الگورتھم مندرجہ ذیل ہے:

  • شروع میں، ڈیٹا اسٹوریج کے ساتھ ایک کنکشن قائم ہوتا ہے، اور ڈیٹا لوڈ ہوتا ہے۔
  • ایک آبجیکٹ ماڈل، بنیادی اشاریہ جات، اور متعلقہ اشاریہ جات (1:1، 1:بہت سے) بنائے گئے ہیں۔
  • آبجیکٹ کی خصوصیات (INotifyPropertyChanged) میں تبدیلیوں اور مجموعہ (INotifyCollectionChanged) میں عناصر کو شامل کرنے یا ہٹانے کے لیے ایک رکنیت بنائی جاتی ہے۔
  • جب سبسکرپشن کو متحرک کیا جاتا ہے، تبدیل شدہ آبجیکٹ کو ڈیٹا اسٹوریج میں لکھنے کے لیے قطار میں شامل کیا جاتا ہے۔
  • سٹوریج میں ہونے والی تبدیلیاں وقتاً فوقتاً (ٹائمر پر) بیک گراؤنڈ تھریڈ میں محفوظ کی جاتی ہیں۔
  • جب آپ ایپلیکیشن سے باہر نکلتے ہیں، تبدیلیاں بھی اسٹوریج میں محفوظ ہوجاتی ہیں۔

کوڈ کی مثال

ضروری انحصار شامل کرنا

// Основная библиотека
Install-Package OutCode.EscapeTeams.ObjectRepository
    
// Хранилище данных, в котором будут сохраняться изменения
// Используйте то, которым будете пользоваться.
Install-Package OutCode.EscapeTeams.ObjectRepository.File
Install-Package OutCode.EscapeTeams.ObjectRepository.LiteDb
Install-Package OutCode.EscapeTeams.ObjectRepository.AzureTableStorage
    
// Опционально - если нужно хранить модель данных для Hangfire
// Install-Package OutCode.EscapeTeams.ObjectRepository.Hangfire

ہم ڈیٹا ماڈل کی وضاحت کرتے ہیں جو اسٹوریج میں محفوظ کیا جائے گا۔

public class ParentEntity : BaseEntity
{
    public ParentEntity(Guid id) => Id = id;
}
    
public class ChildEntity : BaseEntity
{
    public ChildEntity(Guid id) => Id = id;
    public Guid ParentId { get; set; }
    public string Value { get; set; }
}

پھر آبجیکٹ ماڈل:

public class ParentModel : ModelBase
{
    public ParentModel(ParentEntity entity)
    {
        Entity = entity;
    }
    
    public ParentModel()
    {
        Entity = new ParentEntity(Guid.NewGuid());
    }
    
    public Guid? NullableId => null;
    
    // Пример связи 1:Many
    public IEnumerable<ChildModel> Children => Multiple<ChildModel>(x => x.ParentId);
    
    protected override BaseEntity Entity { get; }
}
    
public class ChildModel : ModelBase
{
    private ChildEntity _childEntity;
    
    public ChildModel(ChildEntity entity)
    {
        _childEntity = entity;
    }
    
    public ChildModel() 
    {
        _childEntity = new ChildEntity(Guid.NewGuid());
    }
    
    public Guid ParentId
    {
        get => _childEntity.ParentId;
        set => UpdateProperty(() => _childEntity.ParentId, value);
    }
    
    public string Value
    {
        get => _childEntity.Value;
        set => UpdateProperty(() => _childEntity.Value, value
    }
    
    // Доступ с поиском по индексу
    public ParentModel Parent => Single<ParentModel>(ParentId);
    
    protected override BaseEntity Entity => _childEntity;
}

اور آخر کار، ڈیٹا تک رسائی کے لیے ریپوزٹری کلاس خود:

public class MyObjectRepository : ObjectRepositoryBase
{
    public MyObjectRepository(IStorage storage) : base(storage, NullLogger.Instance)
    {
        IsReadOnly = true; // Для тестов, позволяет не сохранять изменения в базу
    
        AddType((ParentEntity x) => new ParentModel(x));
        AddType((ChildEntity x) => new ChildModel(x));
    
        // Если используется Hangfire и необходимо хранить модель данных для Hangfire в ObjectRepository
        // this.RegisterHangfireScheme(); 
    
        Initialize();
    }
}

ایک ObjectRepository مثال بنائیں:

var memory = new MemoryStream();
var db = new LiteDatabase(memory);
var dbStorage = new LiteDbStorage(db);
    
var repository = new MyObjectRepository(dbStorage);
await repository.WaitForInitialize();

اگر پروجیکٹ HangFire استعمال کرے گا۔

public void ConfigureServices(IServiceCollection services, ObjectRepository objectRepository)
{
    services.AddHangfire(s => s.UseHangfireStorage(objectRepository));
}

ایک نیا آبجیکٹ داخل کرنا:

var newParent = new ParentModel()
repository.Add(newParent);

اس کال کے ساتھ، اعتراض پیرنٹ ماڈل ڈیٹا بیس میں لکھنے کے لیے مقامی کیشے اور قطار دونوں میں شامل کیا جاتا ہے۔ لہذا، یہ آپریشن O(1) لیتا ہے، اور اس چیز کے ساتھ فوری طور پر کام کیا جا سکتا ہے۔

مثال کے طور پر، اس آبجیکٹ کو ریپوزٹری میں تلاش کرنے اور اس بات کی تصدیق کرنے کے لیے کہ لوٹا ہوا آبجیکٹ ایک ہی مثال ہے:

var parents = repository.Set<ParentModel>();
var myParent = parents.Find(newParent.Id);
Assert.IsTrue(ReferenceEquals(myParent, newParent));

پھر کیا ہوتا ہے؟ سیٹ () واپسی ٹیبل ڈکشنری، جس میں ہے کنکرنٹ ڈکشنری اور بنیادی اور ثانوی اشاریہ جات کی اضافی فعالیت فراہم کرتا ہے۔ یہ آپ کو ID (یا دیگر صوابدیدی صارف اشاریہ جات) کے ذریعہ تلاش کرنے کے طریقے حاصل کرنے کی اجازت دیتا ہے بغیر تمام اشیاء پر مکمل طور پر تکرار کیے۔

اشیاء کو شامل کرتے وقت آبجیکٹ ریپوزٹری ان کی خصوصیات کو تبدیل کرنے کے لیے ایک سبسکرپشن شامل کیا جاتا ہے، لہذا خواص میں کسی بھی قسم کی تبدیلی کے نتیجے میں اس اعتراض کو تحریری قطار میں شامل کیا جاتا ہے۔ 
باہر سے پراپرٹیز کو اپ ڈیٹ کرنا POCO آبجیکٹ کے ساتھ کام کرنے جیسا ہی لگتا ہے:

myParent.Children.First().Property = "Updated value";

آپ مندرجہ ذیل طریقوں سے کسی چیز کو حذف کر سکتے ہیں۔

repository.Remove(myParent);
repository.RemoveRange(otherParents);
repository.Remove<ParentModel>(x => !x.Children.Any());

یہ اعتراض کو حذف کرنے کی قطار میں بھی شامل کرتا ہے۔

بچت کیسے کام کرتی ہے؟

آبجیکٹ ریپوزٹری جب نگرانی شدہ اشیاء تبدیل ہوتی ہیں (یا تو شامل کرنا یا حذف کرنا، یا خصوصیات کو تبدیل کرنا)، ایک واقعہ اٹھاتا ہے۔ ماڈل بدل گیا۔سبسکرائب کیا اسٹوریج. نفاذ اسٹوریج جب کوئی واقعہ ہوتا ہے۔ ماڈل بدل گیا۔ تبدیلیاں 3 قطاروں میں رکھی جاتی ہیں - شامل کرنے، اپ ڈیٹ کرنے اور حذف کرنے کے لیے۔

عمل درآمد بھی اسٹوریج شروع کرنے پر، وہ ایک ٹائمر بناتے ہیں جس کی وجہ سے ہر 5 سیکنڈ میں تبدیلیاں محفوظ ہوتی ہیں۔ 

اس کے علاوہ، محفوظ کال پر مجبور کرنے کے لیے ایک API موجود ہے: ObjectRepository.Save().

ہر محفوظ کرنے سے پہلے، بے معنی آپریشنز کو قطاروں سے پہلے ہٹا دیا جاتا ہے (مثال کے طور پر، ڈپلیکیٹ ایونٹس - جب کسی چیز کو دو بار تبدیل کیا گیا یا اشیاء کو جلدی سے شامل/ہٹا دیا گیا)، اور تب ہی خود کو محفوظ کیا جاتا ہے۔ 

تمام صورتوں میں، پوری موجودہ آبجیکٹ کو محفوظ کیا جاتا ہے، لہذا یہ ممکن ہے کہ اشیاء کو تبدیل کیے جانے سے مختلف ترتیب میں محفوظ کیا گیا ہو، بشمول اشیاء کے نئے ورژن اس وقت کے مقابلے میں جب انہیں قطار میں شامل کیا گیا تھا۔

وہاں اور کیا ہے؟

  • تمام لائبریریاں .NET سٹینڈرڈ 2.0 پر مبنی ہیں۔ کسی بھی جدید .NET پروجیکٹ میں استعمال کیا جا سکتا ہے۔
  • API تھریڈ سیف ہے۔ اندرونی مجموعے کی بنیاد پر لاگو کیا جاتا ہے کنکرنٹ ڈکشنری، ایونٹ ہینڈلرز کے پاس یا تو تالے ہیں یا انہیں ان کی ضرورت نہیں ہے۔ 
    یاد رکھنے کے قابل صرف ایک چیز کال کرنا ہے۔ ObjectRepository.Save();
  • صوابدیدی اشاریہ جات (انفرادیت کی ضرورت ہے):

repository.Set<ChildModel>().AddIndex(x => x.Value);
repository.Set<ChildModel>().Find(x => x.Value, "myValue");

کون اسے استعمال کرتا ہے؟

ذاتی طور پر، میں نے شوق کے تمام پروجیکٹس میں اس طریقہ کار کو استعمال کرنا شروع کیا کیونکہ یہ آسان ہے اور ڈیٹا تک رسائی کی تہہ لکھنے یا بھاری انفراسٹرکچر کی تعیناتی کے لیے بڑے اخراجات کی ضرورت نہیں ہے۔ ذاتی طور پر، litedb یا فائل میں ڈیٹا اسٹور کرنا عام طور پر میرے لیے کافی ہوتا ہے۔ 

لیکن ماضی میں، جب اب ناکارہ اسٹارٹ اپ EscapeTeems (میں نے سوچا کہ یہ ہے، پیسہ - لیکن نہیں، دوبارہ تجربہ) - Azure Table Storage میں ڈیٹا کو ذخیرہ کرنے کے لیے استعمال کیا جاتا ہے۔

مستقبل کے لئے منصوبوں

میں اس نقطہ نظر کے اہم نقصانات میں سے ایک کو ٹھیک کرنا چاہوں گا - افقی اسکیلنگ۔ ایسا کرنے کے لیے، آپ کو یا تو تقسیم شدہ لین دین (sic!) کی ضرورت ہے، یا ایک مضبوط ارادہ سے فیصلہ کرنا چاہیے کہ مختلف مثالوں سے ایک ہی ڈیٹا کو تبدیل نہیں ہونا چاہیے، یا انھیں اصول کے مطابق تبدیل ہونے دیں "آخری کون صحیح ہے"۔

تکنیکی نقطہ نظر سے، میں مندرجہ ذیل اسکیم کو ممکنہ طور پر دیکھتا ہوں:

  • آبجیکٹ ماڈل کے بجائے ایونٹ لاگ اور اسنیپ شاٹ اسٹور کریں۔
  • دیگر مثالیں تلاش کریں (ترتیبات میں تمام مثالوں کے اختتامی نکات شامل کریں؟ udp دریافت؟ ماسٹر/غلام؟)
  • کسی بھی متفقہ الگورتھم جیسے کہ RAFT کے ذریعے EventLog مثالوں کے درمیان نقل بنائیں۔

ایک اور مسئلہ بھی ہے جو مجھے پریشان کرتا ہے - جھڑپوں کو حذف کرنا، یا ایسی اشیاء کو حذف کرنے کے معاملات کا پتہ لگانا جن کے دیگر اشیاء سے روابط ہیں۔ 

ماخذ کوڈ

اگر آپ نے یہاں تک کا سارا راستہ پڑھ لیا ہے، تو صرف کوڈ کو پڑھنا باقی ہے؛ یہ GitHub پر پایا جا سکتا ہے:
https://github.com/DiverOfDark/ObjectRepository

ماخذ: www.habr.com

نیا تبصرہ شامل کریں