کچھ عرصہ پہلے ہمیں خالی جگہوں پر ٹیپلز کی صفائی کا مسئلہ درپیش تھا۔
ہمارے لیے ایک اچھی مثال ٹارنٹول ماڈیول تھی جسے کہا جاتا ہے۔
تفصیل
ٹرانٹول کے لیے دستاویزات بہت اچھی ہیں۔
آئیے دور سے شروع کریں اور دیکھیں کہ ایک محدود میعاد ختم ہونے والا ماڈیول باہر سے کیسا لگتا ہے:
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 وصف زندگی بھر کے ساتھ ٹوپل فیلڈ کا نمبر ہے۔ اسکین_سائز وصف ایک ٹرانزیکشن میں اسکین کیے جانے والے ٹیوپلز کی زیادہ سے زیادہ تعداد ہے۔ اسکین_ ٹائم وصف سیکنڈوں میں مکمل اسکین ٹائم ہے۔
ہم تجزیہ کرنے والے دلائل پر غور نہیں کریں گے۔ یہ ایک محنت طلب لیکن آسان کام ہے، جس میں لائبریری آپ کی مدد کرے گی۔
ہم ان تمام فنکشنز کے پروٹو ٹائپس کی فہرست بناتے ہیں جو پارس کرنے کے لیے استعمال ہوتے ہیں:
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