
ከተወሰነ ጊዜ በፊት በቦታዎች ውስጥ የውሃ ማጠራቀሚያዎችን የማጽዳት ችግር አጋጥሞናል ማጽዳቱ መጀመር ያለበት ታራንቱል የማስታወስ ችሎታው ዝቅተኛ በሚሆንበት ጊዜ ሳይሆን አስቀድሞ እና በየጊዜው ነው። ታራንቱል ለዚህ ዓላማ በሉአ የተጻፈ ሞጁል አለው፣ ይባላል። ይህንን ሞጁል ለአጭር ጊዜ ከተጠቀምንበት በኋላ፣ ለፍላጎታችን ተስማሚ እንዳልሆነ ተገነዘብን፡- ሉአ በተከታታይ ከፍተኛ መጠን ያለው መረጃ በማጽዳት ላይ እያለ እየተበላሸ ነበር። ስለዚህ በቋንቋ ቋንቋ የተጻፈ ኮድ ፍላጎቶቻችንን በተሻለ ሁኔታ እንደሚያሟላ ተስፋ በማድረግ የራሳችንን የተገደበ የማለቂያ ሞጁል ማዘጋጀት አስበን ነበር።
ለእኛ ጥሩ ምሳሌ የሚባለው የታራንቱል ሞጁል ነበር የሚጠቀምበት አቀራረብ የtupleን የመኖር ጊዜ (TTL) የሚገልጽ ቦታ ውስጥ የተለየ መስክ በመፍጠር ላይ የተመሠረተ ነው። ሞጁሉ በጀርባ ውስጥ ያለውን ቦታ ይቃኛል፣ TTLን ከአሁኑ ጊዜ ጋር ያወዳድራል፣ እና tupleውን መሰረዝ አለመሰረዝን ይወስናል። የተደበቀበት ሞጁል ኮድ ቀላል እና የሚያምር ነው፣ ግን በጣም አጠቃላይ ነው። በመጀመሪያ፣ የሚሻገረውን እና የሚሰረዘውን የኢንዴክስ አይነት ግምት ውስጥ አያስገባም። ሁለተኛ፣ እያንዳንዱ ማለፊያ ሁሉንም tuples ይቃኛል፣ ይህም በጣም ትልቅ ሊሆን ይችላል። ጊዜው ያለፈበት ሞጁል የመጀመሪያውን ችግር ቢፈታም (የዛፍ ኢንዴክስን ወደ ተለየ ክፍል በመለየት)፣ ሁለተኛው ግን አልተፈታም። እነዚህ ሶስት ነጥቦች የራሴን ኮድ የመጻፍ ምርጫ ወስነዋል።
መግለጫ
ለ tarantool በጣም ጥሩ ሰነድ አለ በሲ ውስጥ የራስዎን የተከማቹ ሂደቶች እንዴት እንደሚጽፉ። ከዚህ በታች የሚያዩዋቸውን ትዕዛዞች እና ኮዶች መረዳት እንዲችሉ በመጀመሪያ እራስዎን በደንብ እንዲያውቁት እመክራለሁ። እንዲሁም ትኩረት መስጠት ተገቢ ነው። የራስዎን ክዳን ያለው ሞጁል ሲጽፉ የሚገኙ ነገሮችን፣ ማለትም , , и .
ከውጭው እንጀምርና የተዘጋ ጊዜ ያለፈበት ሞጁል ምን እንደሚመስል እንመልከት፡
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ን በመጠቀም ከሉአ ተደራሽ ማድረግ አለብን። ከዚያም፣ ቱፕሎቹ ሶስት መስኮችን ብቻ የሚይዙበት ቦታ እንፈጥራለን፡ የመጀመሪያው ልዩ መለያ፣ ሁለተኛው የኢሜይል አድራሻ እና ሦስተኛው የtuple የህይወት ዘመን ነው። በመጀመሪያው መስክ ላይ የዛፍ ማውጫ እንገነባለን እና ዋና ብለን እንጠራዋለን። በመቀጠል፣ ለአገሬው ቤተ መፃህፍት የግንኙነት ነገር እናገኛለን።
ከዝግጅት ስራው በኋላ የመነሻ ተግባሩን እንጀምራለን-
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, 'user0@tarantool.io', math.floor(fiber.time()) + 60}
box.space.tester:insert{1, 'user1@tarantool.io', math.floor(fiber.time()) + 60}
box.space.tester:insert{2, 'user2@tarantool.io', math.floor(fiber.time()) + 60}ማስገባት የተሳካ መሆኑን እናረጋግጥ፦
tarantool> box.space.tester.index.primary:select()
---
- - [0, 'user0@tarantool.io', 1576418976]
- [1, 'user1@tarantool.io', 1576418976]
- [2, 'user2@tarantool.io', 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, 'user0@tarantool.io', math.floor(fiber.time()) + 60}
box.space.tester:insert{1, 'user1@tarantool.io', math.floor(fiber.time()) + 60}
box.space.tester:insert{2, 'user2@tarantool.io', math.floor(fiber.time()) + 60}ከ30 ሰከንዶች በኋላ፣ በተመሳሳይ መንገድ ጥቂት ተጨማሪ ቱፕሎችን እንጨምራለን፡
box.space.tester:insert{3, 'user3@tarantool.io', math.floor(fiber.time()) + 60}
box.space.tester:insert{4, 'user4@tarantool.io', math.floor(fiber.time()) + 60}
box.space.tester:insert{5, 'user5@tarantool.io', math.floor(fiber.time()) + 60}ማስገባት የተሳካ መሆኑን እናረጋግጥ፦
tarantool> box.space.tester.index.primary:select()
---
- - [0, 'user0@tarantool.io', 1576421257]
- [1, 'user1@tarantool.io', 1576421257]
- [2, 'user2@tarantool.io', 1576421257]
- [3, 'user3@tarantool.io', 1576421287]
- [4, 'user4@tarantool.io', 1576421287]
- [5, 'user5@tarantool.io', 1576421287]
...ከ60+ ሰከንዶች በኋላ (ከመጀመሪያው የቱፕል ማስገቢያ መጀመሪያ ጀምሮ በመቁጠር) ምርጫውን እንድገመው እና ክዳኑ የሚያበቃበት ሞዱል ቀድሞውኑ እየሰራ መሆኑን እናያለን፡
tarantool> box.space.tester.index.primary:select()
---
- - [3, 'user3@tarantool.io', 1576421287]
- [4, 'user4@tarantool.io', 1576421287]
- [5, 'user5@tarantool.io', 1576421287]
...በቦታው ውስጥ ቱፕሎች የቀሩ ሲሆን ለ30 ሰከንዶች ያህል በሕይወት ይቆዩ ነበር። ከዚህም በላይ፣ ከአይዲ 2 ካለው ቲፕል ሲንቀሳቀስ ቅኝቱ ቆሟል፣ እና 1576421257 ወደ አይዲ 3 ወዳለው ቲፕል እና 1576421287 የዕድሜ ልክ። 1576421287 ወይም ከዚያ በላይ ዕድሜ ያላቸው ቲፕሎች የExp ኢንዴክስ ቁልፎችን በመደርደር ምክንያት አልተቃኙም። ይህ ከመጀመሪያው ጀምሮ የምንፈልገው ቁጠባ ነው።
ተግባሩን እናቁም፦
capped_connection:call('libcapped-expirationd.kill', {'indexed'})ትግበራ
የአንድን ፕሮጀክት ሁሉንም ገጽታዎች ለመግለጽ በጣም ጥሩው መንገድ ሁልጊዜ ከምንጩ ነው። በዚህ ህትመት ውስጥ፣ በጣም አስፈላጊ በሆኑ ነጥቦች ላይ ብቻ እናተኩራለን፣ ማለትም የቦታ ማለፊያ ስልተ ቀመሮች።
ወደ መጀመሪያ ዘዴ የምናስተላልፋቸው ክርክሮች በ expired_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 መለያ ባህሪው የspace መለያ ነው። የrm_index_id መለያ ባህሪው ለtuple ስረዛ ጥቅም ላይ የሚውለው ልዩ ኢንዴክስ መለያ ነው። የit_index_id መለያ ባህሪው ለtuple traversal ጥቅም ላይ የሚውለው የኢንዴክስ መለያ ነው። የit_index_type መለያ ባህሪው ለtuple traversal ጥቅም ላይ የሚውለው የኢንዴክስ አይነት ነው። የfiled_no መለያ ባህሪው የጊዜ-ወደ-ቀጥታ እሴት ያለው የtuple መስክ ቁጥር ነው። የscan_size መለያ ባህሪው በአንድ ግብይት ውስጥ የተቃኙት ከፍተኛው የtuples ብዛት ነው። የscan_time መለያ በሰከንዶች ውስጥ ሙሉ የፍተሻ ጊዜ ነው።
የክርክር ትንተናን አንሸፍንም። ይህ ከባድ ግን ቀላል ስራ ነው፣ እና ቤተ መፃህፍቱ በዚህ ረገድ ይረዳዎታል። ችግሮች ሊፈጠሩ የሚችሉት ቀላል mp_bool፣ mp_double፣ mp_int፣ mp_uint እና mp_array አይነቶችን ከመጠቀም ይልቅ ከሉአ እንደ ውስብስብ የውሂብ መዋቅር በmp_map አይነት ሲተላለፉ ብቻ ነው። ሆኖም ግን፣ ሙሉውን ኢንዴክስ መተንተን አስፈላጊ አይደለም። ልዩነቱን ማረጋገጥ፣ ዓይነቱን ማስላት እና መለያውን ማውጣት ብቻ በቂ ነው።
ለመተንተን የሚያገለግሉትን ሁሉንም ተግባራት ምሳሌዎች እንዘርዝር፡
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);አሁን ወደ በጣም አስፈላጊው ክፍል እንሸጋገር - የቦታ መተላለፊያ እና የtuple መሰረዝ ጀርባ ያለው አመክንዮ። ከ scan_size የማይበልጥ እያንዳንዱ የtuples ብሎክ በአንድ ግብይት ይቃኛል እና ይሻሻላል። ከተሳካ ይህ ግብይት ተፈጽሟል፤ ስህተት ከተከሰተ ወደ ኋላ ይንከባለላል። የመጨረሻው ክርክር ወደ expired_iterate ተግባር የተላለፈው ክርክር ቅኝቱ የሚጀምርበት ወይም የሚቀጥልበት epiretor ጠቋሚ ነው። ይህ epiretor ስህተት እስኪከሰት፣ ቦታው እስኪያልቅ ወይም ሂደቱን ቀደም ብሎ ለማስቆም የሚያስችል መንገድ እስኪኖር ድረስ በውስጥ ይጨምራል። expired_expired ተግባር የtupleን የህይወት ዘመን ይፈትሻል፣ expired_delete የtupleን ይሰርዛል፣ እና expired_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;
}የተግባር ኮድ expired_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);
}Expirationd_breakable የተግባር ኮድ፡
static bool
expirationd_breakable(struct expirationd_task *task)
{
return task->it_index_id != task->rm_index_id && task->it_index_type == ITER_GT;
}ትግበራ
የምንጭ ኮዱን በ ላይ ማየት ይችላሉ !
ምንጭ: hab.com
