tarantool لاءِ اسان جي پنهنجي ڪئپ ٿيل ختم ٿيل ماڊل لکڻ

tarantool لاءِ اسان جي پنهنجي ڪئپ ٿيل ختم ٿيل ماڊل لکڻ

ڪجهه وقت اڳ اسان کي خالن ۾ ٽوپل جي صفائي جي مسئلي سان منهن ڏيڻو پيو ٽارنٽول. صفائي جي شروعات نه ٿيڻ گهرجي جڏهن ٽرانٽول اڳ ۾ ئي ياداشت کان ٻاهر هلي رهيو هو، پر اڳ ۾ ۽ هڪ خاص تعدد تي. هن ڪم لاء، tarantool ۾ Lua ۾ لکيل هڪ ماڊل آهي ختم ٿيڻ. ٿوري وقت لاءِ هن ماڊل کي استعمال ڪرڻ کان پوءِ، اسان محسوس ڪيو ته اهو اسان لاءِ موزون نه هو: ڊيٽا جي وڏي مقدار جي مسلسل صفائي جي ڪري، لوا GC ۾ لٽڪي رهي هئي. تنهن ڪري، اسان پنهنجو پاڻ کي ختم ڪرڻ واري ماڊل کي ترقي ڪرڻ جي باري ۾ سوچيو، اميد آهي ته ڪوڊ هڪ ڏيهي پروگرامنگ ٻوليء ۾ لکيل اسان جي مسئلن کي بهترين طريقي سان حل ڪندو.

اسان لاء هڪ سٺو مثال tarantool ماڊل سڏيو ويندو هو يادگار. ان ۾ استعمال ٿيل طريقو حقيقت تي مبني آهي ته خلا ۾ هڪ الڳ فيلڊ ٺاهي وئي آهي، جيڪو اشارو ڪري ٿو ٽپل جي زندگي گذارڻ، ٻين لفظن ۾، ttl. پس منظر ۾ ماڊل خلا کي اسڪين ڪري ٿو، موجوده وقت سان TTL جو مقابلو ڪري ٿو ۽ فيصلو ڪري ٿو ته ڇا ٽوپل کي ختم ڪرڻ يا نه. memcached ماڊل ڪوڊ سادو ۽ خوبصورت آهي، پر تمام عام. پهريون، اهو انڊيڪس جي قسم کي حساب ۾ نٿو رکي جيڪو ڪريل ۽ ختم ڪيو پيو وڃي. ٻيو، هر پاسن تي سڀئي ٽوپل اسڪين ٿيل آهن، جن جو تعداد تمام وڏو ٿي سگهي ٿو. ۽ جيڪڏهن ختم ٿيل ماڊل ۾ پهريون مسئلو حل ڪيو ويو آهي (وڻ انڊيڪس هڪ الڳ طبقي ۾ ورهايو ويو آهي)، پوء ٻيو اڃا تائين ڌيان نه ڏنو ويو. اهي ٽي نقطا منهنجي پنهنجي ڪوڊ لکڻ جي حق ۾ چونڊ اڳ ۾ ئي طئي ڪيا.

بيان

tarantool لاء دستاويز تمام سٺو آهي سبق سي ۾ توهان جي ذخيرو ٿيل طريقيڪار کي ڪيئن لکڻ لاءِ. سڀ کان پهريان، مان توهان کي ان سان واقف ڪرڻ جي صلاح ڏيان ٿو ته جيئن هيٺ ڏنل حڪمن ۽ ڪوڊ سان انهن داخلن کي سمجهڻ لاءِ. اهو پڻ ڌيان ڏيڻ جي قابل آهي حوالو انهن شين ڏانهن جيڪي موجود آهن جڏهن توهان جي پنهنجي ڪئپ ٿيل ماڊل لکڻ، يعني باڪس, مڱريو, اشهد и 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 لائبريري واقع آهي. لائبريري مان ٻه فنڪشن برآمد ڪيا ويا آهن: شروع ۽ مارڻ. پهريون قدم اهو آهي ته box.schema.func.create ۽ box.schema.user.grant استعمال ڪندي Lua کان انهن فنڪشن کي دستياب ڪيو وڃي. پوءِ ھڪڙو جڳھ ٺاھيو جنھن جي ٽوپل ۾ صرف ٽن شعبن تي مشتمل ھوندو: پھريون ھڪڙو منفرد سڃاڻپ ڪندڙ آھي، ٻيو آھي اي ميل، ۽ ٽيون آھي ٽيوپل جي حياتي. اسان پهرين فيلڊ جي چوٽي تي هڪ وڻ انڊيڪس ٺاهيو ۽ ان کي پرائمري سڏين ٿا. اڳيون اسان کي ملن ٿا ڪنيڪشن اعتراض اسان جي ڏيهي لائبريري ڏانهن.

تياري واري ڪم کان پوء، شروعاتي فنڪشن کي هلائڻ:

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 سڏين ٿا. هي انڊيڪس منفرد هجڻ ضروري ناهي، انڊيڪس جي برعڪس جنهن کي پرائمري سڏيو ويندو آهي. traversal exp index جي ذريعي ڪيو ويندو، ۽ حذف ڪرڻ پرائمري طرفان ڪيو ويندو. اسان کي ياد آهي ته اڳي ٻنهي کي صرف پرائمري انڊيڪس استعمال ڪندي ڪيو ويو.

تياري واري ڪم کان پوءِ، اسان نون دليلن سان شروعاتي فنڪشن هلون ٿا:

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 جي زندگي گذارڻ واري ٽپل کان 3 جي آئي ڊي ۽ 1576421287 جي زندگي گذارڻ واري ٽوپل تي منتقل ٿي وئي. 1576421287 يا ان کان وڌيڪ جي زندگي گذارڻ واري ٽوپل جي ترتيب جي ڪري اسڪين نه ڪيا ويا. exp index چابيون. اها بچت آهي جيڪا اسان حاصل ڪرڻ چاهيون ٿا شروعات ۾.

اچو ته ڪم کي روڪيو:

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 انتساب انڊيڪس جو قسم آھي جنھن جي ذريعي ٽوپلز کي ڇڪايو ويندو. Filed_no وصف زندگيءَ سان گڏ ٽپل فيلڊ جو تعداد آهي. scan_size وصف ٽيپلن جو وڌ ۾ وڌ تعداد آھي جيڪي ھڪڙي ٽرانزيڪشن ۾ اسڪين ڪيا ويا آھن. scan_time خاصيت سيڪنڊن ۾ مڪمل اسڪين وقت آهي.

اسان بحث مباحثن تي غور نه ڪنداسين. اھو ھڪڙو مشڪل پر سادو ڪم آھي، جنھن سان لائبريري توھان جي مدد ڪندي 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 هڪ ٽوپل کي ختم ڪري ٿو، 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;
}

فنڪشن ڪوڊ expirationd_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;
}

Expirationd_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);
}

Expirationd_breakable فنڪشن ڪوڊ:

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

تبصرو شامل ڪريو