डिस्ट्रिब्यूट किए गए बिल्ड

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

रिमोट कैशिंग

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

रिमोट कैशिंग के साथ डिस्ट्रिब्यूटेड बिल्ड

पहली इमेज. रिमोट कैशिंग दिखाने वाला डिस्ट्रिब्यूटेड बिल्ड

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

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

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

रिमोट एक्ज़िक्यूशन

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

रिमोट एक्ज़िक्यूशन सिस्टम

दूसरी इमेज. रिमोट एक्ज़िक्यूशन सिस्टम

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

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

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

Google में डिस्ट्रिब्यूटेड बिल्ड

Google, 2008 से डिस्ट्रिब्यूटेड बिल्ड सिस्टम का इस्तेमाल कर रहा है. इसमें, रिमोट कैशिंग और रिमोट एक्ज़िक्यूशन, दोनों का इस्तेमाल किया जाता है. इसे तीसरी इमेज में दिखाया गया है.

हाई-लेवल बिल्ड सिस्टम

तीसरी इमेज. Google का डिस्ट्रिब्यूटेड बिल्ड सिस्टम

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

Google के रिमोट एक्ज़िक्यूशन सिस्टम को Forge कहा जाता है. Blaze (Bazel का इंटरनल इक्विवेलेंट) में मौजूद Forge क्लाइंट, जिसे डिस्ट्रिब्यूटर कहा जाता है, हर ऐक्शन के लिए हमारे डेटा सेंटर में चल रहे एक जॉब को अनुरोध भेजता है. इसे शेड्यूलर कहा जाता है. शेड्यूलर, ऐक्शन के नतीजों का कैश बनाए रखता है. इससे, अगर सिस्टम के किसी अन्य उपयोगकर्ता ने पहले ही वह ऐक्शन बना लिया है, तो वह तुरंत जवाब दे पाता है. अगर ऐसा नहीं है, तो वह ऐक्शन को एक क्यू में डाल देता है. एक्ज़िक्यूटर जॉब का एक बड़ा पूल, इस क्यू से लगातार ऐक्शन पढ़ता है, उन्हें एक्ज़िक्यूट करता है, और नतीजों को सीधे ObjFS Bigtable में सेव करता है. ये नतीजे, एक्ज़िक्यूटर के लिए आने वाले ऐक्शन के लिए उपलब्ध होते हैं. साथ ही, इन्हें objfsd के ज़रिए एंड यूज़र डाउनलोड कर सकता है.

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