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

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

रिमोट कैशिंग

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

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

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

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