ٹیرانٹول کے لیے ہمارے اپنے کیپ شدہ میعاد ختم ہونے والے ماڈیول کو لکھنا

ٹیرانٹول کے لیے ہمارے اپنے کیپ شدہ میعاد ختم ہونے والے ماڈیول کو لکھنا

کچھ عرصہ پہلے ہمیں خالی جگہوں پر ٹیپلز کی صفائی کا مسئلہ درپیش تھا۔ ٹیرانٹول. صفائی کا آغاز اس وقت نہیں کرنا تھا جب ٹرانٹول پہلے سے ہی میموری ختم ہو رہا تھا، بلکہ پہلے سے اور ایک خاص تعدد پر۔ اس کام کے لیے، tarantool میں Lua میں لکھا ہوا ایک ماڈیول ہے۔ میعاد ختم. اس ماڈیول کو تھوڑی دیر کے لیے استعمال کرنے کے بعد، ہم نے محسوس کیا کہ یہ ہمارے لیے موزوں نہیں ہے: بڑی مقدار میں ڈیٹا کی مسلسل صفائی کی وجہ سے، لوا جی سی میں لٹک گیا۔ اس لیے، ہم نے اپنے محدود شدہ میعاد ختم ہونے والے ماڈیول کو تیار کرنے کے بارے میں سوچا، امید ہے کہ مقامی پروگرامنگ زبان میں لکھا گیا کوڈ ہمارے مسائل کو بہترین طریقے سے حل کرے گا۔

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

تفصیل

ٹرانٹول کے لیے دستاویزات بہت اچھی ہیں۔ سبق سی میں اپنے ذخیرہ شدہ طریقہ کار کو کیسے لکھیں اس کے بارے میں۔ سب سے پہلے، میں تجویز کرتا ہوں کہ آپ اس سے اپنے آپ کو واقف کر لیں تاکہ نیچے ظاہر ہونے والے کمانڈز اور کوڈ کے ساتھ ان انسرٹس کو سمجھ سکیں۔ یہ بھی توجہ دینے کے قابل ہے۔ حوالہ ان اشیاء کے لیے جو آپ کے اپنے کیپڈ ماڈیول لکھتے وقت دستیاب ہوں، یعنی باکس, فائبر, انڈکس и txn.

آئیے دور سے شروع کریں اور دیکھیں کہ ایک محدود میعاد ختم ہونے والا ماڈیول باہر سے کیسا لگتا ہے:

fiber = require('fiber')
net_box = require('net.box')
box.cfg{listen = 3300}
box.schema.func.create('libcapped-expirationd.start', {language = 'C'})
box.schema.user.grant('guest', 'execute', 'function', 'libcapped-expirationd.start')
box.schema.func.create('libcapped-expirationd.kill', {language = 'C'})
box.schema.user.grant('guest', 'execute', 'function', 'libcapped-expirationd.kill')
box.schema.space.create('tester')
box.space.tester:create_index('primary', {unique = true, parts = {1, 'unsigned'}})
capped_connection = net_box:new(3300)

سادگی کے لیے، ہم ڈائرکٹری میں tarantool شروع کرتے ہیں جہاں ہماری libcapped-expirationd.so لائبریری واقع ہے۔ لائبریری سے دو فنکشن برآمد کیے جاتے ہیں: اسٹارٹ اور مار ڈالیں۔ پہلا قدم یہ ہے کہ Lua سے box.schema.func.create اور box.schema.user.grant کا استعمال کرتے ہوئے ان فنکشنز کو دستیاب کرایا جائے۔ پھر ایک اسپیس بنائیں جس کے ٹوپل میں صرف تین فیلڈز ہوں گے: پہلا ایک منفرد شناخت کنندہ ہے، دوسرا ای میل ہے، اور تیسرا ٹوپل کی زندگی بھر ہے۔ ہم پہلے فیلڈ کے اوپر ایک درخت کا انڈیکس بناتے ہیں اور اسے پرائمری کہتے ہیں۔ اگلا ہمیں اپنی مقامی لائبریری سے کنکشن آبجیکٹ ملتا ہے۔

تیاری کے کام کے بعد، اسٹارٹ فنکشن چلائیں:

capped_connection:call('libcapped-expirationd.start', {'non-indexed', box.space.tester.id, box.space.tester.index.primary, box.space.tester.index.primary, 3, 1024, 3600})

یہ مثال اسکیننگ کے دوران بالکل اسی طرح کام کرے گی جس طرح ایکسپائرڈ ماڈیول، جو Lua میں لکھا گیا ہے۔ اسٹارٹ فنکشن کی پہلی دلیل ٹاسک کا منفرد نام ہے۔ دوسرا خلائی شناخت کنندہ ہے۔ تیسرا ایک منفرد انڈیکس ہے جس کے ذریعے ٹیپلز کو حذف کر دیا جائے گا۔ چوتھا انڈیکس ہے جس کے ذریعے ٹیپلز کو عبور کیا جائے گا۔ پانچواں زندگی بھر کے ساتھ ٹیپل فیلڈ کا نمبر ہے (نمبرنگ 1 سے شروع ہوتی ہے، 0 سے نہیں!) چھٹا اور ساتواں اسکیننگ کی ترتیبات ہیں۔ 1024 ٹوپلز کی زیادہ سے زیادہ تعداد ہے جو ایک ہی لین دین میں دیکھی جا سکتی ہے۔ 3600 - سیکنڈ میں مکمل اسکین کا وقت۔

نوٹ کریں کہ مثال رینگنے اور حذف کرنے کے لیے ایک ہی انڈیکس کا استعمال کرتی ہے۔ اگر یہ ٹری انڈیکس ہے، تو پھر چھوٹی کلید سے بڑی کلید تک منتقلی کی جاتی ہے۔ اگر کوئی اور ہے، مثال کے طور پر، ہیش انڈیکس، تو ٹراورسل، ایک اصول کے طور پر، بے ترتیب ترتیب میں کیا جاتا ہے۔ تمام اسپیس ٹیپلز کو ایک اسکین میں اسکین کیا جاتا ہے۔

آئیے خلا میں 60 سیکنڈ کی زندگی بھر کے ساتھ کئی ٹیپلز داخل کریں:

box.space.tester:insert{0, '[email protected]', math.floor(fiber.time()) + 60}
box.space.tester:insert{1, '[email protected]', math.floor(fiber.time()) + 60}
box.space.tester:insert{2, '[email protected]', math.floor(fiber.time()) + 60}

آئیے چیک کریں کہ اندراج کامیاب رہا:

tarantool> box.space.tester.index.primary:select()
---
- - [0, '[email protected]', 1576418976]
  - [1, '[email protected]', 1576418976]
  - [2, '[email protected]', 1576418976]
...

آئیے 60+ سیکنڈ کے بعد سلیکٹ کو دہرائیں (پہلے ٹیپل کے داخل ہونے کے آغاز سے گنتی) اور دیکھیں کہ کیپ شدہ میعاد ختم ہونے والا ماڈیول پہلے ہی پروسیس ہو چکا ہے:

tarantool> box.space.tester.index.primary:select()
---
  - []
...

آئیے کام کو روکیں:

capped_connection:call('libcapped-expirationd.kill', {'non-indexed'})

آئیے ایک دوسری مثال کو دیکھتے ہیں جہاں کرال کے لیے الگ انڈیکس استعمال کیا جاتا ہے:

fiber = require('fiber')
net_box = require('net.box')
box.cfg{listen = 3300}
box.schema.func.create('libcapped-expirationd.start', {language = 'C'})
box.schema.user.grant('guest', 'execute', 'function', 'libcapped-expirationd.start')
box.schema.func.create('libcapped-expirationd.kill', {language = 'C'})
box.schema.user.grant('guest', 'execute', 'function', 'libcapped-expirationd.kill')
box.schema.space.create('tester')
box.space.tester:create_index('primary', {unique = true, parts = {1, 'unsigned'}})
box.space.tester:create_index('exp', {unique = false, parts = {3, 'unsigned'}})
capped_connection = net_box:new(3300)

یہاں سب کچھ ویسا ہی ہے جیسا کہ پہلی مثال میں، چند مستثنیات کے ساتھ۔ ہم تیسرے فیلڈ کے اوپر ایک ٹری انڈیکس بناتے ہیں اور اسے exp کہتے ہیں۔ پرائمری کہلانے والے انڈیکس کے برعکس یہ انڈیکس منفرد ہونا ضروری نہیں ہے۔ ٹراورسل ایکسپ انڈیکس کے ذریعے کیا جائے گا، اور پرائمری کے ذریعے حذف کیا جائے گا۔ ہمیں یاد ہے کہ پہلے دونوں صرف پرائمری انڈیکس کا استعمال کرتے ہوئے کیے جاتے تھے۔

تیاری کے کام کے بعد، ہم نئے دلائل کے ساتھ اسٹارٹ فنکشن چلاتے ہیں:

capped_connection:call('libcapped-expirationd.start', {'indexed', box.space.tester.id, box.space.tester.index.primary, box.space.tester.index.exp, 3, 1024, 3600})

آئیے 60 سیکنڈ کی زندگی بھر کے ساتھ خلا میں دوبارہ کئی ٹیپلز داخل کریں:

box.space.tester:insert{0, '[email protected]', math.floor(fiber.time()) + 60}
box.space.tester:insert{1, '[email protected]', math.floor(fiber.time()) + 60}
box.space.tester:insert{2, '[email protected]', math.floor(fiber.time()) + 60}

30 سیکنڈ کے بعد، مشابہت کے لحاظ سے، ہم چند مزید ٹیپلز شامل کریں گے:

box.space.tester:insert{3, '[email protected]', math.floor(fiber.time()) + 60}
box.space.tester:insert{4, '[email protected]', math.floor(fiber.time()) + 60}
box.space.tester:insert{5, '[email protected]', math.floor(fiber.time()) + 60}

آئیے چیک کریں کہ اندراج کامیاب رہا:

tarantool> box.space.tester.index.primary:select()
---
- - [0, '[email protected]', 1576421257]
  - [1, '[email protected]', 1576421257]
  - [2, '[email protected]', 1576421257]
  - [3, '[email protected]', 1576421287]
  - [4, '[email protected]', 1576421287]
  - [5, '[email protected]', 1576421287]
...

آئیے 60+ سیکنڈ کے بعد سلیکٹ کو دہرائیں (پہلے ٹیپل کے داخل ہونے کے آغاز سے گنتی) اور دیکھیں کہ کیپ شدہ میعاد ختم ہونے والا ماڈیول پہلے ہی پروسیس ہو چکا ہے:

tarantool> box.space.tester.index.primary:select()
---
- - [3, '[email protected]', 1576421287]
  - [4, '[email protected]', 1576421287]
  - [5, '[email protected]', 1576421287]
...

خلا میں اب بھی کچھ ٹیپلز باقی ہیں جن کے رہنے کے لیے مزید 30 سیکنڈز باقی ہوں گے۔ مزید یہ کہ، 2 کی ID اور 1576421257 کی زندگی بھر کی ID والے tuple سے 3 کی ID اور 1576421287 کی زندگی بھر کے Tuple پر منتقل ہونے پر اسکین رک گیا۔ ایکسپ انڈیکس کیز۔ یہ وہ بچت ہے جسے ہم شروع میں ہی حاصل کرنا چاہتے تھے۔

آئیے کام کو روکیں:

capped_connection:call('libcapped-expirationd.kill', {'indexed'})

Реализация

کسی پروجیکٹ کی تمام خصوصیات کے بارے میں بتانے کا بہترین طریقہ اس کا اصل ماخذ ہے۔ کوڈ! اشاعت کے حصے کے طور پر، ہم صرف سب سے اہم نکات پر توجہ مرکوز کریں گے، یعنی اسپیس بائی پاس الگورتھم۔

جو دلائل ہم اسٹارٹ میتھڈ پر پاس کرتے ہیں وہ ایک ڈھانچے میں محفوظ ہوتے ہیں جسے expirationd_task کہتے ہیں:

struct expirationd_task
{
  char name[256];
  uint32_t space_id;
  uint32_t rm_index_id;
  uint32_t it_index_id;
  uint32_t it_index_type; 
  uint32_t field_no;
  uint32_t scan_size;
  uint32_t scan_time;
};

نام وصف کام کا نام ہے۔ space_id وصف خلائی شناخت کنندہ ہے۔ rm_index_id وصف اس منفرد انڈیکس کا شناخت کنندہ ہے جس کے ذریعے ٹیپلز کو حذف کر دیا جائے گا۔ it_index_id انتساب انڈیکس کا شناخت کنندہ ہے جس کے ذریعے ٹیپلز کو عبور کیا جائے گا۔ it_index_type وصف انڈیکس کی وہ قسم ہے جس کے ذریعے ٹیپلز کو عبور کیا جائے گا۔ file_no وصف زندگی بھر کے ساتھ ٹوپل فیلڈ کا نمبر ہے۔ اسکین_سائز وصف ایک ٹرانزیکشن میں اسکین کیے جانے والے ٹیوپلز کی زیادہ سے زیادہ تعداد ہے۔ اسکین_ ٹائم وصف سیکنڈوں میں مکمل اسکین ٹائم ہے۔

ہم تجزیہ کرنے والے دلائل پر غور نہیں کریں گے۔ یہ ایک محنت طلب لیکن آسان کام ہے، جس میں لائبریری آپ کی مدد کرے گی۔ msgpuck. مشکلات صرف ان اشاریہ جات کے ساتھ پیدا ہو سکتی ہیں جو Lua سے ایک پیچیدہ ڈیٹا سٹرکچر کے طور پر mp_map قسم کے ساتھ پاس کیے جاتے ہیں، اور سادہ اقسام mp_bool، mp_double، mp_int، mp_uint اور mp_array کا استعمال نہیں کرتے ہیں۔ لیکن پورے انڈیکس کو پارس کرنے کی ضرورت نہیں ہے۔ آپ کو صرف اس کی انفرادیت کو چیک کرنے، قسم کا حساب لگانے اور شناخت کنندہ کو نکالنے کی ضرورت ہے۔

ہم ان تمام فنکشنز کے پروٹو ٹائپس کی فہرست بناتے ہیں جو پارس کرنے کے لیے استعمال ہوتے ہیں:

bool expirationd_parse_name(struct expirationd_task *task, const char **pos);
bool expirationd_parse_space_id(struct expirationd_task *task, const char **pos);
bool expirationd_parse_rm_index_id(struct expirationd_task *task, const char **pos);
bool expirationd_parse_rm_index_unique(struct expirationd_task *task, const char **pos);
bool expirationd_parse_rm_index(struct expirationd_task *task, const char **pos);
bool expirationd_parse_it_index_id(struct expirationd_task *task, const char **pos);
bool expirationd_parse_it_index_type(struct expirationd_task *task, const char **pos);
bool expirationd_parse_it_index(struct expirationd_task *task, const char **pos);
bool expirationd_parse_field_no(struct expirationd_task *task, const char **pos);
bool expirationd_parse_scan_size(struct expirationd_task *task, const char **pos);
bool expirationd_parse_scan_time(struct expirationd_task *task, const char **pos);

اب سب سے اہم چیز کی طرف چلتے ہیں - جگہ کو نظرانداز کرنے اور ٹیپلز کو حذف کرنے کی منطق۔ اسکین_سائز سے بڑے ٹیوپلز کے ہر بلاک کو ایک ہی لین دین کے تحت اسکین اور تبدیل کیا جاتا ہے۔ اگر کامیاب ہو جاتا ہے، تو یہ لین دین کا ارتکاب کیا جاتا ہے؛ اگر غلطی ہوتی ہے، تو اسے واپس کر دیا جاتا ہے۔ expirationd_iterate فنکشن کی آخری دلیل دوبارہ کرنے والے کی طرف اشارہ ہے جہاں سے اسکیننگ شروع ہوتی ہے یا جاری رہتی ہے۔ اس تکرار کو اندرونی طور پر بڑھایا جاتا ہے جب تک کہ کوئی خرابی واقع نہ ہو جائے، جگہ ختم نہ ہو جائے، یا اس عمل کو پہلے سے روکنا ممکن نہ ہو۔ expirationd_expired فنکشن ٹیوپل کی زندگی کو چیک کرتا ہے، expirationd_delete ایک tuple کو حذف کرتا ہے، expirationd_breakable چیک کرتا ہے کہ آیا ہمیں آگے بڑھنے کی ضرورت ہے۔

Expirationd_iterate فنکشن کوڈ:

static bool
expirationd_iterate(struct expirationd_task *task, box_iterator_t **iterp)
{
  box_iterator_t *iter = *iterp;
  box_txn_begin();
  for (uint32_t i = 0; i < task->scan_size; ++i) {
    box_tuple_t *tuple = NULL;
    if (box_iterator_next(iter, &tuple) < 0) {
      box_iterator_free(iter);
      *iterp = NULL;
      box_txn_rollback();
      return false;
    }
    if (!tuple) {
      box_iterator_free(iter);
      *iterp = NULL;
      box_txn_commit();
      return true;
    }
    if (expirationd_expired(task, tuple))
      expirationd_delete(task, tuple);
    else if (expirationd_breakable(task))
      break;
  }
  box_txn_commit();
  return true;
}

فنکشن کوڈ expired_expired:

static bool
expirationd_expired(struct expirationd_task *task, box_tuple_t *tuple)
{
  const char *buf = box_tuple_field(tuple, task->field_no - 1);
  if (!buf || mp_typeof(*buf) != MP_UINT)
    return false;
  uint64_t val = mp_decode_uint(&buf);
  if (val > fiber_time64() / 1000000)
    return false;
  return true;
}

Expired_delete فنکشن کوڈ:

static void
expirationd_delete(struct expirationd_task *task, box_tuple_t *tuple)
{
  uint32_t len;
  const char *str = box_tuple_extract_key(tuple, task->space_id, task->rm_index_id, &len);
  box_delete(task->space_id, task->rm_index_id, str, str + len, NULL);
}

فنکشن کوڈ ایکسپائریشن_بریک ایبل:

static bool
expirationd_breakable(struct expirationd_task *task)
{
  return task->it_index_id != task->rm_index_id && task->it_index_type == ITER_GT;
}

درخواست

آپ سورس کوڈ پر دیکھ سکتے ہیں۔ یہاں!

ماخذ: www.habr.com

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