Bir müddət əvvəl boşluqlarda tuplelərin təmizlənməsi problemi ilə qarşılaşdıq
Bizim üçün yaxşı bir nümunə adlanan tarantool modulu idi
Təsvir
Tarantool üçün sənədlər çox yaxşıdır
Uzaqdan başlayaq və müddəti bitmiş modulun kənardan necə göründüyünə baxaq:
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)
Sadəlik üçün libcapped-expirationd.so kitabxanamızın yerləşdiyi kataloqda tarantool-u işə salırıq. Kitabxanadan iki funksiya ixrac olunur: başlamaq və öldürmək. İlk addım box.schema.func.create və box.schema.user.grant istifadə edərək bu funksiyaları Lua-dan əlçatan etməkdir. Sonra dəstləri yalnız üç sahəni ehtiva edəcək bir boşluq yaradın: birincisi unikal identifikator, ikincisi e-poçt, üçüncüsü isə tuplenin ömrüdür. Birinci sahənin üstündə bir ağac indeksi qururuq və onu əsas adlandırırıq. Sonra doğma kitabxanamızla əlaqə obyektini alırıq.
Hazırlıq işindən sonra başlanğıc funksiyasını işə salın:
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})
Bu nümunə skan zamanı Lua dilində yazılmış müddəti bitmiş modulla eyni şəkildə işləyəcək. Başlanğıc funksiyasının ilk arqumenti tapşırığın unikal adıdır. İkincisi boşluq identifikatorudur. Üçüncüsü, tuplelərin silinəcəyi unikal indeksdir. Dördüncüsü, çubuqların keçiləcəyi indeksdir. Beşincisi, ömür boyu olan tuple sahəsinin nömrəsidir (nömrələmə 1-dan deyil, 0-dən başlayır!). Altıncı və yeddinci skan parametrləridir. 1024, bir əməliyyatda baxıla bilən dələlərin maksimum sayıdır. 3600 — saniyələrdə tam skan müddəti.
Qeyd edək ki, nümunə tarama və silmək üçün eyni indeksdən istifadə edir. Bu ağac indeksidirsə, keçid kiçik açardan böyüyə doğru aparılır. Əgər başqa, məsələn, hash indeksi varsa, o zaman keçid, bir qayda olaraq, təsadüfi qaydada həyata keçirilir. Bütün boşluq dəstləri bir skanda skan edilir.
Ömrü 60 saniyə olan fəzaya bir neçə dəst daxil edək:
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}
Daxiletmənin uğurlu olub olmadığını yoxlayaq:
tarantool> box.space.tester.index.primary:select()
---
- - [0, '[email protected]', 1576418976]
- [1, '[email protected]', 1576418976]
- [2, '[email protected]', 1576418976]
...
Gəlin seçimi 60+ saniyədən sonra təkrarlayaq (birinci dəftərin daxil edilməsinin əvvəlindən hesablanır) və bitmiş modulun artıq işləndiyini görək:
tarantool> box.space.tester.index.primary:select()
---
- []
...
Tapşırığı dayandıraq:
capped_connection:call('libcapped-expirationd.kill', {'non-indexed'})
Tarama üçün ayrıca indeksin istifadə olunduğu ikinci nümunəyə baxaq:
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)
Burada hər şey bir neçə istisna olmaqla, birinci nümunədəki kimidir. Üçüncü sahənin üstündə ağac indeksi qururuq və onu exp adlandırırıq. Bu indeks ilkin adlanan indeksdən fərqli olaraq unikal olmalıdır. Keçmə eks indeksi ilə, silinmə isə birincil tərəfindən həyata keçiriləcək. Xatırlayırıq ki, əvvəllər hər ikisi yalnız ilkin indeksdən istifadə edilirdi.
Hazırlıq işindən sonra başlanğıc funksiyasını yeni arqumentlərlə işlədirik:
capped_connection:call('libcapped-expirationd.start', {'indexed', box.space.tester.id, box.space.tester.index.primary, box.space.tester.index.exp, 3, 1024, 3600})
Gəlin, 60 saniyəlik bir ömür ilə yenidən boşluğa bir neçə dəst daxil edək:
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}
30 saniyədən sonra bənzətmə ilə daha bir neçə dəst əlavə edəcəyik:
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}
Daxiletmənin uğurlu olub olmadığını yoxlayaq:
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]
...
Gəlin seçimi 60+ saniyədən sonra təkrarlayaq (birinci dəftərin daxil edilməsinin əvvəlindən hesablanır) və bitmiş modulun artıq işləndiyini görək:
tarantool> box.space.tester.index.primary:select()
---
- - [3, '[email protected]', 1576421287]
- [4, '[email protected]', 1576421287]
- [5, '[email protected]', 1576421287]
...
Kosmosda hələ də yaşamaq üçün təxminən 30 saniyə qalacaq bəzi tuplelər qalıb. Üstəlik, identifikatoru 2 və ömrü 1576421257 olan dəstdən identifikatoru 3 və ömrü 1576421287 olan dəstdən keçərkən skan dayandırıldı. Ömrü 1576421287 və ya daha çox olan dəstlər sıralanmasına görə skan edilmədi. exp indeksi düymələri. Bu, ən əvvəldən əldə etmək istədiyimiz qənaətdir.
Tapşırığı dayandıraq:
capped_connection:call('libcapped-expirationd.kill', {'indexed'})
Tətbiq
Layihənin bütün xüsusiyyətləri haqqında məlumat verməyin ən yaxşı yolu onun orijinal mənbəyidir.
Başlanğıc metoduna verdiyimiz arqumentlər expirationd_task adlı strukturda saxlanılır:
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;
};
Ad atributu tapşırığın adıdır. space_id atributu boşluq identifikatorudur. rm_index_id atributu, dəstlərin silinəcəyi unikal indeksin identifikatorudur. it_index_id atributu çubuqların keçiləcəyi indeksin identifikatorudur. it_index_type atributu dələlərin keçiləcəyi indeks növüdür. filed_no atributu ömür boyu olan tuple sahəsinin sayıdır. scan_size atributu bir əməliyyatda skan edilən dələlərin maksimum sayıdır. scan_time atributu saniyələrlə tam skan vaxtıdır.
Biz arqumentlərin təhlilini nəzərdən keçirməyəcəyik. Bu, zəhmətli, lakin sadə bir işdir və kitabxana sizə kömək edəcəkdir
Biz təhlil üçün istifadə olunan bütün funksiyaların prototiplərini sadalayırıq:
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);
İndi keçək ən vacib şeyə - boşluqdan yan keçmək və tupleləri silmək məntiqinə. scan_size-dən böyük olmayan hər bir blok bloku bir əməliyyat altında skan edilir və dəyişdirilir. Uğurlu olarsa, bu əməliyyat həyata keçirilir, xəta baş verərsə, geri qaytarılır. expirationd_iterate funksiyasının sonuncu arqumenti skanlamanın başladığı və ya davam etdiyi iteratorun göstəricisidir. Bu iterator xəta baş verənə, boşluq bitənə və ya prosesi əvvəlcədən dayandırmaq mümkün olmayana qədər daxili olaraq artırılır. expirationd_expired funksiyası qovluğun ömrünü yoxlayır, expirationd_delete silsiləni silir, expirationd_breakable bizim davam etməyimiz lazım olub-olmadığını yoxlayır.
Expirationd_iterate funksiya kodu:
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;
}
Funksiya kodunun müddəti bitdi_keçdi:
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 funksiya kodu:
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);
}
Funksiya kodu 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
Mənbə koduna burada baxa bilərsiniz
Mənbə: www.habr.com