इस पेज पर, Bazel के बेहतर नियमों को लिखने से जुड़ी खास समस्याओं और चुनौतियों के बारे में खास जानकारी मिलती है.
ज़रूरी जानकारी
- अनुमान: सुधार, थ्रूपुट, इस्तेमाल में आसानी, और इंतज़ार का समय
- अनुमान: बड़े पैमाने पर डेटा स्टोर करने की जगह
- अनुमान: बिल्ड-जैसी विवरण भाषा
- ऐतिहासिक: लोडिंग, विश्लेषण, और एक्ज़ीक्यूशन के बीच का लंबे समय का अंतर पुराने होने के बावजूद एपीआई पर असर डालता है
- स्वाभाविक: रिमोट तरीके से एक्ज़ीक्यूशन और कैश मेमोरी बनाना मुश्किल होता है
- स्वाभाविक: सही और तेज़ी से बढ़ने वाले बिल्ड के लिए बदलाव की जानकारी का इस्तेमाल करना असामान्य कोडिंग पैटर्न की ज़रूरत होती है
- स्वाभाविक: क्वाड्रेटिक समय और मेमोरी उपभोग से बचना मुश्किल है
अनुमान
यहां बिल्ड सिस्टम के बारे में कुछ अनुमान लगाए गए हैं, जैसे कि सुधार, इस्तेमाल में आसानी, थ्रूपुट, और बड़े पैमाने पर डेटा स्टोर करने की जगह. नीचे दिए गए सेक्शन इन अनुमानों और ऑफ़र दिशा-निर्देशों को पूरा करते हैं, ताकि यह पक्का किया जा सके कि नियम सही तरीके से लिखे गए हैं.
सही जानकारी देना, थ्रूपुट, इस्तेमाल में आसानी, और इंतज़ार का समय तय करना
हम मानते हैं कि इंक्रीमेंटल बिल्ड के सम्मान के साथ बिल्ड सिस्टम को सबसे पहले और सही तरीके से काम करना चाहिए. दिए गए सोर्स ट्री के लिए, एक जैसा बिल्ड का आउटपुट हमेशा एक जैसा होना चाहिए, भले ही आउटपुट ट्री कुछ भी दिखता हो. पहले अनुमान में, इसका मतलब है कि बैजल को दिए गए बिल्ड चरण में जाने वाले हर एक इनपुट के बारे में जानने की ज़रूरत है, ताकि कोई भी इनपुट बदलने पर वह चरण को फिर से चला सके. यह तय किया जा सकता है कि Bazel को सही तरीके से कैसे सेट किया जाए. इससे, बिल्ड की तारीख / समय जैसी जानकारी लीक होती है. साथ ही, यह फ़ाइल एट्रिब्यूट में बदलाव जैसे कुछ खास तरह के बदलावों को अनदेखा कर देता है. सैंडबॉक्स करने की सुविधा पूरी तरह से सही है. इसलिए, यह इनपुट की फ़ाइलों को पढ़ने से रोकती है. इससे, फ़ाइलों के सही होने की जानकारी नहीं मिलती. सिस्टम की स्वाभाविक सीमाओं के अलावा, कुछ ऐसी सही गड़बड़ियां हैं जिनके बारे में पहले से पता है. इनमें से ज़्यादातर समस्याएं, फ़ाइलसेट या C++ नियमों से जुड़ी हैं, जो दोनों मुश्किल समस्याएं हैं. हम इन समस्याओं को ठीक करने की लंबे समय तक कोशिश करते हैं.
बिल्ड सिस्टम का दूसरा लक्ष्य, उपयोगकर्ताओं की क्षमता बढ़ाना है. साथ ही, हम इस बात की सीमाएं भी हमेशा के लिए बढ़ा रहे हैं कि एक्ज़ीक्यूट करने से जुड़ी किसी भी सेवा के लिए मौजूदा मशीन ऐलोकेशन में क्या किया जा सकता है. अगर रिमोट निष्पादन सेवा ओवरलोड हो जाती है, तो कोई भी काम पूरा नहीं कर सकता है.
इस्तेमाल में आसानी. हम {0/}
इंतज़ार के समय की वजह से, बिल्ड शुरू होने से लेकर उसकी उम्मीद के मुताबिक नतीजे तक पहुंचने में लगने वाले समय की जानकारी मिलती है. चाहे वह टेस्ट, पास होने या न होने वाली जांच का लॉग हो या गड़बड़ी का मैसेज हो कि BUILD
फ़ाइल में टाइपिंग की कोई गलती तो नहीं है.
ध्यान दें कि ये लक्ष्य अक्सर ओवरलैप होते हैं; इंतज़ार का समय, रिमोट असाइनमेंट सेवा के थ्रूपुट फ़ंक्शन का एक फ़ंक्शन है, क्योंकि यह इस्तेमाल में आसानी के लिए सही है.
बड़े पैमाने पर डेटा स्टोर करने की जगह
बिल्ड सिस्टम को बड़े डेटा स्टोर करने की जगह के स्केल पर काम करना होता है, जहां बड़े पैमाने पर होने का मतलब है कि वह किसी एक हार्ड ड्राइव पर फ़िट नहीं होती. इसलिए, सभी डेवलपर मशीनों पर उसका पूरा चेकआउट नहीं किया जा सकता. मध्यम-आकार के बिल्ड को
लाखों BUILD
फ़ाइलों को पढ़ने और पार्स करने की आवश्यकता होगी, और
लाखों ग्लोब का मूल्यांकन करने की. सैद्धांतिक तौर पर, एक मशीन पर सभी BUILD
फ़ाइलों को पढ़ा जा सकता है. हालांकि, हम अब भी सही समय और मेमोरी में ऐसा नहीं कर पाए हैं. इसी तरह, यह ज़रूरी है कि BUILD
फ़ाइलों को
अलग से लोड और पार्स किया जाए.
बिल्ड-जैसी विवरण भाषा
इस संदर्भ में, हम ऐसा कॉन्फ़िगरेशन भाषा मानते हैं जो लाइब्रेरी और बाइनरी नियमों के साथ-साथ, उनकी डिपेंडेंसी में दी गई BUILD
फ़ाइलों से करीब मिलती-जुलती है. BUILD
फ़ाइलों को अलग से पढ़ा और पार्स किया जा सकता है.
साथ ही, जब भी हो सके, सोर्स फ़ाइलों को देखने से भी बचा जा सकता है.
ऐतिहासिक
Bazel के उन वर्शन के बीच अंतर हैं जिनकी वजह से चुनौतियां आती हैं. इनमें से कुछ वर्शन की जानकारी नीचे दिए गए सेक्शन में दी गई है.
कॉन्टेंट लोड होने, विश्लेषण करने, और उसे एक्ज़ीक्यूट करने के बीच का अंतर अलग-अलग होने के बावजूद, एपीआई पर असर पड़ा है
तकनीकी रूप से, किसी नियम के लिए कार्रवाई को कहीं दूर ले जाने से पहले, काफ़ी कुछ बताना ज़रूरी होता है. हालांकि, मूल Bazel कोड बेस में पैकेज को सख्ती से अलग किया गया था, फिर कॉन्फ़िगरेशन (कमांड-लाइन फ़्लैग) का इस्तेमाल करके, नियमों का विश्लेषण करने के बाद ही कोई कार्रवाई की गई. यह नियम, अब भी एपीआई के नियमों का हिस्सा है. भले ही, Bazel के मुख्य फ़ंक्शन को इसकी ज़रूरत नहीं है. ज़्यादा जानकारी के लिए नीचे देखें.
इसका मतलब है कि नियम एपीआई के लिए, इंटरफ़ेस के बारे में यह जानकारी देना ज़रूरी है कि वह किस तरह का है, उसमें किस तरह के एट्रिब्यूट हैं. एपीआई को लोडिंग चरण के दौरान कस्टम कोड को चलाने की कुछ अपवाद हैं. इससे आउटपुट फ़ाइलों के इंप्लिसिट नाम और एट्रिब्यूट की इंप्लिसिट वैल्यू को कंप्यूट किया जा सकता है. उदाहरण के लिए, 'foo' नाम के एक java_library नियम से, 'libfoo.jam' नाम का आउटपुट जनरेट होता है. इसे बिल्ड ग्राफ़ में दूसरे नियमों से रेफ़र किया जा सकता है.
इसके अलावा, किसी नियम का विश्लेषण न तो कोई सोर्स फ़ाइल पढ़ सकता है और न ही किसी कार्रवाई के आउटपुट की जांच कर सकता है. इसके बजाय, इसे बनाने के चरण और आउटपुट फ़ाइल के नामों का एक ऐसा डायरेक्टिव जनरेट करना होता है जो सीधे तौर पर सिर्फ़ नियम और उसकी डिपेंडेंसी से तय हो.
स्वाभाविक
कुछ ऐसी सामान्य प्रॉपर्टी हैं जो लिखने के नियमों को चुनौती देती हैं और इनमें से कुछ सबसे सामान्य जानकारी के बारे में नीचे दिए गए सेक्शन में बताया गया है.
रिमोट तरीके से एक्ज़ीक्यूशन और कैश मेमोरी में सेव होना मुश्किल है
किसी एक मशीन पर बिल्ड चलाने की तुलना में, रिमोट एक्ज़ीक्यूट करने की प्रोसेस और कैश मेमोरी का इस्तेमाल करके, डेटा स्टोर करने की जगह को स्टोर करने के समय में करीब दो बार बदलाव किया जाता है. हालांकि, जिस पैमाने पर इसे करने की ज़रूरत है, वह चौंका देने वाली है: Google की रिमोट एक्ज़ीक्यूशन सेवा को इस तरह से डिज़ाइन किया गया है कि यह हर सेकंड में बहुत सारे अनुरोधों को हैंडल कर सकती है. साथ ही, प्रोटोकॉल सावधानी से ग़ैर-ज़रूरी दोतरफ़ा यात्राएं और सेवा में होने वाले ग़ैर-ज़रूरी काम से बचता है.
इस समय, प्रोटोकॉल के लिए आवश्यक है कि बिल्ड सिस्टम पहले से दी गई किसी कार्रवाई पर सभी इनपुट को जानता हो; बिल्ड सिस्टम फिर एक अद्वितीय कार्रवाई फ़िंगरप्रिंट की गणना करता है, और शेड्यूलर से कैश हिट मांगता है. कोई कैश हिट मिलने पर, शेड्यूलर आउटपुट फ़ाइलों के डाइजेस्ट के साथ जवाब देता है; फ़ाइलों को बाद में डाइजेस्ट से ठीक किया जाता है. हालांकि, यह Bazel के नियमों पर पाबंदियां लगाता है. ऐसे में, सभी इनपुट फ़ाइलों के बारे में पहले से एलान करना ज़रूरी होता है.
सही और तेज़ी से बढ़ाने वाले बिल्ड के लिए बदलाव की जानकारी का इस्तेमाल करने के लिए, कोडिंग के असामान्य पैटर्न की ज़रूरत होती है
ऊपर, हमने तर्क दिया कि सही करने के लिए, बैज़ल को बिल्ड चरण में जाने वाली सभी इनपुट फ़ाइलों के बारे में पता होना चाहिए, ताकि यह पता चल सके कि वह चरण अभी भी अप-टू-डेट है या नहीं. यह नियम, पैकेज लोड होने और नियम के विश्लेषण के लिए भी सही है. हमने इसे Skyframe के हिसाब से डिज़ाइन किया है. स्काईफ़्रेम एक ग्राफ़ लाइब्रेरी और आकलन फ़्रेमवर्क है जो लक्ष्य नोड (जैसे कि 'इन विकल्पों के साथ //foo बनाएं') को उसके मूल हिस्से में बांटता है. इसके बाद, उसके हिस्से का आकलन किया जाता है और उसे इकट्ठा करके इस नतीजे को हासिल किया जाता है. इस प्रोसेस के दौरान, Skyframe पैकेज पढ़ता है, नियमों का विश्लेषण करता है, और कार्रवाई करता है.
हर नोड पर, Skyframe ऐसे नोड को ट्रैक करता है जो किसी नोड को उसके आउटपुट की गणना करने के लिए इस्तेमाल करते हैं. इसमें लक्ष्य नोड से लेकर इनपुट फ़ाइलों (जो कि स्काईफ़्रेम नोड भी होते हैं) तक शामिल होते हैं. इस ग्राफ़ को मेमोरी में साफ़ तौर पर दिखाने से, बिल्ड सिस्टम यह पहचान कर पाता है कि इनपुट फ़ाइल में बदलाव (इसमें इनपुट फ़ाइल बनाना या उसे मिटाना शामिल है) से किन नोड पर असर पड़ता है. इससे, आउटपुट ट्री को सही स्थिति में लाने के लिए कम से कम काम करना पड़ता है.
इसके हिस्से के तौर पर, हर नोड एक डिपेंडेंसी डिस्कवरी प्रोसेस करता है. हर नोड, डिपेंडेंसी के बारे में जानकारी दे सकता है. साथ ही, उन डिपेंडेंसी के कॉन्टेंट का इस्तेमाल करके, आगे से किसी और डिपेंडेंसी का एलान कर सकता है. असल में, यह हर थ्रेड मॉडल के लिए थ्रेड को अच्छी तरह मैप करता है. हालांकि, मीडियम साइज़ के बिल्ड में सैकड़ों हज़ारों स्काईफ़्रेम नोड होते हैं. हालांकि, मौजूदा Java टेक्नोलॉजी की मदद से, इन्हें आसानी से ठीक नहीं किया जा सकता. साथ ही, अभी तक की वजह से, हम Java का इस्तेमाल कर रहे हैं, इसलिए न तो कोई थ्रेड संभव है और न ही लगातार काम जारी है.
इसके बजाय, Bazel एक तय साइज़ के थ्रेड पूल का इस्तेमाल करता है. हालांकि, इसका मतलब यह है कि अगर कोई नोड ऐसी डिपेंडेंसी के बारे में बताता है जो अब तक उपलब्ध नहीं है, तो हमें उस जांच को रद्द करके उसे फिर से शुरू करना पड़ सकता है. हालांकि, ऐसा तब हो सकता है, जब कोई डिपेंडेंसी उपलब्ध हो. इसका मतलब यह है कि नोड को ऐसा बार-बार नहीं करना चाहिए. ऐसी नोड जो N डिपेंडेंसी के बारे में बताती है उसे N बार बार-बार रीस्टार्ट किया जा सकता है. इससे, O(N^2) का समय बचता है. इसके बजाय, हम डिपेंडेंसी के लिए, पहले से बल्क में एलान करना चाहते हैं. इसके लिए, कभी-कभी कोड को फिर से व्यवस्थित करना पड़ता है या नोड को कई नोड में बांटना होता है, ताकि रीस्टार्ट की संख्या सीमित हो सके.
ध्यान दें कि यह तकनीक वर्तमान में नियम API में उपलब्ध नहीं है; इसके बजाय, लोडिंग, विश्लेषण और निष्पादन चरणों की लेगसी तकनीकों का उपयोग करके नियम API अब भी बताया जाता है. हालांकि, एक बुनियादी प्रतिबंध यह है कि अन्य नोड में होने वाले सभी ऐक्सेस को फ़्रेमवर्क से गुज़रना पड़ता है, ताकि वह इससे जुड़ी डिपेंडेंसी ट्रैक कर सके. चाहे किसी भी भाषा में बिल्डिंग सिस्टम लागू किया गया हो या जिसमें नियम लिखे गए हों (ज़रूरी नहीं कि ये एक ही हों), नियम लिखने वालों को Skyframe को बायपास करने वाले स्टैंडर्ड लाइब्रेरी या पैटर्न का इस्तेमाल नहीं करना चाहिए. Java के लिए, इसका मतलब है कि java.io.File और साथ ही किसी भी तरह के रिफ़्लेक्शन और किसी भी लाइब्रेरी से बचना. कम लेवल के इन इंटरफ़ेस पर डिपेंडेंसी के लिए काम करने वाली लाइब्रेरी को अब भी Skyframe के लिए सही तरीके से सेट अप करना होगा.
हमारा सुझाव है कि नियम के लेखकों को पहली बार में ही पूरी भाषा में रनटाइम के बारे में न बताएं. अनजाने में ऐसे एपीआई के इस्तेमाल का खतरा बहुत बड़ा होता है. पहले, कई बेज़ल बग असुरक्षित एपीआई का इस्तेमाल करने वाले नियमों की वजह से होते थे. हालांकि, नियमों को Bazel टीम या अन्य डोमेन विशेषज्ञों ने लिखा था.
क्वाड्रेटिक समय और मेमोरी खर्च करने से बचना मुश्किल है
स्काईफ़्रेम से लागू की गई शर्तों, Java इस्तेमाल करने की पुरानी पाबंदियों, और एपीआई के पुराने होने के अलावा, मामलों को और खराब करने के लिए, लाइब्रेरी और बाइनरी नियमों के आधार पर, किसी भी बिल्ड सिस्टम में बुनियादी तौर पर क्वाड्रेटिक टाइम या मेमोरी के इस्तेमाल की बुनियादी समस्या आती है. ऐसे दो बहुत ही सामान्य पैटर्न हैं जो क्वाड्रेटिक मेमोरी के इस्तेमाल की जानकारी देते हैं (और इसलिए क्वाड्रेटिक समय का इस्तेमाल).
लाइब्रेरी के नियमों की चेन - लाइब्रेरी के नियम चेन के उदाहरण पर विचार करें A, B के मुताबिक, C पर निर्भर करता है. इसके बाद, हम इन नियमों के ट्रांज़िट के दौरान कुछ प्रॉपर्टी का हिसाब लगाना चाहते हैं. जैसे, Java रनटाइम क्लास पाथ या हर लाइब्रेरी के लिए C++ लिंक करने वाला निर्देश. याद रखें, हम स्टैंडर्ड सूची लागू कर सकते हैं; हालांकि, यह पहले से ही क्वाड्रेटिक मेमोरी इस्तेमाल करता है: पहली लाइब्रेरी में, क्लासपाथ पर एक एंट्री, दूसरी दो, तीसरे तीन वगैरह, कुल 1+2+3+...+N = O(N^2) एंट्री होती है.
एक ही लाइब्रेरी के नियमों पर निर्भर बाइनरी नियम - उस मामले पर विचार करें जिसमें एक ही लाइब्रेरी के नियमों पर निर्भर बाइनरी का सेट हो. जैसे, अगर आपके पास जांच के लिए कई नियम हैं जो एक ही लाइब्रेरी कोड की जांच करते हैं. मान लें कि N नियमों में से, आधे नियम बाइनरी नियम हैं, और अन्य आधे लाइब्रेरी नियम हैं. अब ध्यान दें कि हर बाइनरी लाइब्रेरी के कुछ समय के लिए बंद होने के समय का हिसाब लगाने के लिए, कुछ प्रॉपर्टी की कॉपी बनाती है. जैसे, Java रनटाइम क्लासपाथ या लिंक करने के लिए C++ कमांड लाइन. उदाहरण के लिए, यह C++ लिंक कार्रवाई के कमांड लाइन स्ट्रिंग प्रदर्शन को विस्तृत कर सकता है. N/2 एलिमेंट की N/2 कॉपी {0}O(N^2) मेमोरी हैं.
क्वाड्रेटिक कॉम्प्लेक्सिटी से बचने के लिए, कस्टम कलेक्शन क्लास
इन दो स्थितियों से बेज़ल बहुत प्रभावित होता है, इसलिए हमने कस्टम संग्रह कक्षाओं का एक सेट शुरू किया जो हर चरण में कॉपी से बचते हुए मेमोरी में जानकारी को प्रभावी रूप से कंप्रेस करता है. इनमें से करीब-करीब सभी डेटा स्ट्रक्चर ने सिमेंटिक सेट किया है, इसलिए हमने इसे
depset
कहा है. इसे इंटरनल इंप्लिमेंटेशन में NestedSet
भी कहा जाता है. पिछले कुछ सालों में Bazel की मेमोरी का कम इस्तेमाल करने के लिए, ज़्यादातर बदलावों को 'डिपेट' के इस्तेमाल के बजाय, पहले के मुकाबले कम इस्तेमाल किया गया है.
माफ़ करें, डिपसेट का इस्तेमाल करने से सभी समस्याएं अपने-आप हल नहीं होतीं. खास तौर पर, हर नियम में गिरावट का बार-बार इस्तेमाल करने पर भी, चतुर्भुज समय का इस्तेमाल फिर से होने लगता है. अंदरूनी तौर पर, NestedSets के पास संग्रह की सामान्य कक्षाओं के साथ इंटरऑपरेबिलिटी (दूसरे सिस्टम के साथ काम करना) की सुविधा देने के लिए कुछ मददगार तरीके भी हैं; माफ़ करें, गलत तरीके से NestedSet को इनमें से किसी एक तरीके से पास करने से यह तरीका कॉपी हो जाता है और क्वाड्रेटिक मेमोरी का इस्तेमाल फिर से शुरू हो जाता है.