लेखन के नियमों की चुनौतियां

संग्रह की मदद से व्यवस्थित रहें अपनी प्राथमिकताओं के आधार पर, कॉन्टेंट को सेव करें और कैटगरी में बांटें.
किसी समस्या की शिकायत करें स्रोत देखें

इस पेज पर, बेज़ल के बेहतर नियमों को लिखने और उनसे जुड़ी समस्याओं के बारे में खास जानकारी मिलती है.

खास जानकारी के लिए ज़रूरी शर्तें

  • अनुमान: सही तरीके से काम करने, ज़्यादा जानकारी देने, आसान इस्तेमाल, और इंतज़ार के समय का लक्ष्य
  • अनुमान: बड़े पैमाने पर डेटा संग्रह स्थान
  • अनुमान: बिल्ड जैसी विवरण भाषा
  • ऐतिहासिक: लोडिंग, विश्लेषण, और एक्ज़ीक्यूशन के बीच का अलग-अलग सेगमेंट पुराना हो गया है, लेकिन यह अब भी एपीआई पर असर डालता है
  • स्वाभाविक: दूरस्थ निष्पादन और कैश मेमोरी में मुश्किल
  • स्वाभाविक: सही और तेज़ इंक्रीमेंटल बिल्ड के लिए बदलाव जानकारी का इस्तेमाल करने के लिए असामान्य कोडिंग पैटर्न की ज़रूरत होती है
  • स्वाभाविक: क्वाड्रेटिक समय और मेमोरी का इस्तेमाल करना मुश्किल होता है

अनुमान

यहां बिल्ड सिस्टम के बारे में कुछ अनुमान लगाए गए हैं, जैसे कि सुधार करने की ज़रूरत, इस्तेमाल में आसानी, थ्रूपुट, और बड़े पैमाने पर डेटा स्टोर करने की जगह. नीचे दिए गए सेक्शन में इन अनुमानों और ऑफ़र के दिशा-निर्देशों का पालन किया जाता है, ताकि यह पक्का किया जा सके कि नियम सही तरीके से लिखे गए हैं.

सही तरीके से काम करने, आसानी से इस्तेमाल करने में आसान, और इंतज़ार के समय का मकसद बनाएं

हम मानते हैं कि इंक्रीमेंटल बिल्ड का सम्मान करने के लिए, बिल्ड सिस्टम सबसे पहले और सबसे सही होना चाहिए. किसी दिए गए सोर्स ट्री के लिए, बिल्ड का आउटपुट हमेशा एक जैसा होना चाहिए, भले ही आउटपुट ट्री कुछ भी दिखता हो. पहले अनुमान में, इसका मतलब है कि बेज़ैल को हर एक इनपुट के बारे में जानना होगा, जो दिए गए बिल्ड चरण में जाता है. जैसे कि अगर कोई इनपुट बदलता है, तो उसे उस चरण को फिर से चलाया जा सकता है. इस बात की सीमाएं हैं कि Bazel को कैसे सही जानकारी मिल सकती है. ऐसा इसलिए होता है, क्योंकि यह बिल्ड की तारीख / समय जैसी कुछ जानकारी लीक करता है. साथ ही, फ़ाइल एट्रिब्यूट में बदलाव जैसे कुछ खास तरह के बदलावों को नज़रअंदाज़ कर देता है. सैंडबॉक्सिंग, तय की गई इनपुट फ़ाइलों को पढ़ने से रोकता है और उनमें सुधार करता है. सिस्टम की आंतरिक सीमाओं के अलावा, कुछ सुधार की समस्याएं भी होती हैं जिनमें से ज़्यादातर समस्या फ़ाइलसेट या C++ नियमों से जुड़ी होती हैं, जो दोनों मुश्किल समस्याएं होती हैं. हम इन्हें ठीक करने की लंबे समय से कोशिश कर रहे हैं.

बिल्ड सिस्टम का दूसरा लक्ष्य अच्छी तरह से काम करना है; हम रिमोट तरीके से किसी मशीन का इस्तेमाल करने के लिए, मौजूदा मशीन से किए जा सकने वाले कामों की सीमाएं पार कर रहे हैं. अगर रिमोट एक्ज़ीक्यूशन सेवा ओवरलोड हो जाती है, तो कोई भी काम पूरा नहीं कर सकता.

इसके बाद, इस्तेमाल में आसानी होगी. रिमोट एक्ज़ीक्यूशन सेवा के एक जैसे (या मिलते-जुलते) फ़ुटप्रिंट के लिए कई सही तरीकों के साथ, हम उस तरीके को चुनते हैं जो इस्तेमाल में सबसे आसान है.

इंतज़ार के समय से यह पता चलता है कि बिल्ड शुरू होने से लेकर उम्मीद के मुताबिक नतीजे पाने तक में कितना समय लगता है, चाहे वह पास हुए टेस्ट की जांच या विफल टेस्ट से मिला हो या BUILD फ़ाइल में टाइपिंग की कोई गड़बड़ी हो.

ध्यान दें कि ये लक्ष्य अक्सर ओवरलैप होते हैं. इंतज़ार का समय, उतना ही सही है जितना दूर से इस्तेमाल करने की सेवा की क्षमता है.

बड़े पैमाने पर डेटा स्टोर करने की जगह

बिल्ड सिस्टम को रिपॉज़िटरी के उस बड़े पैमाने पर काम करना पड़ता है जहां बड़े साइज़ का मतलब है कि वह एक हार्ड ड्राइव पर फ़िट नहीं होता. इसलिए, सभी डेवलपर मशीनों पर पूरी तरह से चेकआउट नहीं किया जा सकता. मीडियम साइज़ के बिल्ड को हज़ारों BUILD फ़ाइलों को पढ़कर पार्स करना होगा और हज़ारों ग्लोब का मूल्यांकन करना होगा. हालांकि, सैद्धांतिक तौर पर सभी BUILD फ़ाइलों को एक ही मशीन पर पढ़ा जा सकता है. हालांकि, अब तक हम ऐसा करने के लिए, काफ़ी समय और मेमोरी में बात नहीं कर पा रहे हैं. इसलिए, यह ज़रूरी है कि BUILD फ़ाइलों को अलग से लोड और पार्स किया जाए.

बिल्ड जैसी विवरण भाषा

इस संदर्भ में, हम कॉन्फ़िगरेशन की एक ऐसी भाषा का इस्तेमाल करते हैं जो लाइब्रेरी और बाइनरी नियमों के एलान में दी गई BUILD फ़ाइलों और उनकी डिपेंडेंसी से काफ़ी हद तक मेल खाती है. BUILD फ़ाइलों को अलग से पढ़ा और पार्स किया जा सकता है. साथ ही, जब भी हो सके, तब हम सोर्स फ़ाइलों को देखने से बचते हैं (मौजूद नहीं है).

ऐतिहासिक

Bazel के अलग-अलग वर्शन में कई अंतर हैं. इनमें से कुछ वर्शन की जानकारी नीचे दिए गए सेक्शन में दी गई है.

पेज लोड होने, विश्लेषण करने, और उसे एक्ज़ीक्यूट करने से जुड़ी अलग-अलग मेट्रिक पुरानी होती हैं. हालांकि, इसका एपीआई पर असर पड़ता है

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

इसका मतलब है कि नियम एपीआई को नियम के इंटरफ़ेस के बारे में एलान करने की ज़रूरत होती है. इसमें यह भी बताया जाता है कि नियम में क्या एट्रिब्यूट हैं, किस तरह के एट्रिब्यूट हैं. एपीआई अपवाद के तौर पर, कुछ ऐसे अपवाद हैं जहां लोड होने के दौरान कस्टम कोड को आउटपुट फ़ाइलों के इंप्लिसिट नाम और एट्रिब्यूट की इंप्लिसिट वैल्यू को चलाने की अनुमति मिलती है. उदाहरण के लिए, 'foo' नाम का एक java_library नियम पूरी तरह से 'libfoo.java' नाम का एक आउटपुट जनरेट करता है. इसे बिल्ड ग्राफ़ में दूसरे नियमों से देखा जा सकता है.

इसके अलावा, किसी नियम का विश्लेषण किसी भी सोर्स फ़ाइल को नहीं पढ़ सकता या किसी कार्रवाई के आउटपुट की जांच नहीं कर सकता. इसके बजाय, उसे बिल्ड के चरणों और आउटपुट फ़ाइल के नामों का एक ऐसा डाइरेक्टिव बायपार्टेट ग्राफ़ जनरेट करना होगा जो सिर्फ़ नियम और उसकी डिपेंडेंसी से तय होता है.

स्वाभाविक

कुछ ऐसी सामान्य प्रॉपर्टी हैं जो लिखने के नियमों को चुनौती देने लायक बनाती हैं और इनमें से कुछ सबसे सामान्य प्रॉपर्टी के बारे में नीचे दिए गए सेक्शन में बताया गया है.

रिमोट तरीके से एक्ज़ीक्यूशन और कैश मेमोरी बनाना मुश्किल होता है

एक ही मशीन पर बिल्ड चलाने के बजाय, उसका रखरखाव करने के लिए बड़ी दूरी पर कई जगहों पर मौजूद होते हैं. हालांकि, जिस पैमाने पर इसे करने की ज़रूरत होती है वह है: Google की रिमोट एक्ज़ीक्यूशन सेवा को बड़ी संख्या में अनुरोधों को हर सेकेंड मैनेज करने के लिए डिज़ाइन किया गया है. साथ ही, प्रोटोकॉल सावधानी से ग़ैर-ज़रूरी ट्रिप्स और सेवा के गैर-ज़रूरी काम से बचाता है.

इस समय, प्रोटोकॉल के लिए यह ज़रूरी है कि बिल्ड सिस्टम को दी गई कार्रवाई के बारे में सभी इनपुट पहले से पता हों; इसके बाद, बिल्ड सिस्टम एक यूनीक ऐक्शन फ़िंगरप्रिंट की गिनती करता है और शेड्यूलर से कैश मेमोरी को हिट करने के लिए कहता है. अगर कैश मेमोरी मिलती है, तो शेड्यूलर आउटपुट फ़ाइलों के डाइजेस्ट के साथ जवाब देता है; फ़ाइलों को बाद में डाइजेस्ट से बढ़ाया जाता है. हालांकि, यह बेज़ल के नियमों पर पाबंदी लगाता है. ऐसे में, यह ज़रूरी है कि समय से पहले सभी इनपुट फ़ाइलों का एलान कर दिया जाए.

सही और तेज़ इंक्रीमेंटल बिल्ड के लिए बदलाव से जुड़ी जानकारी का इस्तेमाल करने के लिए, असामान्य कोडिंग पैटर्न की ज़रूरत होती है

ऊपर, हमने तर्क दिया था कि सही करने के लिए, बेज़ल को बिल्ड चरण में जाने वाली सभी इनपुट फ़ाइलों के बारे में पता लगाना चाहिए, ताकि यह पता लगाया जा सके कि वह बिल्ड चरण अब भी अप-टू-डेट है या नहीं. पैकेज लोड करने और नियम का विश्लेषण करने पर भी यही बात लागू होती है. हमने आम तौर पर Skyframe को इस तरह डिज़ाइन किया है जिससे कि इसे आम तौर पर इस्तेमाल किया जा सके. Skyframe एक ग्राफ़ लाइब्रेरी और आकलन फ़्रेमवर्क है, जो एक लक्ष्य नोड (जैसे कि 'इन विकल्पों के साथ //foo बनाएं') लेता है. साथ ही, इसे अपने मूल हिस्सों में बांट देता है, इसके बाद इस नतीजे को पाने के लिए उसका मूल्यांकन किया जाता है. इस प्रक्रिया के तहत, स्काईफ़्रेम पैकेज को पढ़ता है, नियमों का विश्लेषण करता है, और कार्रवाइयां करता है.

हर नोड में, स्काईफ़्रेम सटीक रूप से ट्रैक करता है कि किसी दिए गए नोड को अपने आउटपुट की गणना करने के लिए किस नोड का इस्तेमाल किया जाता है. यह पूरी तरह से लक्ष्य नोड से इनपुट फ़ाइलों तक (जो कि स्काईफ़्रेम नोड भी हैं) होता है. मेमोरी में इस ग्राफ़ को साफ़ तौर पर दिखाने से, बिल्ड सिस्टम को यह पता लगाने में मदद मिलती है कि इनपुट फ़ाइल में किए गए बदलाव (जिसमें किसी इनपुट फ़ाइल को बनाना या मिटाना शामिल है) से किन नोड पर असर पड़ता है. इस वजह से, आउटपुट ट्री को पहले जैसा करने के लिए बहुत कम काम करना पड़ता है.

इसके तहत, हर नोड एक डिपेंडेंसी खोज प्रोसेस करता है. हर नोड डिपेंडेंसी का एलान कर सकता है और फिर उन डिपेंडेंसी का कॉन्टेंट इस्तेमाल कर सकता है. सिद्धांत में, यह थ्रेड-प्रति-नोड मॉडल पर अच्छी तरह मैप होता है. हालांकि, मध्यम आकार के बिल्ड में लाखों हज़ारों Skyframe नोड होते हैं, जो मौजूदा Java तकनीक के ज़रिए आसानी से संभव नहीं है (और ऐतिहासिक वजहों से, हम फ़िलहाल Java का इस्तेमाल कर रहे हैं, इसलिए कोई सस्ता थ्रेड नहीं है या लगातार नहीं हो रहा है).

इसके बजाय, बेज़ल एक निश्चित साइज़ के थ्रेड पूल का इस्तेमाल करता है. हालांकि, इसका मतलब यह है कि अगर कोई नोड ऐसी डिपेंडेंसी के बारे में बताता है जो अभी उपलब्ध नहीं है, तो हमें उस आकलन को रद्द करके फिर से शुरू करना पड़ सकता है (शायद उसे किसी दूसरी थ्रेड में), जब डिपेंडेंसी उपलब्ध हो जाए. इसका मतलब यह है कि नोड को बहुत ज़्यादा बार ऐसा नहीं करना चाहिए. ऐसी नोड जो लगातार डिपेंडेंसी बताती है, वह N बार फिर से शुरू हो सकती है. इसकी वजह से, O(N^2) समय लगेगा. इसके बजाय, हम पूरी तरह से डिपेंडेंसी का एलान करते हैं. इसके लिए, कभी-कभी कोड को फिर से व्यवस्थित करना पड़ सकता है या रीस्टार्ट करने की संख्या को सीमित करने के लिए, किसी नोड को कई नोड में बांटना पड़ सकता है.

ध्यान दें कि यह टेक्नोलॉजी फ़िलहाल नियमों के एपीआई में उपलब्ध नहीं है; इसके बजाय, लोड करने, विश्लेषण करने, और एक्ज़ीक्यूशन के चरणों के लेगसी कॉन्सेप्ट का इस्तेमाल करके, नियमों का एपीआई अब भी तय किया गया है. हालांकि, बुनियादी पाबंदी यह है कि अन्य नोड से ऐक्सेस करने के लिए, सभी फ़्रेमवर्क को पास करना होगा, ताकि इससे जुड़ी डिपेंडेंसी ट्रैक की जा सकें. चाहे जिस भाषा में बिल्ड सिस्टम लागू किया गया हो या जिसमें नियम लिखे गए हों (उनका एक जैसा होना ज़रूरी नहीं है) नियम के लेखकों को स्टैंडर्ड लाइब्रेरी या पैटर्न का इस्तेमाल नहीं करना चाहिए जो स्काईफ़्रेम को बायपास करते हैं. Java के लिए, इसका मतलब है कि java.io.File के साथ-साथ किसी भी तरह के रिफ़्लेक्शन और किसी भी लाइब्रेरी से बचना. ऐसे लाइब्रेरी जो इन लो-लेवल इंटरफ़ेस के डिपेंडेंसी के साथ काम करती हैं, उन्हें अब भी Skyframe के लिए सही से सेट अप करना होगा.

यह सुझाव देता है कि नियम के लेखकों को पहली बार में पूरी भाषा में रनटाइम से बचें. इस तरह के एपीआई के अनजाने में इस्तेमाल होने का खतरा बहुत ज़्यादा होता है - पहले, बेज़ल की समस्याओं की वजह से असुरक्षित एपीआई का इस्तेमाल किया जाता था. हालांकि, नियमों को बेज़ल टीम या डोमेन के अन्य विशेषज्ञों ने लिखा था.

क्वाड्रेटिक समय और मेमोरी का इस्तेमाल करने से बचना मुश्किल है

स्काईफ़्रेम की लागू की गई ज़रूरतों, Java का इस्तेमाल करने की पुरानी सीमाओं, और नियमों वाले एपीआई के पुराने होने के अलावा, मामलों को बदतर करना, लाइब्रेरी और बाइनरी नियमों के आधार पर किसी भी बिल्ड सिस्टम में गलती से क्वाड्रेटिक समय या मेमोरी का इस्तेमाल शुरू करना बुनियादी समस्या है. ऐसे दो बहुत ही सामान्य पैटर्न हैं, जो क्वाड्रेटिक स्टोरेज का इस्तेमाल करते हैं (इसलिए, क्वाड्रेटिक समय का इस्तेमाल किया जाता है).

  1. लाइब्रेरी के नियमों की चेन - लाइब्रेरी के नियम की चेन के मामले में A, B पर निर्भर करता है, जो C पर निर्भर करता है. फिर, हम जावा रनटाइम क्लासपाथ या हर लाइब्रेरी के C++ लिंकर निर्देश जैसे इन नियमों के ट्रांज़िशनल बंद होने पर कुछ प्रॉपर्टी की गिनती करना चाहते हैं. दरअसल, हम स्टैंडर्ड सूची लागू कर सकते हैं. हालांकि, इसमें पहले से ही क्वाड्रेटिक स्टोरेज का इस्तेमाल किया जा रहा है: पहली लाइब्रेरी में क्लासपाथ पर एक एंट्री, दूसरे दो, तीसरे तीन, और जैसे कि कुल 1+2+3+...+N = O(N^2) एंट्री होती हैं.

  2. एक ही लाइब्रेरी के नियमों पर निर्भर बाइनरी नियम - उस मामले पर विचार करें जहां एक ही लाइब्रेरी के नियमों पर निर्भर बाइनरी का सेट होता है — जैसे कि आपके पास एक ही लाइब्रेरी के कोड की जांच करने वाले टेस्ट के कई नियम हों. मान लीजिए कि N नियमों में से, आधे नियम बाइनरी नियम हैं और दूसरे आधे नियम उनके आधे नियम हैं. अब ध्यान दें कि हर बाइनरी लाइब्रेरी के ट्रांज़िशनल बंद होने पर कुछ प्रॉपर्टी की कॉपी बनाती है. जैसे, Java रनटाइम क्लासपाथ या C++ लिंकर कमांड लाइन. उदाहरण के लिए, C++ लिंक की कार्रवाई को कमांड लाइन स्ट्रिंग के तौर पर दिखाया जा सकता है. N/2 एलिमेंट की N/2 कॉपी एक O(N^2) मेमोरी है.

कस्टम कलेक्शन की क्लास

बेज़ल पर इन दोनों स्थितियों का बहुत ज़्यादा असर पड़ा है. इसलिए, हमने कस्टम कलेक्शन क्लास का एक सेट पेश किया है. इसमें, हर चरण में कॉपी से बचने के साथ, स्टोरेज में मौजूद जानकारी को कंप्रेस किया जाता है. इनमें से करीब सभी डेटा स्ट्रक्चर के लिए सिमैंटिक सेट किया गया है, इसलिए हमने इसे डिप्सेट कहा है. इसे इंटरनल इंप्लिमेंटेशन में NestedSet भी कहा जाता है. पिछले कई सालों में, Bzel की मेमोरी की खपत को कम करने के लिए ज़्यादातर बदलाव, पहले इस्तेमाल किए गए स्टोरेज के बजाय डिपेक्ट का इस्तेमाल करने में हुए बदलाव थे.

माफ़ करें, डेप्सेट के इस्तेमाल से सभी समस्याएं अपने-आप हल नहीं होती हैं. खास तौर पर, हर नियम में गिरावट आने पर, बार-बार कम खपत होती है. अंदरूनी तौर पर, नेस्ट किए गए सेट में भी हेल्पर के कुछ तरीके होते हैं, जो सामान्य कलेक्शन क्लास के साथ इंटरऑपरेबिलिटी (दूसरे सिस्टम के साथ काम करना) की सुविधा देते हैं. माफ़ करें, इनमें से किसी भी तरीके से NestedSet को पास करने से व्यवहार कॉपी हो जाता है और क्वाड्रेटिक मेमोरी का इस्तेमाल फिर से शुरू हो जाता है.