Nulis modul kadaluwarsa sing ditutup kanggo tarantool

Nulis modul kadaluwarsa sing ditutup kanggo tarantool

Sawetara wektu kepungkur kita ngadhepi masalah ngresiki tuple ing spasi tarantool. Reresik kudu diwiwiti ora nalika tarantool wis entek memori, nanging sadurunge lan ing frekuensi tartamtu. Kanggo tugas iki, tarantool duwe modul sing ditulis ing Lua disebut kadaluwarsa. Sawise nggunakake modul iki kanggo wektu cendhak, kita temen maujud sing ora cocok kanggo kita: amarga reresik pancet saka jumlah gedhe saka data, Lua Hung ing GC. Mula, kita mikir babagan ngembangake modul kadaluwarsa sing ditutup dhewe, ngarep-arep kode sing ditulis ing basa pamrograman asli bakal ngrampungake masalah kita kanthi cara sing paling apik.

Conto sing apik kanggo kita yaiku modul tarantool sing diarani memcached. Pendekatan sing digunakake ing kono adhedhasar kasunyatan manawa lapangan sing kapisah digawe ing papan kasebut, sing nuduhake umur tuple, kanthi tembung liya, ttl. Modul ing latar mburi mindai spasi, mbandhingake TTL karo wektu saiki lan arep mbusak tuple utawa ora. Kode modul memcached prasaja lan elegan, nanging banget umum. Kaping pisanan, ora nganggep jinis indeks sing dirayapi lan dibusak. Kapindho, ing saben pass kabeh tuples dipindai, nomer kang bisa cukup gedhe. Lan yen ing modul kadaluwarsa masalah pisanan ditanggulangi (indeks wit dipisahake dadi kelas sing kapisah), banjur sing nomer loro isih ora entuk perhatian. Iki telung TCTerms predetermined pilihan ing sih saka nulis kode dhewe.

Description

Dokumentasi kanggo tarantool wis apik banget tutorial babagan carane nulis tata cara sing disimpen ing C. Kaping pisanan, aku saranake sampeyan familiarize dhewe karo supaya ngerti sisipan karo printah lan kode sing bakal katon ing ngisor iki. Iku uga worth mbayar manungsa waΓ© kanggo referensi kanggo obyek sing kasedhiya nalika nulis modul katutup dhewe, yaiku kothak, serat, indeks ΠΈ txn.

Ayo miwiti saka adoh lan ndeleng apa modul kadaluwarsa sing ditutup saka njaba:

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)

Kanggo gamblang, kita miwiti tarantool ing direktori ngendi perpustakaan libcapped-expirationd.so kita dumunung. Rong fungsi diekspor saka perpustakaan: miwiti lan mateni. Langkah pisanan yaiku nggawe fungsi kasebut kasedhiya saka Lua nggunakake box.schema.func.create lan box.schema.user.grant. Banjur gawe spasi sing tuple mung ngemot telung kolom: sing pisanan minangka pengenal unik, sing kapindho yaiku email, lan sing katelu yaiku umur tuple. Kita mbangun indeks wit ing ndhuwur lapangan pisanan lan nyebataken utami. Sabanjure kita entuk obyek sambungan menyang perpustakaan asli kita.

Sawise karya persiapan, jalanake fungsi wiwitan:

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

Conto iki bakal bisa digunakake nalika mindhai persis padha karo modul kadaluwarsa, sing ditulis ing Lua. Argumentasi pisanan kanggo fungsi wiwitan yaiku jeneng unik saka tugas kasebut. Kapindho yaiku pengenal spasi. Katelu yaiku indeks unik sing tuples bakal dibusak. Ingkang kaping sekawan inggih menika indeks ingkang badhe dipunlampahi tuple. Kalima yaiku nomer lapangan tuple kanthi umur (nomer diwiwiti saka 1, dudu 0!). Sing nomer enem lan kapitu yaiku setelan mindhai. 1024 minangka jumlah maksimum tuple sing bisa dideleng ing siji transaksi. 3600 - wektu scan lengkap ing detik.

Elinga yen conto nggunakake indeks sing padha kanggo crawling lan mbusak. Yen iki minangka indeks wit, mula traversal ditindakake saka kunci sing luwih cilik menyang sing luwih gedhe. Yen ana sawetara liyane, umpamane, indeks hash, banjur traversal ditindakake, minangka aturan, kanthi urutan acak. Kabeh tuple spasi dipindai ing siji scan.

Ayo lebokake sawetara tupel menyang ruang kanthi umur 60 detik:

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}

Ayo priksa manawa sisipan kasebut sukses:

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

Ayo baleni pilih sawise 60+ detik (dietung saka wiwitan nglebokake tuple pisanan) lan deleng manawa modul kadaluwarsa sing ditutup wis diproses:

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

Ayo mungkasi tugas:

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

Ayo goleki conto liyane ing ngendi indeks kapisah digunakake kanggo nyusup:

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)

Kabeh ing kene padha karo conto pisanan, kanthi sawetara pangecualian. Kita mbangun indeks wit ing ndhuwur lapangan katelu lan nyebataken exp. Indeks iki ora kudu unik, ora kaya indeks sing diarani primer. Traversal bakal ditindakake kanthi indeks exp, lan pambusakan kanthi utami. Kita elinga yen sadurunge loro-lorone ditindakake mung nggunakake indeks utama.

Sawise karya persiapan, kita mbukak fungsi wiwitan kanthi argumen anyar:

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

Ayo lebokake sawetara tupel menyang spasi maneh kanthi umur 60 detik:

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}

Sawise 30 detik, kanthi analogi, kita bakal nambah sawetara tuple maneh:

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}

Ayo priksa manawa sisipan kasebut sukses:

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]
...

Ayo baleni pilih sawise 60+ detik (dietung saka wiwitan nglebokake tuple pisanan) lan deleng manawa modul kadaluwarsa sing ditutup wis diproses:

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

Isih ana sawetara tuple sing isih ana ing papan sing bakal duwe udakara 30 detik maneh. Kajaba iku, pindai mandheg nalika pindhah saka tuple kanthi ID 2 lan umur 1576421257 menyang tuple kanthi ID 3 lan umur 1576421287. Tuple kanthi umur 1576421287 utawa luwih ora dipindai amarga pesenan saka tombol indeks exp. Iki minangka tabungan sing dikarepake ing wiwitan.

Ayo mungkasi tugas:

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

РСализация

Cara paling apik kanggo nyritakake babagan kabeh fitur proyek yaiku sumber asline. kode! Minangka bagΓ©an saka publikasi, kita bakal fokus mung ing TCTerms paling penting, yaiku, algoritma spasi bypass.

Argumentasi sing diterusake menyang metode wiwitan disimpen ing struktur sing diarani 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;
};

Atribut jeneng yaiku jeneng tugas. Atribut space_id minangka pengenal spasi. Atribut rm_index_id minangka pengenal indeks unik sing tuple bakal dibusak. Atribut it_index_id minangka pengenal indeks sing bakal dilewati tuple. Atribut it_index_type yaiku jinis indeks sing bakal dilewati tuple. Atribut filed_no yaiku nomer kolom tuple kanthi umur. Atribut scan_size yaiku jumlah maksimum tuple sing dipindai ing siji transaksi. Atribut scan_time yaiku wektu scan lengkap ing detik.

Kita ora bakal nimbang argumen parsing. Iki minangka proyek sing angel nanging prasaja, sing bakal mbantu sampeyan perpustakaan msgpuck. Kangelan mung bisa njedhul karo indeks sing liwati saka Lua minangka struktur data Komplek karo jinis mp_map, lan ora nggunakake jinis prasaja mp_bool, mp_double, mp_int, mp_uint lan mp_array. Nanging ora perlu ngurai kabeh indeks. Sampeyan mung kudu mriksa keunikane, ngetung jinis lan ekstrak pengenal.

Kita dhaptar prototipe kabeh fungsi sing digunakake kanggo parsing:

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

Saiki ayo pindhah menyang sing paling penting - logika ngliwati spasi lan mbusak tuple. Saben blok tuple ora luwih gedhe tinimbang scan_size dipindai lan diowahi ing siji transaksi. Yen kasil, transaksi iki dileksanakake; yen ana kesalahan, digulung maneh. Argumentasi pungkasan kanggo fungsi expirationd_iterate minangka pointer menyang iterator saka ngendi pemindaian diwiwiti utawa diterusake. iterator iki incremented internal nganti ana kesalahan, spasi entek, utawa ora bisa kanggo mungkasi proses ing advance. Fungsi expirationd_expired mriksa umur tuple, expirationd_delete mbusak tuple, expirationd_breakable mriksa apa kita kudu nerusake.

Kode fungsi 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;
}

Kode fungsi 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;
}

Kode fungsi 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);
}

Kode fungsi 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;
}

Aplikasi

Sampeyan bisa ndeleng kode sumber ing kene!

Source: www.habr.com

Add a comment