
Bir muncha vaqt oldin biz bo'shliqlardagi katakchalarni tozalash muammosiga duch keldik Tozalashni tarantool xotirasi kam bo'lganda emas, balki oldindan va ma'lum bir chastotada boshlash kerak edi. Tarantool bu vazifa uchun Lua tilida yozilgan modulga ega, u shunday nomlanadi. Ushbu moduldan qisqa vaqt foydalangandan so'ng, biz uning bizning ehtiyojlarimizga mos kelmasligini angladik: Lua katta hajmdagi ma'lumotlarni doimiy ravishda tozalash paytida ishdan chiqib ketayotgan edi. Shuning uchun biz o'zimizning cheklangan amal qilish muddati modulini ishlab chiqishni ko'rib chiqdik, umid qilamizki, ona dasturlash tilida yozilgan kod bizning ehtiyojlarimizni eng yaxshi qondiradi.
Biz uchun yaxshi misol tarantool moduli edi U foydalanadigan yondashuv bo'shliqda alohida maydon yaratishga asoslangan bo'lib, u tuplening yashash vaqtini (TTL) belgilaydi. Modul fonda bo'shliqni skanerlaydi, TTLni joriy vaqt bilan taqqoslaydi va tupleni o'chirish-o'chirmaslik to'g'risida qaror qabul qiladi. Memcached modulining kodi sodda va oqlangan, ammo juda umumiy. Birinchidan, u o'tilayotgan va o'chirilayotgan indeks turini hisobga olmaydi. Ikkinchidan, har bir o'tish barcha tuplelarni skanerlaydi, ular juda katta bo'lishi mumkin. Muddati tugagan modul birinchi muammoni (daraxt indeksini alohida sinfga ajratish) hal qilgan bo'lsa-da, ikkinchisi hal qilinmaganligicha qolmoqda. Bu uchta nuqta mening o'z kodimni yozish tanlovimni belgilab berdi.
Tavsif
tarantool uchun juda yaxshi hujjatlar mavjud C tilida o'zingizning saqlangan protseduralaringizni qanday yozish haqida. Avval u bilan tanishib chiqishingizni maslahat beraman, shunda quyida ko'radigan buyruqlar va kodni tushunishingiz mumkin. Shuningdek, bunga e'tibor berishga arziydi. o'zingizning yopiq modulingizni yozishda mavjud bo'lgan obyektlarga, ya'ni , , и .
Keling, tashqaridan boshlaymiz va muddati tugagan modul qanday ko'rinishini ko'rib chiqamiz:
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)Soddalashtirish uchun biz libcapped-expirationd.so kutubxonamizni o'z ichiga olgan katalogda tarantool ni ishga tushiramiz. Kutubxona ikkita funksiyani eksport qiladi: start va kill. Birinchidan, biz bu funksiyalarga box.schema.func.create va box.schema.user.grant yordamida Lua'dan kirish mumkin bo'lishini ta'minlashimiz kerak. Keyin, biz faqat uchta maydonni o'z ichiga oladigan bo'sh joy yaratamiz: birinchisi noyob identifikator, ikkinchisi elektron pochta manzili va uchinchisi esa katakchaning ishlash muddati. Birinchi maydonning ustiga daraxt indeksini yaratamiz va uni asosiy deb ataymiz. Keyin, biz o'zimizning mahalliy kutubxonamiz uchun ulanish obyektini olamiz.
Tayyorgarlik ishlaridan so'ng, biz boshlash funksiyasini ishga tushiramiz:
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})Bu misol skanerlash paytida Lua tilida yozilgan muddati tugagan modul bilan bir xil tarzda ishlaydi. Start funksiyasiga uzatiladigan birinchi argument noyob vazifa nomidir. Ikkinchisi bo'sh joy identifikatori. Uchinchisi - bu karetalarni o'chirish uchun noyob indeks. To'rtinchisi - bu karetalarni kesib o'tish uchun indeks. Beshinchisi - bu ishlash vaqti ko'rsatilgan kareta maydoni raqami (raqamlash 0 dan emas, balki 1 dan boshlanadi!). Oltinchi va yettinchi - skanerlash sozlamalari. 1024 - bitta tranzaksiyada skanerlangan karetalarning maksimal soni. 3600 - bu to'liq skanerlash vaqti (sekundlarda).
E'tibor bering, misolda aylanish va o'chirish uchun bir xil indeks ishlatiladi. Agar bu daraxt indeksi bo'lsa, aylanish kichikroq kalitdan kattaroq kalitga o'tadi. Agar bu xesh indeksi kabi boshqa indeks bo'lsa, aylanish odatda tasodifiy tartibda bajariladi. Bo'shliqdagi barcha katakchalar bitta skanerlashda skanerlanadi.
Keling, bo'sh joyga umri 60 soniya bo'lgan bir nechta juftliklarni joylashtiramiz:
box.space.tester:insert{0, 'user0@tarantool.io', math.floor(fiber.time()) + 60}
box.space.tester:insert{1, 'user1@tarantool.io', math.floor(fiber.time()) + 60}
box.space.tester:insert{2, 'user2@tarantool.io', math.floor(fiber.time()) + 60}Keling, kiritish muvaffaqiyatli bo'lganligini tekshirib ko'ramiz:
tarantool> box.space.tester.index.primary:select()
---
- - [0, 'user0@tarantool.io', 1576418976]
- [1, 'user1@tarantool.io', 1576418976]
- [2, 'user2@tarantool.io', 1576418976]
...Keling, 60+ soniyadan keyin (birinchi tuple qo'shilishining boshidan boshlab hisoblaganda) tanlovni takrorlaymiz va cheklangan muddati tugagan modul allaqachon ishlaganligini ko'ramiz:
tarantool> box.space.tester.index.primary:select()
---
- []
...Keling, vazifani to'xtataylik:
capped_connection:call('libcapped-expirationd.kill', {'non-indexed'})Keling, aylanma yo'l uchun alohida indeks ishlatiladigan ikkinchi misolni ko'rib chiqaylik:
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)Bu yerda hamma narsa birinchi misoldagidek, bir nechta istisnolardan tashqari. Biz uchinchi maydonning ustiga daraxt indeksini quramiz va uni exp deb ataymiz. Bu indeks, asosiy deb nomlangan indeksdan farqli o'laroq, noyob bo'lishi shart emas. Sayr qilish exp indeksi yordamida, o'chirish esa asosiy indeks yordamida amalga oshiriladi. Eslatib o'tamiz, ilgari ikkalasi ham faqat asosiy indeks yordamida amalga oshirilgan.
Tayyorgarlik ishlaridan so'ng, biz yangi argumentlar bilan start funksiyasini ishga tushiramiz:
capped_connection:call('libcapped-expirationd.start', {'indexed', box.space.tester.id, box.space.tester.index.primary, box.space.tester.index.exp, 3, 1024, 3600})Keling, yana bir nechta katakchalarni bo'sh joyga joylashtiramiz, ularning umri 60 soniya:
box.space.tester:insert{0, 'user0@tarantool.io', math.floor(fiber.time()) + 60}
box.space.tester:insert{1, 'user1@tarantool.io', math.floor(fiber.time()) + 60}
box.space.tester:insert{2, 'user2@tarantool.io', math.floor(fiber.time()) + 60}30 soniyadan so'ng, biz xuddi shu tarzda yana bir nechta juftliklarni qo'shamiz:
box.space.tester:insert{3, 'user3@tarantool.io', math.floor(fiber.time()) + 60}
box.space.tester:insert{4, 'user4@tarantool.io', math.floor(fiber.time()) + 60}
box.space.tester:insert{5, 'user5@tarantool.io', math.floor(fiber.time()) + 60}Keling, kiritish muvaffaqiyatli bo'lganligini tekshirib ko'ramiz:
tarantool> box.space.tester.index.primary:select()
---
- - [0, 'user0@tarantool.io', 1576421257]
- [1, 'user1@tarantool.io', 1576421257]
- [2, 'user2@tarantool.io', 1576421257]
- [3, 'user3@tarantool.io', 1576421287]
- [4, 'user4@tarantool.io', 1576421287]
- [5, 'user5@tarantool.io', 1576421287]
...Keling, 60+ soniyadan keyin (birinchi tuple qo'shilishining boshidan boshlab hisoblaganda) tanlovni takrorlaymiz va cheklangan muddati tugagan modul allaqachon ishlaganligini ko'ramiz:
tarantool> box.space.tester.index.primary:select()
---
- - [3, 'user3@tarantool.io', 1576421287]
- [4, 'user4@tarantool.io', 1576421287]
- [5, 'user5@tarantool.io', 1576421287]
...Bo'shliqda taxminan 30 soniya yashashi mumkin bo'lgan juftliklar qoldi. Bundan tashqari, 2-raqamli va umri 1576421257 bo'lgan juftlikdan 3-raqamli va umri 1576421287 bo'lgan juftlikka o'tishda skanerlash to'xtadi. 1576421287 yoki undan katta umr ko'rish muddatiga ega juftliklar exp indeks kalitlarining tartiblanishi tufayli skanerlanmadi. Bu biz boshidanoq maqsad qilgan tejash edi.
Keling, vazifani to'xtataylik:
capped_connection:call('libcapped-expirationd.kill', {'indexed'})Реализация
Loyihaning barcha xususiyatlari haqida gapirishning eng yaxshi usuli har doim uning manbasidan kelib chiqadi. Ushbu nashrda biz faqat eng muhim jihatlarga, ya'ni fazoni chetlab o'tish algoritmlariga e'tibor qaratamiz.
start usuliga uzatadigan argumentlarimiz expiryd_task deb nomlangan tuzilmada saqlanadi:
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;
};Name atributi vazifa nomidir. space_id atributi bo'sh joy identifikatoridir. rm_index_id atributi tuple o'chirish uchun ishlatiladigan noyob indeksning identifikatoridir. it_index_id atributi tuple o'tish uchun ishlatiladigan indeksning identifikatoridir. it_index_type atributi tuple o'tish uchun ishlatiladigan indeks turidir. filed_no atributi - vaqt-to-live qiymatiga ega tuple maydoni raqamidir. scan_size atributi - bitta tranzaksiyada skanerlangan tuplelarning maksimal soni. scan_time atributi - sekundlarda to'liq skanerlash vaqti.
Biz argumentlarni tahlil qilishni ko'rib chiqmaymiz. Bu mashaqqatli, ammo sodda ish va kutubxona sizga bu borada yordam beradi. Qiyinchiliklar faqat Lua'dan mp_bool, mp_double, mp_int, mp_uint va mp_array turlaridan foydalanish o'rniga, mp_map turiga ega murakkab ma'lumotlar tuzilmasi sifatida uzatilgan indekslar bilan yuzaga kelishi mumkin. Biroq, butun indeksni tahlil qilish shart emas. Uning noyobligini tekshirish, turini hisoblash va identifikatorni ajratib olish kifoya.
Keling, tahlil qilish uchun ishlatiladigan barcha funktsiyalarning prototiplarini sanab o'tamiz:
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);Endi eng muhim qismga — bo'shliqni aylanib o'tish va katakchani o'chirish mantig'iga o'tamiz. scan_size dan katta bo'lmagan katakchalarning har bir bloki bitta tranzaksiya orqali skanerlanadi va o'zgartiriladi. Agar muvaffaqiyatli bo'lsa, bu tranzaksiya amalga oshiriladi; agar xatolik yuzaga kelsa, u orqaga qaytariladi. expiryd_iterate funksiyasiga uzatilgan oxirgi argument skanerlash boshlanadigan yoki davom etadigan iteratorga ko'rsatkichdir. Bu iterator xatolik yuzaga kelguncha, bo'sh joy tugaguncha yoki jarayonni erta to'xtatishning iloji bo'lmaguncha ichki ravishda oshiriladi. expiryd_expired funksiyasi katakchaning ishlash muddatini tekshiradi, expiryd_delete katakchani o'chiradi va expiryd_breakable biz davom etishimiz kerakligini tekshiradi.
Expirationd_iterate funksiya kodi:
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;
}expiryd_expired funksiya kodi:
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 funksiya kodi:
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);
}Funktsiya kodi expiry_breakable:
static bool
expirationd_breakable(struct expirationd_task *task)
{
return task->it_index_id != task->rm_index_id && task->it_index_type == ITER_GT;
}ariza
Siz manba kodini quyidagi manzilda ko'rishingiz mumkin !
Manba: www.habr.com
