टैरनटूल के लिए अपने कैप्ड एक्सपायरीड मॉड्यूल लिखना

छवि


कुछ समय पहले, हमने टैरनटूल रिक्त स्थान में ट्यूपल्स को साफ करने की समस्या का सामना किया। सफाई तब शुरू नहीं की जानी चाहिए थी जब टारेंटूल पहले से ही मेमोरी से बाहर चल रहा था, लेकिन पहले से और एक निश्चित आवृत्ति के साथ। टारनटूल में इस कार्य के लिए Lua में लिखा गया एक मॉड्यूल है, जिसे एक्सपीरिएंस कहा जाता है। इस मॉड्यूल के थोड़े समय के उपयोग के बाद, हमने महसूस किया कि यह हमारे लिए उपयुक्त नहीं था: डेटा के बड़े संस्करणों की निरंतर सफाई पर, Lua जीसी में लटका दिया। इसलिए, हमने अपने कैप्ड एक्सपायरीड मॉड्यूल को विकसित करने के बारे में सोचा, उम्मीद है कि देशी प्रोग्रामिंग भाषा में लिखे गए कोड हमारी समस्याओं को सबसे अच्छे तरीके से हल करेंगे।


एक अच्छा उदाहरण टारेंटूल मॉड्यूल था जिसे मेमकेड कहा जाता है। इसमें प्रयुक्त दृष्टिकोण इस तथ्य पर आधारित है कि एक अलग क्षेत्र उस स्थान में प्रवेश किया जाता है जिसमें टपल के जीवनकाल को इंगित किया जाता है, दूसरे शब्दों में, टीटीएल। पृष्ठभूमि में मॉड्यूल स्थान को स्कैन करता है, वर्तमान समय के साथ ttl की तुलना करता है और यह तय करता है कि टपल को हटाना है या नहीं। ज्ञापन मॉड्यूल कोड सरल और सुरुचिपूर्ण है, लेकिन बहुत सामान्य है। सबसे पहले, यह क्रॉल और हटाने के लिए उपयोग किए जाने वाले इंडेक्स के प्रकार को ध्यान में नहीं रखता है। दूसरे, प्रत्येक पास पर, सभी ट्यूपल्स स्कैन किए जाते हैं, जिनमें से संख्या काफी बड़ी हो सकती है। और अगर एक्सपायरीड मॉड्यूल में पहली समस्या हल हो गई (ट्री-इंडेक्स को एक अलग वर्ग में आवंटित किया गया है), तो दूसरा अभी भी कोई ध्यान नहीं देता है। इसने अपने कोड लिखने के पक्ष में चुनाव को पूर्व निर्धारित किया।


विवरण


टारनटूल के लिए प्रलेखन में बहुत अच्छा ट्यूटोरियल है कि सी में अपनी संग्रहीत प्रक्रियाओं को कैसे लिखना है। सबसे पहले, मैं सुझाव देता हूं कि आप अपने आप को इसके साथ परिचित करें ताकि कमांड और कोड के साथ आवेषण को समझने के लिए नीचे मिल जाएगा। यह उन वस्तुओं के संदर्भ पर भी ध्यान देने योग्य है जो आपके स्वयं के कैप्ड मॉड्यूल, अर्थात् बॉक्स , फाइबर , इंडेक्स और 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 स्थित है। पुस्तकालय से दो कार्य निर्यात किए जाते हैं: शुरू करो और मार डालो। पहले आपको Lua से box.schema.func.create और box.schema.user.grant का उपयोग करके इन कार्यों को उपलब्ध कराने की आवश्यकता है। फिर एक ऐसा स्थान बनाएं जिसके टुपल्स में केवल तीन फ़ील्ड होंगे: पहला एक विशिष्ट पहचानकर्ता है, दूसरा ईमेल है, तीसरा है टपल का जीवनकाल। पहले फ़ील्ड के शीर्ष पर, ट्री-इंडेक्स बनाएं और इसे प्राथमिक कहें। अगला, हमें अपने मूल पुस्तकालय से जुड़ने के लिए वस्तु मिलती है।


प्रारंभिक कार्य के बाद, हम प्रारंभ कार्य शुरू करते हैं:


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

यह उदाहरण समाप्ति के मॉड्यूल की तरह ही स्कैनिंग पर काम करेगा, जो Lua में लिखा गया है। फ़ंक्शन के लिए पहला तर्क कार्य का अद्वितीय नाम है। दूसरा है अंतरिक्ष पहचानकर्ता। तीसरा एक अनोखा सूचकांक है जिसके द्वारा टुपल्स को हटा दिया जाएगा। चौथा वह सूचकांक है जिसके द्वारा ट्यूपल्स को बाईपास किया जाएगा। पांचवां - जीवनकाल के साथ टपल क्षेत्र की संख्या (संख्या 1 से जाती है, 0 नहीं!)। छठी और सातवीं - स्कैन सेटिंग्स। 1024 एक लेन-देन में देखी जा सकने वाली टुपल्स की अधिकतम संख्या है। 3600 - सेकंड में पूर्ण स्कैन समय।


ध्यान दें कि उदाहरण में क्रॉल और विलोपन के लिए एक ही सूचकांक का उपयोग किया जाता है। यदि यह एक पेड़ सूचकांक है, तो क्रॉल छोटी कुंजी से लेकर बड़े तक है। यदि कुछ अन्य, उदाहरण के लिए, हैश इंडेक्स, तो ट्रैवर्सल को एक नियम के रूप में, एक अनियंत्रित क्रम में किया जाता है। एक स्कैन में, सभी स्पेस ट्यूपल देखे जाते हैं।


चलो 60 सेकंड के जीवनकाल के साथ अंतरिक्ष में कई tuples डालें:


 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 सेकंड के बाद, सादृश्य द्वारा, कुछ और tuples जोड़ें:


 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 के जीवनकाल के साथ ट्यूपल और एक्सप इंडेक्स कुंजी के आदेश के कारण अधिक नहीं देखा गया। यह वह बचत है जो हम शुरुआत में हासिल करना चाहते थे।


कार्य रोकें:


 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 विशेषता इंडेक्स का प्रकार है जिसके द्वारा ट्यूपल्स का पता लगाया जाएगा। दायर_नहीं विशेषता जीवनकाल के साथ टपल क्षेत्र संख्या है। Scan_size विशेषता एक एकल लेनदेन में देखी जाने वाली tuples की अधिकतम संख्या है। स्कैन_टाइम विशेषता सेकंड में एक पूर्ण स्कैन के लिए समय है।


हम पार्सिंग तर्कों पर विचार नहीं करेंगे। यह एक श्रमसाध्य लेकिन सीधी नौकरी है जिसमें मेसपैक लाइब्रेरी आपकी मदद करेगी। मुश्किलें केवल उन अनुक्रमणिकाओं के साथ उत्पन्न हो सकती हैं जो Lua से एक जटिल डेटा संरचना के रूप में mp_map के साथ स्थानांतरित की जाती हैं, और सरल प्रकार mp_bool, mp_double, mp_int, mp_uint और mp_ray की सहायता से नहीं। लेकिन पूरे सूचकांक को पार्स नहीं किया गया है। यह इसकी विशिष्टता की जांच करने, प्रकार की गणना करने और पहचानकर्ता को निकालने के लिए पर्याप्त है।


हम उन सभी कार्यों के प्रोटोटाइप को सूचीबद्ध करते हैं जो पार्सिंग के लिए उपयोग किए जाते हैं:


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

और अब चलो सबसे महत्वपूर्ण बात पर चलते हैं - अंतरिक्ष को बायपास करने और ट्यूपल्स को हटाने का तर्क। Tuples के प्रत्येक ब्लॉक को scan_size से बड़ा नहीं देखा जाता है और इसे एक लेनदेन के तहत बदला जाता है। सफल होने पर, यह लेन-देन प्रतिबद्ध है; त्रुटि के मामले में, इसे वापस ले लिया जाता है। एक्सपीरिएंस_टरेट फ़ंक्शन के लिए अंतिम तर्क उस सूचक का सूचक है जहां से स्कैनिंग शुरू होती है या जारी रहती है। जब तक कोई त्रुटि नहीं होती है, तब तक यह पुनरावृत्ति अंदर होती है, स्थान समाप्त हो जाता है, या पहले से प्रक्रिया को रोकने का कोई अवसर नहीं है। 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); } 

समाप्ति तिथि समारोह का कोड:


 static bool expirationd_breakable(struct expirationd_task *task) { return task->it_index_id != task->rm_index_id && task->it_index_type == ITER_GT; } 

आवेदन


यहाँ स्रोत कोड की जाँच करें !

Source: https://habr.com/ru/post/hi479166/


All Articles