டரான்டூலுக்கு எங்கள் சொந்த மூடிய காலாவதியான தொகுதியை எழுதுகிறோம்

டரான்டூலுக்கு எங்கள் சொந்த மூடிய காலாவதியான தொகுதியை எழுதுகிறோம்

சில காலத்திற்கு முன்பு, இடைவெளிகளில் டூப்பிள்களை சுத்தம் செய்வதில் சிக்கலை எதிர்கொண்டோம் டரான்டூல். டரான்டூல் ஏற்கனவே நினைவகம் இல்லாமல் இருக்கும்போது சுத்தம் செய்யத் தொடங்கவில்லை, ஆனால் முன்கூட்டியே மற்றும் ஒரு குறிப்பிட்ட அதிர்வெண்ணில். இந்த பணிக்காக, டரான்டூல் லுவாவில் எழுதப்பட்ட ஒரு தொகுதி உள்ளது காலாவதி. இந்த தொகுதியை சிறிது நேரம் பயன்படுத்திய பிறகு, இது எங்களுக்கு ஏற்றது அல்ல என்பதை நாங்கள் உணர்ந்தோம்: பெரிய அளவிலான தரவை தொடர்ந்து சுத்தம் செய்ததால், லுவா ஜிசியில் தொங்கினார். எனவே, சொந்த நிரலாக்க மொழியில் எழுதப்பட்ட குறியீடு எங்கள் பிரச்சினைகளை சிறந்த முறையில் தீர்க்கும் என்று நம்பி, எங்களுடைய சொந்த மூடிய காலாவதியான தொகுதியை உருவாக்குவது பற்றி யோசித்தோம்.

எங்களுக்கு ஒரு நல்ல உதாரணம் டரான்டூல் தொகுதி என்று அழைக்கப்படுகிறது memcached. அதில் பயன்படுத்தப்படும் அணுகுமுறை விண்வெளியில் ஒரு தனி புலம் உருவாக்கப்பட்டுள்ளது என்ற உண்மையை அடிப்படையாகக் கொண்டது, இது டூபிளின் வாழ்நாளைக் குறிக்கிறது, வேறுவிதமாகக் கூறினால், ttl. பின்னணியில் உள்ள தொகுதியானது இடத்தை ஸ்கேன் செய்து, தற்போதைய நேரத்துடன் TTL ஐ ஒப்பிட்டு, tuple ஐ நீக்கலாமா வேண்டாமா என்பதை தீர்மானிக்கிறது. மெம்கேச் செய்யப்பட்ட தொகுதி குறியீடு எளிமையானது மற்றும் நேர்த்தியானது, ஆனால் மிகவும் பொதுவானது. முதலாவதாக, வலைவலம் மற்றும் நீக்கப்படும் குறியீட்டு வகையை இது கணக்கில் எடுத்துக்கொள்ளாது. இரண்டாவதாக, ஒவ்வொரு பாஸிலும் அனைத்து டூப்பிள்களும் ஸ்கேன் செய்யப்படுகின்றன, அவற்றின் எண்ணிக்கை மிகப் பெரியதாக இருக்கும். காலாவதியான தொகுதியில் முதல் சிக்கல் தீர்க்கப்பட்டால் (மரக் குறியீடு ஒரு தனி வகுப்பாகப் பிரிக்கப்பட்டது), இரண்டாவது இன்னும் எந்த கவனத்தையும் பெறவில்லை. இந்த மூன்று புள்ளிகளும் எனது சொந்த குறியீட்டை எழுதுவதற்கு ஆதரவாக தேர்வை முன்னரே தீர்மானித்தன.

விளக்கம்

டரான்டூலுக்கான ஆவணங்கள் மிகச் சிறந்தவை பயிற்சி உங்கள் சேமிக்கப்பட்ட நடைமுறைகளை 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 நூலகம் அமைந்துள்ள கோப்பகத்தில் நாங்கள் டரான்டூலைத் தொடங்குகிறோம். நூலகத்திலிருந்து இரண்டு செயல்பாடுகள் ஏற்றுமதி செய்யப்படுகின்றன: தொடங்குதல் மற்றும் கொல்லுதல். 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)

ஒரு சில விதிவிலக்குகளுடன், முதல் எடுத்துக்காட்டில் உள்ளதைப் போலவே இங்கேயும் உள்ளது. மூன்றாவது புலத்தின் மேல் ஒரு மரக் குறியீட்டை உருவாக்கி அதை எக்ஸ்ப் என்று அழைக்கிறோம். முதன்மை எனப்படும் குறியீட்டைப் போலன்றி, இந்தக் குறியீடு தனித்துவமாக இருக்க வேண்டியதில்லை. எக்ஸ்ப் இண்டெக்ஸ் மூலம் டிராவர்சல் மேற்கொள்ளப்படும், முதன்மை மூலம் நீக்கம் செய்யப்படும். முன்பு இரண்டும் முதன்மைக் குறியீட்டைப் பயன்படுத்தி மட்டுமே செய்யப்பட்டன என்பதை நாங்கள் நினைவில் கொள்கிறோம்.

ஆயத்த வேலைக்குப் பிறகு, புதிய வாதங்களுடன் தொடக்க செயல்பாட்டை இயக்குகிறோம்:

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 ஐடி 3 மற்றும் வாழ்நாள் 1576421287 கொண்ட டூபிளில் இருந்து நகரும் போது ஸ்கேன் நிறுத்தப்பட்டது. எக்ஸ்ப் குறியீட்டு விசைகள். ஆரம்பத்திலேயே நாம் அடைய விரும்பிய சேமிப்பு இதுதான்.

பணியை நிறுத்துவோம்:

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 பண்புக்கூறு என்பது டூப்பிள்கள் கடந்து செல்லும் குறியீட்டின் வகையாகும். File_no பண்புக்கூறு என்பது வாழ்நாளுடன் கூடிய tuple புலத்தின் எண்ணிக்கை. scan_size பண்புக்கூறு என்பது ஒரு பரிவர்த்தனையில் ஸ்கேன் செய்யப்படும் அதிகபட்ச டூப்பிள்களின் எண்ணிக்கையாகும். scan_time பண்புக்கூறு என்பது நொடிகளில் முழு ஸ்கேன் நேரமாகும்.

பாகுபடுத்தும் வாதங்களை நாங்கள் கருத்தில் கொள்ள மாட்டோம். இது கடினமான ஆனால் எளிமையான வேலை, இதன் மூலம் நூலகம் உங்களுக்கு உதவும் 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);

இப்போது மிக முக்கியமான விஷயத்திற்குச் செல்வோம் - இடத்தைத் தவிர்த்து டூப்பிள்களை நீக்குவதற்கான தர்க்கம். Scan_size ஐ விட பெரிய டூப்பிள்களின் ஒவ்வொரு தொகுதியும் ஒரே பரிவர்த்தனையின் கீழ் ஸ்கேன் செய்யப்பட்டு மாற்றியமைக்கப்படுகிறது. வெற்றிகரமாக இருந்தால், இந்த பரிவர்த்தனை உறுதியானது; பிழை ஏற்பட்டால், அது திரும்பப் பெறப்படும். 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

கருத்தைச் சேர்