Nulis modul kadaluwarsa capped urang sorangan pikeun tarantool

Nulis modul kadaluwarsa capped urang sorangan pikeun tarantool

Sababaraha waktu ka tukang urang nyanghareupan masalah beberesih tuples dina spasi tarantool. Beberesih kedah dimimitian sanés nalika tarantool parantos kaluar tina mémori, tapi sateuacanna sareng dina frékuénsi anu tangtu. Pikeun tugas ieu, tarantool boga modul ditulis dina Lua disebut béakna. Saatos nganggo modul ieu kanggo waktos anu singget, urang sadar yén éta henteu cocog pikeun urang: kusabab beberesih konstan tina jumlah data anu ageung, Lua ngagantung dina GC. Ku alatan éta, urang mikir ngeunaan ngamekarkeun modul kadaluwarsa capped urang sorangan, hoping yén kode ditulis dina basa programming pituin bakal ngajawab masalah urang dina cara pangalusna mungkin.

Hiji conto alus keur urang éta modul tarantool disebut memcached. Pendekatan anu digunakeun dina éta dumasar kana kanyataan yén widang anu misah didamel dina rohangan, anu nunjukkeun umur tuple, dina basa sanés, ttl. Modul dina latar tukang nyeken rohangan, ngabandingkeun TTL sareng waktos ayeuna sareng mutuskeun pikeun ngahapus tuple atanapi henteu. Kode modul memcached basajan tur elegan, tapi teuing generik. Kahiji, éta henteu tumut kana akun jenis indéks nu keur crawled tur dihapus. Bréh, dina unggal pass sagala tuples discan, jumlah nu bisa jadi rada badag. Sareng upami dina modul kadaluwarsa masalah kahiji direngsekeun (indéks tangkal dipisahkeun kana kelas anu misah), maka anu kadua tetep henteu nampi perhatian. Tilu titik ieu predetermined pilihan dina kahadean nulis kode kuring sorangan.

gambaran

Dokuméntasi pikeun tarantool ngabogaan pohara alus tutorial ngeunaan kumaha carana nulis prosedur disimpen anjeun dina C. Munggaran sadaya, abdi nyarankeun Anjeun familiarize diri kalawan eta dina urutan ngartos eta inserts kalawan paréntah jeung kode anu bakal muncul di handap. Éta ogé patut nengetan rujukan ka objék nu sadia nalika nulis modul capped sorangan, nyaéta kotak, serat, daptar eusi и txn.

Hayu urang mimitian ti kajauhan sareng tingali kumaha modél kadaluwarsa anu ditutup ti luar:

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)

Pikeun kesederhanaan, urang ngaluncurkeun tarantool dina diréktori tempat perpustakaan libcapped-expirationd.so urang aya. Dua fungsi anu diékspor ti perpustakaan: ngamimitian jeung maéhan. Hambalan munggaran nyaéta sangkan pungsi ieu sadia ti Lua maké box.schema.func.create jeung box.schema.user.grant. Teras jieun rohangan anu tuplena ngan ukur ngandung tilu widang: anu kahiji nyaéta identifier unik, anu kadua nyaéta email, sareng anu katilu nyaéta umur tuple. Urang ngawangun hiji indéks tangkal dina luhureun widang kahiji jeung nelepon deui primér. Salajengna urang meunang obyék sambungan kana perpustakaan asli urang.

Saatos damel persiapan, jalankeun fungsi ngamimitian:

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 ieu tiasa dianggo nalika nyeken persis sami sareng modul kadaluwarsa, anu diserat dina Lua. Argumen munggaran pikeun fungsi ngamimitian nyaéta nami unik tugas. Nu kadua nyaéta identifier spasi. Anu katilu nyaéta indéks unik dimana tuples bakal dihapus. Kaopat nyaéta indéks dimana tuple bakal dijalankeun. Kalima nyaeta jumlah widang tuple kalawan hirupna (panomeran dimimitian ti 1, teu 0!). Kagenep sareng katujuh nyaéta setélan scanning. 1024 mangrupikeun jumlah maksimal tuple anu tiasa ditingali dina hiji transaksi. 3600 - waktos scan pinuh dina detik.

Catet yén conto nganggo indéks anu sami pikeun ngorondang sareng ngahapus. Upami ieu mangrupikeun indéks tangkal, maka traversal dilaksanakeun tina konci anu langkung alit ka anu langkung ageung. Lamun aya sababaraha séjén, contona, indéks Hash, traversal dilumangsungkeun, sakumaha aturan, dina urutan acak. Kabéh tuples spasi discan dina hiji scan.

Hayu urang selapkeun sababaraha tupel kana rohangan kalayan 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}

Hayu urang pariksa yén sisipan éta suksés:

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

Hayu urang malikan pilihan saatos 60+ detik (cacah ti mimiti nyelapkeun tuple munggaran) sareng tingali yén modul kadaluwarsa anu ditutupan parantos diolah:

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

Hayu urang ngeureunkeun tugas:

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

Hayu urang tingali conto kadua dimana indéks anu misah dianggo pikeun ngorondang:

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)

Sadayana di dieu sami sareng dina conto anu munggaran, sareng sababaraha pengecualian. Urang ngawangun hiji indéks tangkal dina luhureun widang katilu jeung nelepon deui exp. Indéks ieu henteu kedah unik, teu sapertos indéks anu disebut primér. Traversal bakal dilaksanakeun ku indéks exp, sarta ngahapus ku primér. Urang inget yen saméméhna duanana dipigawé ngan ngagunakeun indéks primér.

Saatos karya préparasi, urang ngajalankeun fungsi ngamimitian kalawan 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})

Hayu urang selapkeun sababaraha tupel kana rohangan deui kalayan 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}

Saatos 30 detik, ku analogi, urang bakal nambihan sababaraha tuple deui:

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}

Hayu urang pariksa yén sisipan éta suksés:

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

Hayu urang malikan pilihan saatos 60+ detik (cacah ti mimiti nyelapkeun tuple munggaran) sareng tingali yén modul kadaluwarsa anu ditutupan parantos diolah:

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

Aya kénéh sababaraha tuples ditinggalkeun di spasi nu bakal boga ngeunaan 30 detik deui hirup. Leuwih ti éta, scan dieureunkeun nalika pindah ti tuple kalayan ID 2 sareng umurna 1576421257 ka tuple kalayan ID 3 sareng umur 1576421287. Tuple kalayan umur 1576421287 atanapi langkung henteu diseken kusabab pesenan konci indéks exp. Ieu mangrupikeun tabungan anu urang hoyong ngahontal dina awalna.

Hayu urang ngeureunkeun tugas:

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

Реализация

Cara anu pangsaéna pikeun nyarioskeun ngeunaan sadaya fitur proyék nyaéta sumber asli na. di! Salaku bagian tina publikasi, urang bakal museurkeun ukur dina titik pangpentingna, nyaéta, algoritma spasi bypass.

Argumen anu urang lebet kana metode ngamimitian disimpen dina struktur anu disebut 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 nami mangrupikeun nami tugas. Atribut space_id nyaeta identifier spasi. Atribut rm_index_id nyaéta identifier tina indéks unik nu tuples bakal dihapus. Atribut it_index_id teh identifier tina indéks nu tuples bakal traversed. Atribut it_index_type nyaéta jinis indéks dimana tuple bakal dijalankeun. Atribut filed_no nyaéta jumlah kolom tuple kalayan umurna. Atribut scan_size nyaeta jumlah maksimum tuples nu discan dina hiji transaksi. Atribut scan_time nyaéta waktos scan pinuh dina detik.

Kami moal nganggap parsing argumen. Ieu mangrupikeun padamelan anu susah tapi saderhana, dimana perpustakaan bakal ngabantosan anjeun msgpuck. Kasusah ngan bisa timbul jeung indéks nu diliwatan ti Lua salaku struktur data kompléks jeung tipe mp_map, sarta teu ngagunakeun tipe basajan mp_bool, mp_double, mp_int, mp_uint jeung mp_array. Tapi aya teu kudu parse sakabéh indéks. Anjeun ngan perlu pariksa uniqueness na, ngitung jenis na nimba identifier nu.

Kami daptar prototipe sadaya fungsi anu dianggo pikeun 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);

Ayeuna hayu urang ngalih ka hal pangpentingna - logika bypassing spasi tur mupus tuples. Unggal blok tuple henteu langkung ageung tibatan scan_size diseken sareng dirobih dina hiji transaksi. Upami suksés, transaksi ieu dilakukeun; upami aya kasalahan, éta digulung deui. Argumen panungtungan pikeun fungsi expirationd_iterate mangrupakeun pointer ka iterator ti mana scanning dimimitian atawa dituluykeun. iterator ieu incremented internal nepi ka lumangsung kasalahan, spasi béak, atawa teu mungkin pikeun ngeureunkeun prosés sateuacanna. Fungsi expirationd_expired pariksa hirupna tuple a, expirationd_delete mupus tuple a, expirationd_breakable cék naha urang kudu ngaléngkah.

Kodeu 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 expiration_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

Anjeun tiasa ningali kode sumber di di dieu!

sumber: www.habr.com

Tambahkeun komentar