Tarantool uchun o'zimizning muddati tugagan modulni yozish

Tarantool uchun o'zimizning muddati tugagan modulni yozish

Bir muncha vaqt oldin biz bo'shliqlarda tuplarni tozalash muammosiga duch keldik tarantool. Tozalashni tarantool xotirasi tugab qolganda emas, balki oldindan va ma'lum bir chastotada boshlash kerak edi. Ushbu vazifa uchun tarantoolda Lua tilida yozilgan modul mavjud amal qilish muddati. Ushbu modulni qisqa vaqt davomida ishlatganimizdan so'ng, biz bu biz uchun mos emasligini tushundik: katta hajmdagi ma'lumotlarni doimiy tozalash tufayli Lua GCda osilgan. Shuning uchun biz ona dasturlash tilida yozilgan kod bizning muammolarimizni eng yaxshi tarzda hal qilishiga umid qilib, o'z muddati tugagan modulimizni ishlab chiqish haqida o'yladik.

Biz uchun yaxshi misol tarantool moduli edi yodlangan. Unda qo'llaniladigan yondashuv fazoda kortejning ishlash muddatini, boshqacha aytganda, ttlni ko'rsatadigan alohida maydon yaratilishiga asoslanadi. Fondagi modul bo'sh joyni skanerlaydi, TTLni joriy vaqt bilan solishtiradi va kortejni o'chirish yoki o'chirishni hal qiladi. Memcached modul kodi oddiy va oqlangan, lekin juda umumiy. Birinchidan, u tekshirilayotgan va o'chirilayotgan indeks turini hisobga olmaydi. Ikkinchidan, har bir o'tishda barcha kortejlar skanerdan o'tkaziladi, ularning soni juda katta bo'lishi mumkin. Va agar muddati tugagan modulda birinchi muammo hal qilingan bo'lsa (daraxt indeksi alohida sinfga ajratilgan), ikkinchisiga hali ham e'tibor berilmagan. Ushbu uchta nuqta o'z kodimni yozish foydasiga tanlovni oldindan belgilab qo'ydi.

tavsifi

Tarantool uchun hujjatlar juda yaxshi darslik C da saqlangan protseduralaringizni qanday yozish haqida. Avvalo, quyida paydo bo'ladigan buyruqlar va kodli qo'shimchalarni tushunish uchun u bilan tanishishingizni tavsiya qilaman. Bundan tashqari, e'tibor berishga arziydi ma'lumotnoma o'zingizning yopiq modulingizni yozishda mavjud bo'lgan ob'ektlarga, ya'ni box, tola, indeks ΠΈ txn.

Keling, uzoqdan boshlaymiz va muddati tugagan modul tashqi tomondan 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)

Oddiylik uchun biz libcapped-expirationd.so kutubxonamiz joylashgan katalogda tarantoolni ishga tushiramiz. Kutubxonadan ikkita funktsiya eksport qilinadi: boshlash va o'ldirish. Birinchi qadam bu funksiyalarni Lua'dan box.schema.func.create va box.schema.user.grant yordamida mavjud qilishdir. Keyin kortejlarida faqat uchta maydon bo'ladigan bo'sh joy yarating: birinchisi - noyob identifikator, ikkinchisi - elektron pochta, uchinchisi - kortejning ishlash muddati. Biz birinchi maydonning tepasida daraxt indeksini quramiz va uni birlamchi deb ataymiz. Keyin biz o'z kutubxonamizga ulanish ob'ektini olamiz.

Tayyorgarlik ishlaridan so'ng boshlash funksiyasini ishga tushiring:

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

Ushbu misol skanerlash paytida Lua tilida yozilgan muddati tugagan modul bilan bir xil ishlaydi. Boshlash funksiyasining birinchi argumenti vazifaning noyob nomidir. Ikkinchisi bo'sh joy identifikatoridir. Uchinchisi - noyob indeks bo'lib, uning yordamida kortejlar o'chiriladi. To'rtinchisi - kortejlar kesib o'tiladigan indeks. Beshinchisi - umr bo'yi bo'lgan kortej maydonining soni (raqamlash 1 dan emas, 0 dan boshlanadi!). Oltinchi va ettinchisi skanerlash sozlamalari. 1024 - bitta tranzaksiyada ko'rish mumkin bo'lgan kortejlarning maksimal soni. 3600 - soniyalarda to'liq skanerlash vaqti.

E'tibor bering, misolda skanerlash va o'chirish uchun bir xil indeks ishlatiladi. Agar bu daraxt ko'rsatkichi bo'lsa, u holda o'tish kichikroq kalitdan kattaroqqa amalga oshiriladi. Agar boshqa, masalan, hash indeksi bo'lsa, u holda o'tish, qoida tariqasida, tasodifiy tartibda amalga oshiriladi. Barcha kosmik kortejlar bitta skanerda skanerlanadi.

Keling, fazoga 60 soniya xizmat qiladigan bir nechta kortejlarni kiritamiz:

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}

Kiritish muvaffaqiyatli bo'lganligini tekshirib ko'raylik:

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

60+ soniyadan so'ng tanlashni takrorlaymiz (birinchi kortej qo'shilganidan boshlab) va cheklov muddati tugagan modul allaqachon qayta ishlanganligini ko'ramiz:

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

Keling, vazifani to'xtatamiz:

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

Keling, ikkinchi misolni ko'rib chiqaylik, bu erda alohida indeks skanerlash uchun ishlatiladi:

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 erda hamma narsa birinchi misolda bo'lgani kabi, bir nechta istisnolardan tashqari. Biz uchinchi maydonning tepasida daraxt indeksini quramiz va uni exp deb ataymiz. Bu indeks birlamchi deb ataladigan indeksdan farqli o'laroq, noyob bo'lishi shart emas. O'tish eksp indeksi bo'yicha, o'chirish esa birlamchi bo'yicha amalga oshiriladi. Esda tutamizki, 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, bo'shliqqa yana bir nechta kortejlarni 60 soniya davomida kiritamiz:

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 soniyadan so'ng, analogiya bo'yicha, biz yana bir nechta kortejlarni qo'shamiz:

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}

Kiritish muvaffaqiyatli bo'lganligini tekshirib ko'raylik:

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+ soniyadan so'ng tanlashni takrorlaymiz (birinchi kortej qo'shilganidan boshlab) va cheklov muddati tugagan modul allaqachon qayta ishlanganligini ko'ramiz:

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

Fazoda yana 30 soniya yashashi kerak bo'lgan ba'zi kortejlar qoldi. Bundan tashqari, identifikatori 2 va umri 1576421257 boβ€˜lgan kortejdan ID raqami 3 va umri 1576421287 boβ€˜lgan kortejga oβ€˜tishda skanerlash toβ€˜xtatildi. Yaroqlilik muddati 1576421287 va undan ortiq boβ€˜lgan kortejlar tartiblanganligi sababli tekshirilmadi. exp indeks kalitlari. Bu biz boshida erishmoqchi bo'lgan tejash.

Keling, vazifani to'xtatamiz:

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

РСализация

Loyihaning barcha xususiyatlari haqida gapirishning eng yaxshi usuli bu uning asl manbasidir. kodi! Nashrning bir qismi sifatida biz faqat eng muhim nuqtalarga, ya'ni kosmik aylanib o'tish algoritmlariga e'tibor qaratamiz.

Biz start usuliga o'tadigan argumentlar expirationd_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 vazifaning nomidir. space_id atributi bo'sh joy identifikatoridir. rm_index_id atributi kortejlar o'chiradigan noyob indeksning identifikatoridir. it_index_id atributi kortejlar o'tadigan indeksning identifikatoridir. it_index_type atributi kortejlar o'tadigan indeks turidir. filed_no atributi umr bo'yi bo'lgan kortej maydonining soni. scan_size atributi - bu bitta tranzaksiyada skanerlanadigan kortejlarning maksimal soni. scan_time atributi soniyalardagi to'liq skanerlash vaqtidir.

Biz argumentlarni tahlil qilishni ko'rib chiqmaymiz. Bu mashaqqatli, ammo oddiy ish bo'lib, kutubxona sizga yordam beradi msgpuck. Qiyinchiliklar faqat mp_bool, mp_double, mp_int, mp_uint va mp_array oddiy turlaridan foydalanmasdan, Lua'dan mp_map turiga ega murakkab ma'lumotlar strukturasi sifatida uzatiladigan indekslar bilan yuzaga kelishi mumkin. Ammo butun indeksni tahlil qilishning hojati yo'q. Siz shunchaki uning o'ziga xosligini tekshirishingiz, turini hisoblashingiz va identifikatorni olishingiz kerak.

Biz 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 narsaga - bo'sh joyni chetlab o'tish va kortejlarni o'chirish mantig'iga o'tamiz. scan_size dan katta bo'lmagan har bir kortej bloki bitta tranzaksiya ostida skanerlanadi va o'zgartiriladi. Muvaffaqiyatli bo'lsa, bu tranzaksiya amalga oshiriladi; xatolik yuzaga kelsa, u orqaga qaytariladi. expirationd_iterate funktsiyasining oxirgi argumenti skanerlash boshlanadigan yoki davom etadigan iteratorga ko'rsatgichdir. Ushbu iterator xatolik yuzaga kelgunga qadar, bo'sh joy tugamaguncha yoki jarayonni oldindan to'xtatib bo'lmaguncha ichki ravishda oshiriladi. expirationd_expired funktsiyasi kortejning ishlash muddatini tekshiradi, expirationd_delete kortejni o'chiradi, expirationd_breakable biz davom etishimiz kerakligini tekshiradi.

Expirationd_iterate funktsiya 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;
}

Funktsiya kodi 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 funktsiya 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);
}

Muddati tugagan_buzilishi mumkin bo'lgan funksiya kodi:

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

ariza

Manba kodini quyidagi manzilda ko'rishingiz mumkin shu yerda!

Manba: www.habr.com

a Izoh qo'shish