Ekri pwòp modil ekspirasyon plafon nou an pou tarantool

Ekri pwòp modil ekspirasyon plafon nou an pou tarantool

Kèk tan de sa nou te fè fas ak pwoblèm nan netwaye tuples nan espas yo tarantool. Netwayaj te dwe kòmanse pa lè tarantool te deja kouri soti nan memwa, men davans ak nan yon frekans sèten. Pou travay sa a, tarantool gen yon modil ekri nan Lua rele ekspirasyon. Apre w fin itilize modil sa a pou yon ti tan, nou reyalize ke li pa apwopriye pou nou: akòz netwayaj konstan nan gwo kantite done, Lua pandye nan GC la. Se poutèt sa, nou te panse sou devlope pwòp modil ekspirasyon limit nou an, espere ke kòd ki ekri nan yon lang natif natal ta rezoud pwoblèm nou yo nan pi bon fason posib.

Yon bon egzanp pou nou se te modil tarantool yo rele memcached. Apwòch la itilize nan li baze sou lefèt ke yo kreye yon jaden separe nan espas ki la, ki endike tout lavi a nan tuple la, nan lòt mo, ttl. Modil la nan background nan analize espas la, konpare TTL a ak tan aktyèl la epi deside si yo efase tuple la oswa ou pa. Kòd modil memcached la senp ak elegant, men li twò jenerik. Premyèman, li pa pran an kont ki kalite endèks ke yo te ranpe ak efase. Dezyèmman, sou chak pas tout tuple yo analize, ki kantite yo ka byen gwo. Men, si nan modil ekspirasyon an premye pwoblèm nan te rezoud (endèks pye bwa a te separe nan yon klas separe), Lè sa a, dezyèm lan toujou pa t 'resevwa atansyon. Twa pwen sa yo te detèmine chwa a an favè ekri pwòp kòd mwen an.

Deskripsyon

Dokiman an pou tarantool gen yon trè bon leson patikilye sou kòman yo ekri pwosedi ki estoke ou nan C. Premye a tout, mwen sijere ou familyarize w avèk li yo nan lòd yo konprann sa yo foure ak kòmandman ak kòd ki pral parèt anba a. Li se tou vo peye atansyon a referans nan objè ki disponib lè w ap ekri pwòp modil plafon ou a, sètadi bwat, fib, endèks и txn.

Ann kòmanse byen lwen epi gade nan kisa yon modil ekspirasyon ki gen limit sanble soti deyò:

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)

Pou senplisite, nou lanse tarantool nan anyè kote bibliyotèk libcapped-expirationd.so nou ye. De fonksyon yo ekspòte soti nan bibliyotèk la: kòmanse ak touye. Premye etap la se fè fonksyon sa yo disponib nan Lua lè l sèvi avèk box.schema.func.create ak box.schema.user.grant. Lè sa a, kreye yon espas ki gen tuples pral genyen sèlman twa jaden: premye a se yon idantifyan inik, dezyèm lan se imèl, ak twazyèm lan se lavi a nan tuple la. Nou bati yon endèks pye bwa sou tèt premye jaden an epi rele li prensipal. Apre sa, nou jwenn objè a koneksyon nan bibliyotèk natif natal nou an.

Apre travay preparasyon an, kouri fonksyon an kòmanse:

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

Egzanp sa a pral travay pandan optik egzakteman menm jan ak modil la ekspirasyon, ki ekri nan Lua. Premye agiman nan fonksyon an kòmanse se non an inik nan travay la. Dezyèm lan se idantifyan espas. Twazyèm lan se yon endèks inik kote tuple yo pral efase. Katriyèm lan se endèks kote tuple yo pral travèse. Senkyèm lan se nimewo a nan jaden an tuple ak tout lavi (nimero kòmanse soti nan 1, pa 0!). Sizyèm ak setyèm yo se anviwònman optik. 1024 se kantite maksimòm tuple ki ka wè nan yon sèl tranzaksyon. 3600 - tan eskanè konplè an segonn.

Remake byen ke egzanp lan sèvi ak endèks la menm pou rale ak efase. Si sa a se yon endèks pye bwa, Lè sa a, travèse a te pote soti nan kle ki pi piti a nan youn nan pi gwo. Si gen kèk lòt, pou egzanp, hash endèks, Lè sa a, travèse a te pote soti, kòm yon règ, nan lòd o aza. Tout tuple espas yo analize nan yon sèl eskanè.

Ann mete plizyè tuple nan espas la ak yon lavi 60 segonn:

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}

Ann tcheke si ensèsyon an te reyisi:

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

Ann repete seleksyon an apre plis pase 60 segonn (konte depi nan konmansman an nan ensèsyon an nan premye tuple la) epi wè ke modil la ekspirasyon limit la te deja trete:

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

Ann sispann travay la:

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

Ann gade yon dezyèm egzanp kote yo itilize yon endèks separe pou rale a:

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)

Tout bagay isit la se menm jan ak nan premye egzanp lan, ak kèk eksepsyon. Nou bati yon endèks pye bwa sou tèt twazyèm jaden an epi rele li exp. Endèks sa a pa dwe inik, kontrèman ak endèks yo rele prensipal la. Traversal pral fèt pa endèks eksp, ak sipresyon pa prensipal. Nou sonje ke deja tou de te fè sèlman lè l sèvi avèk endèks prensipal la.

Apre travay preparasyon an, nou kouri fonksyon kòmanse ak nouvo agiman:

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

Ann mete plizyè tuple nan espas la ankò ak yon lavi 60 segonn:

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}

Apre 30 segonn, pa analoji, nou pral ajoute kèk plis tuples:

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}

Ann tcheke si ensèsyon an te reyisi:

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

Ann repete seleksyon an apre plis pase 60 segonn (konte depi nan konmansman an nan ensèsyon an nan premye tuple la) epi wè ke modil la ekspirasyon limit la te deja trete:

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

Genyen toujou kèk tuple ki rete nan espas ki pral gen apeprè 30 segonn plis pou viv. Anplis, eskanè a sispann lè w ap deplase soti nan yon tuple ki gen yon ID 2 ak yon lavi 1576421257 nan yon tuple ki gen yon ID 3 ak yon lavi 1576421287. kle exp endèks yo. Sa a se ekonomi yo ke nou te vle reyalize nan kòmansman an anpil.

Ann sispann travay la:

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

Aplikasyon

Pi bon fason pou di tout karakteristik yon pwojè se sous orijinal li. kòd! Kòm yon pati nan piblikasyon an, nou pral konsantre sèlman sou pwen ki pi enpòtan yo, sètadi, algoritm espas kontoune.

Agiman yo nou pase nan metòd la kòmanse yo estoke nan yon estrikti ki rele 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;
};

Non atribi a se non travay la. Atribi space_id se idantifyan espas. Atribi rm_index_id la se idantifyan endèks inik kote tuple yo pral efase. Atribi it_index_id la se idantifyan endèks kote tuple yo pral travèse. Atribi it_index_type a se kalite endèks kote tuple yo pral travèse. Atribi filed_no se nimewo jaden tuple ak tout lavi. Atribi scan_size la se kantite maksimòm tuple ke yo analize nan yon sèl tranzaksyon. Atribi scan_time se tan eskanè konplè an segonn.

Nou p ap konsidere analize agiman yo. Sa a se yon travay rigoureux men senp, ak ki bibliyotèk la pral ede w msgpuck. Difikilte yo ka parèt sèlman ak endèks ki pase nan Lua kòm yon estrikti done konplèks ak kalite mp_map, epi yo pa itilize kalite ki senp mp_bool, mp_double, mp_int, mp_uint ak mp_array. Men, pa gen okenn nesesite analize endèks la tout antye. Ou jis bezwen tcheke singularité li yo, kalkile kalite a ak ekstrè idantifyan an.

Nou lis pwototip tout fonksyon yo itilize pou analiz:

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

Koulye a, kite a deplase sou bagay ki pi enpòtan - lojik la nan kontourne espas ak efase tuples. Chak blòk tuple ki pa pi gwo pase scan_size analize epi modifye anba yon sèl tranzaksyon. Si yo gen siksè, tranzaksyon sa a komèt; si erè rive, li anile. Dènye agiman an nan fonksyon expirationd_iterate la se yon konsèy sou iteratè ki soti nan ki optik kòmanse oswa kontinye. Iteratè sa a ogmante anndan jiskaske yon erè rive, espas la fini, oswa li pa posib pou sispann pwosesis la davans. Fonksyon expirationd_expired tcheke tout lavi yon tuple, expirationd_delete efase yon tuple, expirationd_breakable tcheke si nou bezwen avanse.

Expirationd_iterate kòd fonksyon:

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

Kòd fonksyon 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;
}

Kòd fonksyon 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);
}

Kòd fonksyon 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;
}

Aplikasyon

Ou ka wè kòd sous la nan isit la!

Sous: www.habr.com

Add nouvo kòmantè