ටැරන්ටූල් සඳහා අපගේම ආවරණ සහිත කල් ඉකුත් වූ මොඩියුලය ලිවීම

ටැරන්ටූල් සඳහා අපගේම ආවරණ සහිත කල් ඉකුත් වූ මොඩියුලය ලිවීම

කලකට පෙර අපි අවකාශයේ ටියුපල් පිරිසිදු කිරීමේ ගැටලුවට මුහුණ දුන්නා tarantool. පිරිසිදු කිරීම ආරම්භ කිරීමට සිදු වූයේ ටැරන්ටූල් දැනටමත් මතකය අවසන් වූ විට නොව, කල්තියා සහ නිශ්චිත සංඛ්යාතයකින්. මෙම කාර්යය සඳහා ටැරන්ටූල් සතුව ලුවා හි ලියා ඇති මොඩියුලයක් ඇත කල් ඉකුත්වීම. කෙටි කාලයක් සඳහා මෙම මොඩියුලය භාවිතා කිරීමෙන් පසුව, එය අපට සුදුසු නොවන බව අපට වැටහුණි: විශාල දත්ත ප්රමාණයක් නිරන්තරයෙන් පිරිසිදු කිරීම නිසා, ලූවා GC හි එල්ලා තැබුවේය. එමනිසා, ස්වදේශීය ක්‍රමලේඛන භාෂාවෙන් ලියා ඇති කේතය අපගේ ගැටළු හොඳම ආකාරයෙන් විසඳනු ඇතැයි බලාපොරොත්තු වන අපගේම ආවරණ සහිත කල් ඉකුත් වූ මොඩියුලය සංවර්ධනය කිරීමට අපි සිතුවෙමු.

අපට හොඳ උදාහරණයක් වූයේ ටැරන්ටූල් මොඩියුලයයි memcached. එහි භාවිතා වන ප්රවේශය පදනම් වී ඇත්තේ අවකාශයේ වෙනම ක්ෂේත්රයක් නිර්මාණය වී ඇති අතර, එය ටුපල්ගේ ආයු කාලය පෙන්නුම් කරයි, වෙනත් වචන වලින්, ttl. පසුබිමේ ඇති මොඩියුලය අවකාශය පරිලෝකනය කරයි, TTL වත්මන් වේලාව සමඟ සංසන්දනය කර ටියුපල් මකා දැමිය යුතුද නැද්ද යන්න තීරණය කරයි. memcached මොඩියුල කේතය සරල සහ අලංකාර, නමුත් ඉතා සාමාන්‍ය වේ. පළමුව, එය බඩගා යන සහ මකා දමන ලද දර්ශකයේ වර්ගය සැලකිල්ලට නොගනී. දෙවනුව, සෑම පාස් එකකම සියලුම ටියුපල් පරිලෝකනය කරනු ලැබේ, ඒවායින් සංඛ්‍යාව තරමක් විශාල විය හැකිය. කල් ඉකුත් වූ මොඩියුලයේ පළමු ගැටළුව විසඳා ඇත්නම් (ගස් දර්ශකය වෙනම පන්තියකට වෙන් කර ඇත), දෙවැන්න තවමත් අවධානයට ලක් නොවීය. මෙම කරුණු තුන මගේම කේතය ලිවීමට පක්ෂව තේරීම කලින් තීරණය කළේය.

විස්තර

ටැරන්ටූල් සඳහා ලියකියවිලි ඉතා හොඳයි නිබන්ධනය ඔබේ ගබඩා කර ඇති ක්‍රියා පටිපාටි C හි ලියන්නේ කෙසේද යන්න ගැන. පළමුවෙන්ම, පහත දිස්වන විධාන සහ කේතය සහිත එම ඇතුළු කිරීම් තේරුම් ගැනීමට ඔබට එය හුරු කරවීමට මම යෝජනා කරමි. එය ද අවධානය යොමු කිරීම වටී යොමු කිරීම ඔබේම ආවරණ මොඩියුලය ලිවීමේදී පවතින වස්තූන් වෙත, එනම් කොටුව, කෙඳි, දර්ශකය и txn.

අපි දුර සිට ආරම්භ කර පිටතින් පෙනෙන කල් ඉකුත් වූ මොඩියුලය දෙස බලමු:

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)

සරල බව සඳහා, අපි අපගේ libcapped-expirationd.so පුස්තකාලය පිහිටා ඇති නාමාවලියෙහි tarantool දියත් කරන්නෙමු. පුස්තකාලයෙන් කාර්යයන් දෙකක් අපනයනය කෙරේ: ආරම්භ කිරීම සහ මරා දැමීම. පළමු පියවර වන්නේ box.schema.func.create සහ box.schema.user.grant භාවිතයෙන් Lua වෙතින් මෙම කාර්යයන් ලබා ගත හැකි කිරීමයි. ඉන්පසු ටියුපල් වල ක්ෂේත්‍ර තුනක් පමණක් අඩංගු වන ඉඩක් සාදන්න: පළමුවැන්න අද්විතීය හඳුනාගැනීමක්, දෙවැන්න විද්‍යුත් තැපෑල සහ තෙවනුව ටූපල්ගේ ආයු කාලයයි. අපි පළමු ක්ෂේත්‍රයට ඉහළින් ගස් දර්ශකයක් ගොඩනඟා එය ප්‍රාථමික ලෙස හඳුන්වමු. ඊළඟට අපි අපේ ස්වදේශික පුස්තකාලයට සම්බන්ධ කිරීමේ වස්තුව ලබා ගනිමු.

සූදානම් වීමේ කාර්යයෙන් පසු, ආරම්භක කාර්යය ක්රියාත්මක කරන්න:

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

මෙම උදාහරණය ලුවා හි ලියා ඇති කල් ඉකුත් වූ මොඩියුලයට සමානව ස්කෑන් කිරීමේදී ක්‍රියා කරයි. ආරම්භක කාර්යය සඳහා පළමු තර්කය වන්නේ කාර්යයේ අද්විතීය නමයි. දෙවැන්න අභ්‍යවකාශ හඳුනාගැනීමයි. තෙවනුව ටියුපල් මකා දමන අද්විතීය දර්ශකයකි. හතරවෙනි එක තමයි ටියුපල් හරහා ගමන් කරන දර්ශකය. පස්වන යනු ආයු කාලය සහිත ටියුපල් ක්ෂේත්‍රයේ සංඛ්‍යාවයි (සංඛ්‍යාකරණය ආරම්භ වන්නේ 1 නොව 0 සිටයි!). හයවන සහ හත්වන ස්කෑන් සැකසුම් වේ. 1024 යනු එක් ගනුදෙනුවකදී නැරඹිය හැකි උපරිම ටුපල් ගණනයි. 3600 — සම්පූර්ණ ස්කෑන් කාලය තත්පර වලින්.

බඩගා යාම සහ මකා දැමීම සඳහා උදාහරණය එකම දර්ශකය භාවිතා කරන බව සලකන්න. මෙය ගස් දර්ශකයක් නම්, කුඩා යතුරේ සිට විශාල එක දක්වා ගමන් කිරීම සිදු කරනු ලැබේ. වෙනත් යමක් තිබේ නම්, උදාහරණයක් ලෙස, හැෂ් දර්ශකය, එවිට ගමන් කිරීම රීතියක් ලෙස, අහඹු ලෙස සිදු කරනු ලැබේ. සියලුම අභ්‍යවකාශ ටියුපල් එක ස්කෑන් එකකින් ස්කෑන් කෙරේ.

තත්පර 60 ක ආයු කාලයක් සහිත අභ්‍යවකාශයට ටුපල් කිහිපයක් ඇතුළු කරමු:

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}

ඇතුළත් කිරීම සාර්ථක දැයි පරීක්ෂා කරමු:

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

තත්පර 60+ ට පසුව තේරීම නැවත සිදු කරමු (පළමු ටූපල් ඇතුල් කිරීමේ ආරම්භයේ සිට ගණන් කිරීම) සහ ආවරණ කල් ඉකුත් වූ මොඩියුලය දැනටමත් සකසා ඇති බව බලන්න:

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

කාර්යය නතර කරමු:

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

බඩගා යාම සඳහා වෙනම දර්ශකයක් භාවිතා කරන දෙවන උදාහරණය දෙස බලමු:

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)

මෙහි ඇති සෑම දෙයක්ම පළමු උදාහරණයේ මෙන් ම, කිහිපයක් හැරුණු විට. අපි තුන්වන ක්ෂේත්‍රයට ඉහළින් ගස් දර්ශකයක් ගොඩනඟා එය Exp ලෙස හඳුන්වමු. මෙම දර්ශකය ප්‍රාථමික ලෙස හැඳින්වෙන දර්ශකය මෙන් නොව, අද්විතීය විය යුතු නැත. සංක්‍රමණය එක්ස්ප් ඉන්ඩෙක්ස් මගින්ද, ප්‍රාථමිකය මගින් මකාදැමීමද සිදු කෙරේ. අපට මතකයි මීට පෙර දෙකම ප්‍රාථමික දර්ශකය භාවිතයෙන් පමණක් සිදු කරන ලදී.

සූදානම් වීමේ කාර්යයෙන් පසුව, අපි නව තර්ක සමඟ ආරම්භක කාර්යය ධාවනය කරමු:

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

තත්පර 60 ක ජීවිත කාලයක් සමඟ නැවත අභ්‍යවකාශයට ටියුපල් කිහිපයක් ඇතුළු කරමු:

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 කට පසු, ප්‍රතිසමයෙන්, අපි තවත් ටියුපල් කිහිපයක් එකතු කරන්නෙමු:

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}

ඇතුළත් කිරීම සාර්ථක දැයි පරීක්ෂා කරමු:

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

තත්පර 60+ ට පසුව තේරීම නැවත සිදු කරමු (පළමු ටූපල් ඇතුල් කිරීමේ ආරම්භයේ සිට ගණන් කිරීම) සහ ආවරණ කල් ඉකුත් වූ මොඩියුලය දැනටමත් සකසා ඇති බව බලන්න:

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

තව තත්ත්පර 30ක් පමණ ජීවත් වීමට ඇති අවකාශයේ ටියුපල් කිහිපයක් ඉතිරිව ඇත. තව ද, 2 හැඳුනුම්පතක් සහ 1576421257 ජීවිත කාලය සහිත tuple එකක සිට 3 හි ID සහ 1576421287 ජීවිත කාලය සහිත tuple වෙත මාරු වන විට ස්කෑන් කිරීම නතර විය. exp index යතුරු. මෙය අපට ආරම්භයේදීම ලබා ගැනීමට අවශ්‍ය වූ ඉතිරිකිරීමයි.

කාර්යය නතර කරමු:

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

Реализация

ව්‍යාපෘතියක සියලුම විශේෂාංග ගැන පැවසීමට හොඳම ක්‍රමය එහි මුල් මූලාශ්‍රයයි. කේතය! ප්‍රකාශනයේ කොටසක් ලෙස, අපි වඩාත් වැදගත් කරුණු කෙරෙහි පමණක් අවධානය යොමු කරමු, එනම් අභ්‍යවකාශ බයිපාස් ඇල්ගොරිතම.

අපි ආරම්භක ක්‍රමයට ලබා දෙන තර්කයන් 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;
};

නාම ගුණාංගය යනු කාර්යයේ නමයි. space_id ගුණාංගය අවකාශය හඳුනාගැනීමයි. rm_index_id ගුණාංගය යනු ටියුපල් මකනු ලබන අද්විතීය දර්ශකයේ හඳුනාගැනීමයි. it_index_id ගුණාංගය යනු ටියුපල් හරහා ගමන් කරන දර්ශකයේ හඳුනාගැනීමයි. it_index_type attribute යනු ටියුපල් හරහා ගමන් කරන දර්ශක වර්ගයයි. File_no attribute යනු ආයු කාලය සහිත tuple field ගණනයි. scan_size attribute යනු එක් ගනුදෙනුවකදී ස්කෑන් කරන ලද උපරිම ටියුපල් ගණනයි. ස්කෑන්_ටයිම් ගුණාංගය යනු තත්පර වලින් සම්පූර්ණ ස්කෑන් කාලයයි.

අපි තර්ක විග්‍රහ කිරීම සලකා බලන්නේ නැත. මෙය වේදනාකාරී නමුත් සරල කාර්යයක් වන අතර, පුස්තකාලය ඔබට උපකාර කරනු ඇත msgpuck. දුෂ්කරතා ඇති විය හැක්කේ mp_map වර්ගය සමඟ සංකීර්ණ දත්ත ව්‍යුහයක් ලෙස Lua වෙතින් සම්මත කරන ලද දර්ශක සමඟ පමණක් වන අතර mp_bool, mp_double, mp_int, mp_uint සහ mp_array යන සරල වර්ග භාවිතා නොකරයි. නමුත් සම්පූර්ණ දර්ශකය විග්‍රහ කිරීම අවශ්‍ය නොවේ. ඔබට අවශ්‍ය වන්නේ එහි සුවිශේෂත්වය පරීක්ෂා කිරීම, වර්ගය ගණනය කිරීම සහ හඳුනාගැනීම උකහා ගැනීම පමණි.

විග්‍රහ කිරීම සඳහා භාවිතා කරන සියලුම ශ්‍රිතවල මූලාකෘති අපි ලැයිස්තුගත කරමු:

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

දැන් අපි වඩාත්ම වැදගත් දෙය වෙත යමු - අවකාශය මඟ හැරීමේ සහ ටියුපල් මකා දැමීමේ තර්කනය. ස්කෑන්_ප්‍රමාණයට වඩා විශාල නොවන සෑම ටියුපල් බ්ලොක් එකක්ම තනි ගනුදෙනුවක් යටතේ ස්කෑන් කර වෙනස් කරනු ලැබේ. සාර්ථක නම්, මෙම ගනුදෙනුව සිදු කරනු ලැබේ; දෝෂයක් සිදුවුවහොත්, එය ආපසු හරවනු ලැබේ. Expirationd_iterate ශ්‍රිතයේ අවසාන තර්කය ස්කෑන් කිරීම ආරම්භ වන හෝ දිගටම පවතින පුනරාවර්තකයට දර්ශකයකි. දෝෂයක් සිදු වන තුරු, අවකාශය අවසන් වන තුරු හෝ ක්‍රියාවලිය කල්තියා නැවැත්වීමට නොහැකි වන තෙක් මෙම පුනරාවර්තකය අභ්‍යන්තරව වැඩි වේ. expirationd_expired ශ්‍රිතය tuple එකක ආයු කාලය පරීක්ෂා කරයි, expirationd_delete tuple එකක් මකයි, expirationd_breakable අපි ඉදිරියට යා යුතුද යන්න පරීක්ෂා කරයි.

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

ක්‍රියාකාරී කේතය 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 ශ්‍රිත කේතය:

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

ක්‍රියාකාරී කේතය 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;
}

අයදුම්පත

ඔබට මූලාශ්‍ර කේතය නැරඹිය හැක මෙහි!

මූලාශ්රය: www.habr.com

අදහස් එක් කරන්න