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


