Noong nakaraan, nahaharap tayo sa problema sa paglilinis ng mga tuple sa mga espasyo
Isang magandang halimbawa para sa amin ang tinatawag na tarantool module
ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅
Ang dokumentasyon para sa tarantool ay may napakahusay
Magsimula tayo sa malayo at tingnan kung ano ang hitsura ng naka-cap na expiration na module mula sa labas:
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)
Para sa pagiging simple, inilunsad namin ang tarantool sa direktoryo kung saan matatagpuan ang aming libcapped-expirationd.so library. Dalawang function ang na-export mula sa library: simulan at patayin. Ang unang hakbang ay gawing available ang mga function na ito mula sa Lua gamit ang box.schema.func.create at box.schema.user.grant. Pagkatapos ay lumikha ng isang puwang na ang mga tuple ay maglalaman lamang ng tatlong mga field: ang una ay isang natatanging identifier, ang pangalawa ay ang email, at ang pangatlo ay ang haba ng buhay ng tuple. Bumubuo kami ng tree index sa tuktok ng unang field at tinatawag itong pangunahin. Susunod na makuha namin ang object ng koneksyon sa aming katutubong library.
Pagkatapos ng gawaing paghahanda, patakbuhin ang start function:
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})
Ang halimbawang ito ay gagana sa panahon ng pag-scan na eksaktong kapareho ng nag-expire na module, na nakasulat sa Lua. Ang unang argumento sa start function ay ang natatanging pangalan ng gawain. Ang pangalawa ay ang space identifier. Ang pangatlo ay isang natatanging index kung saan ang mga tuple ay tatanggalin. Ang ikaapat ay ang index kung saan ang mga tuple ay dadaanan. Ang panglima ay ang bilang ng field ng tuple na may panghabambuhay (nagsisimula ang pagnunumero sa 1, hindi 0!). Ang ikaanim at ikapito ay mga setting ng pag-scan. Ang 1024 ay ang maximum na bilang ng mga tuple na maaaring matingnan sa isang transaksyon. 3600 β buong oras ng pag-scan sa mga segundo.
Tandaan na ang halimbawa ay gumagamit ng parehong index para sa pag-crawl at pagtanggal. Kung ito ay isang index ng puno, kung gayon ang traversal ay isinasagawa mula sa mas maliit na susi hanggang sa mas malaki. Kung mayroong ilang iba pa, halimbawa, hash index, pagkatapos ay ang traversal ay isinasagawa, bilang isang panuntunan, sa random na pagkakasunud-sunod. Ang lahat ng space tuple ay ini-scan sa isang pag-scan.
Magpasok tayo ng ilang tuple sa espasyo na may habang buhay na 60 segundo:
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}
Suriin natin kung matagumpay ang pagpasok:
tarantool> box.space.tester.index.primary:select()
---
- - [0, '[email protected]', 1576418976]
- [1, '[email protected]', 1576418976]
- [2, '[email protected]', 1576418976]
...
Ulitin natin ang piliin pagkatapos ng 60+ segundo (nagbibilang mula sa simula ng pagpasok ng unang tuple) at tingnan na ang naka-cap na expiration na module ay naproseso na:
tarantool> box.space.tester.index.primary:select()
---
- []
...
Itigil natin ang gawain:
capped_connection:call('libcapped-expirationd.kill', {'non-indexed'})
Tingnan natin ang pangalawang halimbawa kung saan ginagamit ang isang hiwalay na index para sa pag-crawl:
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)
Ang lahat dito ay kapareho ng sa unang halimbawa, na may ilang mga pagbubukod. Bumubuo kami ng tree index sa ibabaw ng ikatlong field at tinatawag itong exp. Ang index na ito ay hindi kailangang maging natatangi, hindi katulad ng index na tinatawag na pangunahin. Ang traversal ay isasagawa sa pamamagitan ng exp index, at pagtanggal sa pamamagitan ng primary. Natatandaan namin na dati pareho ay ginawa lamang gamit ang pangunahing index.
Pagkatapos ng gawaing paghahanda, pinapatakbo namin ang start function na may mga bagong argumento:
capped_connection:call('libcapped-expirationd.start', {'indexed', box.space.tester.id, box.space.tester.index.primary, box.space.tester.index.exp, 3, 1024, 3600})
Magpasok muli tayo ng ilang tuple sa espasyo na may habang buhay na 60 segundo:
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}
Pagkatapos ng 30 segundo, sa pamamagitan ng pagkakatulad, magdaragdag kami ng ilan pang tuple:
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}
Suriin natin kung matagumpay ang pagpasok:
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]
...
Ulitin natin ang piliin pagkatapos ng 60+ segundo (nagbibilang mula sa simula ng pagpasok ng unang tuple) at tingnan na ang naka-cap na expiration na module ay naproseso na:
tarantool> box.space.tester.index.primary:select()
---
- - [3, '[email protected]', 1576421287]
- [4, '[email protected]', 1576421287]
- [5, '[email protected]', 1576421287]
...
Mayroon pa ring ilang tuple na natitira sa espasyo na magkakaroon ng humigit-kumulang 30 segundo upang mabuhay. Bukod dito, huminto ang pag-scan nang lumipat mula sa isang tuple na may ID na 2 at panghabambuhay na 1576421257 patungo sa isang tuple na may ID na 3 at panghabambuhay na 1576421287. Ang mga tuple na may habang-buhay na 1576421287 o higit pa ay hindi na-scan dahil sa pag-order ng ang mga exp index key. Ito ang savings na gusto nating makamit sa simula pa lang.
Itigil natin ang gawain:
capped_connection:call('libcapped-expirationd.kill', {'indexed'})
Pagpapatupad
Ang pinakamahusay na paraan upang sabihin ang tungkol sa lahat ng mga tampok ng isang proyekto ay ang orihinal na pinagmulan nito.
Ang mga argumento na ipinapasa namin sa paraan ng pagsisimula ay naka-imbak sa isang istraktura na tinatawag na 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;
};
Ang katangian ng pangalan ay ang pangalan ng gawain. Ang space_id attribute ay ang space identifier. Ang attribute na rm_index_id ay ang identifier ng natatanging index kung saan matatanggal ang mga tuple. Ang attribute na it_index_id ay ang identifier ng index kung saan dadaanan ang mga tuple. Ang attribute na it_index_type ay ang uri ng index kung saan dadaanan ang mga tuple. Ang filed_no attribute ay ang bilang ng tuple field na may panghabambuhay. Ang katangiang scan_size ay ang maximum na bilang ng mga tuple na na-scan sa isang transaksyon. Ang katangian ng scan_time ay ang buong oras ng pag-scan sa mga segundo.
Hindi namin isasaalang-alang ang pag-parse ng mga argumento. Ito ay isang maingat ngunit simpleng trabaho, kung saan ang library ay makakatulong sa iyo
Inilista namin ang mga prototype ng lahat ng mga function na ginagamit para sa pag-parse:
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);
Ngayon ay lumipat tayo sa pinakamahalagang bagay - ang lohika ng pag-bypass sa espasyo at pagtanggal ng mga tuple. Ang bawat bloke ng tuple na hindi hihigit sa scan_size ay ini-scan at binago sa ilalim ng iisang transaksyon. Kung matagumpay, gagawin ang transaksyong ito; kung may nangyaring error, ibabalik ito. Ang huling argumento sa expirationd_iterate function ay isang pointer sa iterator kung saan magsisimula o magpapatuloy ang pag-scan. Ang iterator na ito ay dinadagdagan sa loob hanggang sa magkaroon ng error, maubos ang espasyo, o hindi posibleng ihinto ang proseso nang maaga. Sinusuri ng function na expirationd_expired ang buhay ng isang tuple, tinatanggal ng expirationd_delete ang isang tuple, sinusuri ng expirationd_breakable kung kailangan nating magpatuloy.
Expirationd_iterate function code:
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;
}
Function code 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 function code:
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);
}
Function code 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;
}
App
Maaari mong tingnan ang source code sa
Pinagmulan: www.habr.com