'n Tyd gelede het ons gekonfronteer met die probleem om tupels in spasies skoon te maak
'n Goeie voorbeeld vir ons was die tarantool-module genaamd
Beskrywing
Die dokumentasie vir tarantool het 'n baie goeie
Kom ons begin van ver af en kyk hoe 'n module met 'n afgeslote vervaldatum van buite af lyk:
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)
Vir eenvoud begin ons tarantool in die gids waar ons libcapped-expirationd.so-biblioteek geleΓ« is. Twee funksies word vanaf die biblioteek uitgevoer: begin en doodmaak. Die eerste stap is om hierdie funksies vanaf Lua beskikbaar te stel deur box.schema.func.create en box.schema.user.grant te gebruik. Skep dan 'n spasie waarvan die tupels slegs drie velde sal bevat: die eerste is 'n unieke identifiseerder, die tweede is e-pos, en die derde is die leeftyd van die tupel. Ons bou 'n boomindeks bo-op die eerste veld en noem dit primΓͺr. Volgende kry ons die verbindingsvoorwerp na ons inheemse biblioteek.
Na die voorbereidende werk, voer die beginfunksie uit:
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})
Hierdie voorbeeld sal tydens skandering presies dieselfde werk as die module wat verval het, wat in Lua geskryf is. Die eerste argument vir die beginfunksie is die unieke naam van die taak. Die tweede is die spasie-identifiseerder. Die derde is 'n unieke indeks waardeur tupels uitgevee sal word. Die vierde is die indeks waardeur die tupels deurkruis sal word. Die vyfde is die nommer van die tupelveld met leeftyd (nommering begin by 1, nie 0 nie!). Die sesde en sewende is skandeerinstellings. 1024 is die maksimum aantal tupels wat in 'n enkele transaksie besigtig kan word. 3600 β volle skanderingstyd in sekondes.
Let daarop dat die voorbeeld dieselfde indeks gebruik vir deurkruip en uitvee. As dit 'n boomindeks is, word die deurkruising van die kleiner sleutel na die groter een uitgevoer. As daar 'n ander, byvoorbeeld, hash-indeks is, word die deurkruising, as 'n reΓ«l, in ewekansige volgorde uitgevoer. Alle spasie-tupels word in een skandering geskandeer.
Kom ons plaas verskeie tupels in die spasie met 'n leeftyd van 60 sekondes:
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}
Kom ons kyk of die invoeging suksesvol was:
tarantool> box.space.tester.index.primary:select()
---
- - [0, '[email protected]', 1576418976]
- [1, '[email protected]', 1576418976]
- [2, '[email protected]', 1576418976]
...
Kom ons herhaal die keuse na 60+ sekondes (tel vanaf die begin van die invoeging van die eerste tupel) en sien dat die beperkte verstrykte module reeds verwerk is:
tarantool> box.space.tester.index.primary:select()
---
- []
...
Kom ons stop die taak:
capped_connection:call('libcapped-expirationd.kill', {'non-indexed'})
Kom ons kyk na 'n tweede voorbeeld waar 'n aparte indeks vir die kruip gebruik word:
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)
Alles hier is dieselfde as in die eerste voorbeeld, met 'n paar uitsonderings. Ons bou 'n boomindeks bo-op die derde veld en noem dit exp. Hierdie indeks hoef nie uniek te wees nie, anders as die indeks wat primΓͺr genoem word. Traversering sal uitgevoer word deur exp-indeks, en verwydering deur primΓͺre. Ons onthou dat albei voorheen slegs met die primΓͺre indeks gedoen is.
Na die voorbereidende werk, loop ons die beginfunksie met nuwe argumente:
capped_connection:call('libcapped-expirationd.start', {'indexed', box.space.tester.id, box.space.tester.index.primary, box.space.tester.index.exp, 3, 1024, 3600})
Kom ons plaas weer verskeie tupels in die spasie met 'n leeftyd van 60 sekondes:
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}
Na 30 sekondes, na analogie, sal ons nog 'n paar tupels byvoeg:
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}
Kom ons kyk of die invoeging suksesvol was:
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]
...
Kom ons herhaal die keuse na 60+ sekondes (tel vanaf die begin van die invoeging van die eerste tupel) en sien dat die beperkte verstrykte module reeds verwerk is:
tarantool> box.space.tester.index.primary:select()
---
- - [3, '[email protected]', 1576421287]
- [4, '[email protected]', 1576421287]
- [5, '[email protected]', 1576421287]
...
Daar is nog 'n paar tupels oor in die spasie wat nog sowat 30 sekondes sal hΓͺ om te lewe. Boonop het die skandering gestop toe daar van 'n tupel met 'n ID van 2 en 'n leeftyd van 1576421257 na 'n tupel met 'n ID van 3 en 'n leeftyd van 1576421287 beweeg is. Tupels met 'n leeftyd van 1576421287 of meer is nie geskandeer nie as gevolg van die bestelling van die exp indeks sleutels. Dit is die besparings wat ons heel aan die begin wou behaal.
Kom ons stop die taak:
capped_connection:call('libcapped-expirationd.kill', {'indexed'})
Implementering
Die beste manier om oor al die kenmerke van 'n projek te vertel, is die oorspronklike bron daarvan.
Die argumente wat ons na die beginmetode deurgee word gestoor in 'n struktuur genaamd 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;
};
Die naam-kenmerk is die naam van die taak. Die space_id kenmerk is die spasie identifiseerder. Die rm_index_id kenmerk is die identifiseerder van die unieke indeks waardeur tuples uitgevee sal word. Die it_index_id kenmerk is die identifiseerder van die indeks waardeur tupels deurkruis sal word. Die it_index_type kenmerk is die tipe indeks waardeur tupels deurkruis sal word. Die filed_no-kenmerk is die nommer van die tuple-veld met leeftyd. Die scan_size-kenmerk is die maksimum aantal tupels wat in een transaksie geskandeer word. Die scan_time-kenmerk is die volle skanderingstyd in sekondes.
Ons sal nie die ontleed van argumente oorweeg nie. Dit is 'n moeisame maar eenvoudige werk waarmee die biblioteek jou sal help
Ons lys die prototipes van alle funksies wat vir ontleding gebruik word:
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);
Kom ons gaan nou oor na die belangrikste ding - die logika om spasie te omseil en tupels uit te vee. Elke blok tupels wat nie groter is as scan_size nie, word onder 'n enkele transaksie geskandeer en gewysig. As dit suksesvol is, word hierdie transaksie gepleeg; as fout voorkom, word dit teruggerol. Die laaste argument vir die expirationd_iterate-funksie is 'n wyser na die iterator vanwaar skandering begin of voortgaan. Hierdie iterator word intern verhoog totdat 'n fout voorkom, die spasie opraak of dit nie moontlik is om die proses vooraf te stop nie. Die funksie expirationd_expired kontroleer die leeftyd van 'n tupel, expirationd_delete verwyder 'n tupel, expirationd_breakable kontroleer of ons moet aanbeweeg.
Expirationd_iterate funksie kode:
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;
}
Funksie kode 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 funksie kode:
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);
}
Funksie kode 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;
}
Artikels
U kan die bronkode sien by
Bron: will.com