हाल ही में हमने अपने सिस्टम थ्रेड्स को फिर से बनाया है। चूंकि हमने Sphinx से फोर्क किया है और पहले searchd में सभी सिस्टम कार्यों को अक्सर उठने की शैली में बनाया गया था। प्रत्येक सेवा एक समर्पित थ्रेड में काम करती थी जो हर 50ms में जगती थी और जांचती थी कि क्या करने के लिए कुछ है या नहीं। इसका मतलब है कि एक निष्क्रिय डेमन भी ‘सिर्फ जांचने के लिए’ 200 बार प्रति सेकंड जगता है। 6 ऐसे कार्य होने से 1200 बार जगने की समस्या होगी, और यह विशेष रूप से Amazon AWS पर ग्राहकों द्वारा स्पष्ट हो गया है, जो CPU उपयोग की गणना करता है। उन आंतरिक कार्यों में शामिल हैं:
- सामान्य अनुक्रमणिका घुमाना
- एक एजेंट को पिंग करना
- अनुक्रमणिका प्रीलोड करना
- अनुक्रमणिका विशेषताओं को फ्लश करना
- एक रियलटाइम अनुक्रमणिका को फ्लश करना
- और बाइनरी लॉग को फ्लश करना
इनमें से सभी काफी दुर्लभ हैं (जैसे फ्लश RT हर 10 घंटे में एक बार हो सकता है), इसलिए उन्हें प्रति सेकंड 200 बार जांचना CPU का बर्बाद करना है। इसके अलावा, सभी ये कार्य स्वाभाविक रूप से ठोस नहीं हैं (जैसे विशाल लॉग में पाठ जोड़ना), बल्कि बस आवधिक क्रियाएँ हैं (एक ही कार्य को आवधिक रूप से दोहराना)। इस कार्यप्रणाली को ध्यान में रखते हुए, हमने पूरी चीज़ को पूरी तरह से फिर से लिखा है:
- सबसे पहले, हमने एक थ्रेड जोड़ा है जो टाइमर्स की सेवा करता है, जहां प्रत्येक क्रिया निर्धारित की जाती है।
- दूसरा, हमने कार्रवाईयों को स्वयं करने के लिए थ्रेड-पूल जोड़ा है।
- ताकि अंततः एक सेवा क्रिया (कार्य) निर्धारित हो, और जब टाइमर पहुंचता है, तो इसे थ्रेड पूल में स्थानांतरित किया जाता है, और फिर, इसे निष्पादित किया जाता है।
- समाप्त होने पर इसे हटा दिया जाता है, ताकि आवधिक कार्य स्वयं को अंत में फिर से निर्धारित कर सकें और पूरी तरह से नए कार्य का निर्माण कर सकें।
इस दृष्टिकोण पर, समर्पित सेवा थ्रेड्स का कोई समूह नहीं है, केवल एक टाइमर थ्रेड है। और यह, बारी-बारी से, हर 20ms में नहीं जगता है, बल्कि समय सीमाओं के साथ एक बाइनरी हीप रखता है और केवल कतार में सबसे पुराने टाइमर द्वारा निर्धारित अवधि पर ही जगता है। थ्रेड पूल, बारी-बारी से, समानांतर में 32 थ्रेड्स तक चला सकता है, लेकिन वास्तव में केवल एक, कभी-कभी दो ही काम में होते हैं। प्रत्येक थ्रेड का पूर्व निर्धारित निष्क्रिय अवधि (10m) होती है जिसके बाद इसे खत्म कर दिया जाता है, इसलिए ‘कोई कार्य नहीं है’ के मामले में, कुछ भी निष्क्रिय नहीं होता है (यहां तक कि टाइमर थ्रेड भी एक ‘आलसी’ तरीके से प्रारंभ किया जाता है, यानी इसे केवल तब शुरू किया जाता है जब वास्तविक कार्य निर्धारित करने की आवश्यकता होती है)। बहुत कम (<10min की अवधि) कार्यों के मामले में, श्रमिक थ्रेड पूल भी छोड़ दिया जाता है, ताकि श्रमिक थ्रेड केवल तब बनाए जाएं जब करने के लिए कुछ हो। इसलिए, सभी सेवा थ्रेड्स हटा दिए जाते हैं और कोई भी CPU को सेकंड में सैकड़ों बार किक नहीं करता है।
इस दृष्टिकोण का सबसे आशाजनक परिवर्तन ‘पिंग’ कार्य का नया व्यवहार है। अतीत में, हमने सभी एजेंट होस्ट एकत्र किए, और प्रत्येक पिंग अंतराल पर ‘पिंग’ आदेश उन्हें एक समूह के रूप में जारी किया। इसलिए, यदि एक होस्ट धीमा था, तो पूरा समूह भी धीमा था। इसके अलावा, इसका वास्तविक होस्ट स्थिति से कोई लेना-देना नहीं था - जैसे जब आप एक होस्ट को प्रश्नों द्वारा लोड करते हैं, तो इसे अलग से पिंग करना आवश्यक नहीं है क्योंकि प्रश्न स्वयं होस्ट की स्थिति के बारे में व्यापक सांख्यिकी प्रदान करते हैं। अब पिंग स्पष्ट है: इसे प्रत्येक होस्ट के लिए अलग से योजना बनाई जाती है और इसे प्रत्येक एक के वास्तविक last_answer_time से बंधित किया जाता है। यदि होस्ट धीमा है - केवल इसका पिंग कार्य इसके लिए इंतजार करेगा, अन्य सामान्य समय पर काम करेंगे। यदि एक होस्ट लोड में है - इसका last_answer_time निरंतर अपडेट किया जाएगा, ताकि वास्तविक पिंग तब नहीं होगा यदि कुछ क्वेरी पहले से हुई है last_query_time और ping_interval के बाद।
एक और विशेषता अब यह है कि कार्य समानांतर में काम कर सकते हैं। मान लीजिए, अनुक्रमणिका को फ्लश करना किसी भी संख्या की अनुक्रमणिकाओं पर एक साथ, न कि अनुक्रम में, बल्कि समानांतर में किया जा सकता है। फिलहाल, यह संख्या 2 कार्यों के लिए निर्धारित है, लेकिन यह केवल ट्यूनिंग का मामला है। इसके अलावा, जब कई समान कार्य निर्धारित होते हैं, तो अब हम उनकी संख्या को सीमित कर सकते हैं। कहें, ‘malloc_trim’ का कोई अर्थ नहीं है कि इसे एक से अधिक बार निर्धारित किया जाए, इसलिए यह एक प्रकार का ‘सिंगलटन’ है - यदि एक निर्धारित है, तो दूसरा निर्धारित करने का प्रयास खारिज कर दिया जाएगा।
इस प्रकार के कार्य प्रबंधन की अगली विशेषता इस तथ्य से आती है कि अब सभी कार्य एक कतार (किसी भी अन्य अलग समूह के थ्रेड्स) में निर्धारित / योजना बनाई जाती हैं, और हम बिल्कुल जानते हैं कि यह कब चलेगा। इसलिए, अब इस तरह की सांख्यिकी प्रदर्शित की जा सकती है, और इसे ‘debug sched’, ‘debug tasks’ और ‘debug systhreads’ जोड़कर किया जाता है।
पहला टाइमर के बाइनरी हीप को दिखाता है: शीर्षmost मान अगली समय सीमा और इसके साथ जुड़े कार्य को दर्शाता है; अन्य मान बाद में आते हैं (हालांकि वे अब कच्चे बाइनरी हीप के रूप में प्रदर्शित होते हैं; उन्हें अगर जरूरत हो तो क्रमबद्ध किया जा सकता है)।
‘debug tasks’ डेमन में पंजीकृत सभी कार्यों को उनके सांख्यिकी और गुणों के साथ दिखाता है (कितने ऐसे कार्य समानांतर में चल सकते हैं; कितने निर्धारित किए जा सकते हैं; वर्तमान में कितने कार्यान्वित हो रहे हैं, किसी कार्य के लिए कितना समय बिताया गया, इसे अंतिम बार कब समाप्त किया गया, इसे कितनी बार कार्यान्वित किया गया, कतार की ओवरलोडिंग के कारण कितने गिराए गए, और अभी कितने कतारबद्ध हैं)।
अंतिम ‘debug systhreads’ कार्यकर्ता थ्रेड्स की स्थिति को प्रदर्शित करता है, जैसे आंतरिक आईडी, थ्रेड आईडी, अंतिम चलने का समय, कुल CPU समय, कुल टिक्स और किए गए कार्यों की संख्या, अंतिम कार्य में कितना समय लगा, और कार्यकर्ता कितनी देर तक निष्क्रिय है। जैसा कि उल्लेख किया गया है, 10 मिनट के लिए निष्क्रिय रहना कार्यकर्ता को रोक देता है।
इस परिवर्तन का लाभ उठाने के लिए Manticore 3.1.0 या नए संस्करण में अपग्रेड करें