बड़े कोडबेस में, डिपेंडेंसी की चेन बहुत लंबी हो सकती है. आम तौर पर, आसान बाइनरी भी 10 हज़ार बिल्ड टारगेट पर निर्भर हो सकती हैं. इस स्केल पर, किसी एक मशीन पर तय समय में बिल्ड पूरा करना असंभव है: कोई भी बिल्ड सिस्टम, मशीन के हार्डवेयर पर लागू होने वाले फ़िज़िक्स के बुनियादी नियमों को पूरा नहीं कर सकता. इसे काम करने के लिए, डिस्ट्रिब्यूट किए गए बिल्ड के साथ काम करने वाले बिल्ड सिस्टम का इस्तेमाल करना ही एकमात्र तरीका है. इसमें, सिस्टम के ज़रिए किए जा रहे काम की इकाइयां, मनमुताबिक और स्केल की जा सकने वाली मशीनों पर फैली होती हैं. मान लें कि हमने सिस्टम के काम को छोटी-छोटी यूनिट में बांट दिया है (इस बारे में बाद में ज़्यादा जानकारी दी जाएगी). इससे, हम किसी भी साइज़ के बिल्ड को उतनी ही जल्दी पूरा कर पाएंगे जितना जल्दी हम इसके लिए पैसे चुकाने को तैयार हैं. हम आर्टफ़ैक्ट पर आधारित बिल्ड सिस्टम को तय करके, इस स्केलेबिलिटी पर काम कर रहे हैं.
रिमोट कैश मेमोरी
डिस्ट्रिब्यूटेड बिल्ड का सबसे आसान टाइप वह है जो सिर्फ़ रिमोट कैश मेमोरी का फ़ायदा उठाता है. इसे पहली इमेज में दिखाया गया है.
पहली इमेज. रिमोट कैश मेमोरी में सेव करने की सुविधा दिखाने वाला डिस्ट्रिब्यूट किया गया बिल्ड
डेवलपर वर्कस्टेशन और निरंतर इंटिग्रेशन सिस्टम, दोनों में मौजूद हर सिस्टम, एक ही रिमोट कैश मेमोरी सेवा का रेफ़रंस शेयर करता है. यह सेवा, Redis जैसा तेज़ और लोकल स्टोरेज सिस्टम हो सकता है. इसके अलावा, यह Google Cloud Storage जैसी क्लाउड सेवा भी हो सकती है. जब भी किसी उपयोगकर्ता को सीधे तौर पर या डिपेंडेंसी के तौर पर आर्टफ़ैक्ट बनाने की ज़रूरत होती है, तो सिस्टम सबसे पहले रिमोट कैश मेमोरी में यह जांच करता है कि वह आर्टफ़ैक्ट पहले से मौजूद है या नहीं. अगर ऐसा है, तो यह आर्टफ़ैक्ट बनाने के बजाय, उसे डाउनलोड कर सकता है. अगर ऐसा नहीं होता है, तो सिस्टम खुद आर्टफ़ैक्ट बनाता है और नतीजे को कैश मेमोरी में वापस अपलोड करता है. इसका मतलब है कि अक्सर न बदलने वाली लो-लेवल डिपेंडेंसी को एक बार बनाया जा सकता है और सभी उपयोगकर्ताओं के साथ शेयर किया जा सकता है. इसके बजाय, हर उपयोगकर्ता को इसे फिर से बनाना पड़ता है. Google पर, कई आर्टफ़ैक्ट को फिर से बनाने के बजाय, कैश मेमोरी से दिखाया जाता है. इससे हमारे बिल्ड सिस्टम को चलाने की लागत काफ़ी कम हो जाती है.
रिमोट कैश मेमोरी सिस्टम के काम करने के लिए, बिल्ड सिस्टम को यह पक्का करना होगा कि बिल्ड को फिर से बनाया जा सकता है. इसका मतलब है कि किसी भी बिल्ड टारगेट के लिए, उस टारगेट के इनपुट सेट को तय करना संभव होना चाहिए, ताकि किसी भी मशीन पर एक ही इनपुट सेट से एक ही आउटपुट मिले. यह पक्का करने का सिर्फ़ एक तरीका है कि किसी आर्टफ़ैक्ट को डाउनलोड करने के नतीजे, उसे खुद बनाने के नतीजों से मेल खाते हैं. ध्यान दें कि इसके लिए ज़रूरी है कि कैश मेमोरी में मौजूद हर आर्टफ़ैक्ट को उसके टारगेट और इनपुट के हैश, दोनों पर आधारित किया जाए. ऐसा करने से, अलग-अलग इंजीनियर एक ही समय पर एक ही टारगेट में अलग-अलग बदलाव कर सकते हैं. साथ ही, रिमोट कैश मेमोरी, इन बदलावों के बाद बने सभी आर्टफ़ैक्ट को सेव कर लेगी और बिना किसी रुकावट के उन्हें सही तरीके से दिखाएगी.
बेशक, रिमोट कैश मेमोरी से कोई फ़ायदा पाने के लिए, आर्टफ़ैक्ट को बनाने के मुकाबले डाउनलोड करने में कम समय लगना चाहिए. हालांकि, ऐसा हमेशा नहीं होता. खास तौर पर, अगर कैश मेमोरी सर्वर, बिल्ड करने वाली मशीन से दूर है. Google के नेटवर्क और बिल्ड सिस्टम को ध्यान से ट्यून किया गया है, ताकि बिल्ड के नतीजे तुरंत शेयर किए जा सकें.
रिमोट से चलाना
रिमोट कैश मेमोरी, डिस्ट्रिब्यूटेड बिल्ड नहीं है. अगर कैश मेमोरी मिट जाती है या कोई ऐसा बदलाव किया जाता है जिसकी वजह से पूरी प्रोसेस को फिर से शुरू करना पड़ता है, तो भी आपको अपनी मशीन पर पूरी प्रोसेस को फिर से शुरू करना होगा. इसका असल मकसद, रिमोट तौर पर प्रोसेस को पूरा करने की सुविधा देना है. इसमें, बिल्ड करने का असल काम, जितने चाहें उतने वर्कर्स के बीच बांटा जा सकता है. दूसरे चित्र में, रिमोट एक्ज़ीक्यूशन सिस्टम दिखाया गया है.
दूसरी इमेज. रिमोट एक्ज़ीक्यूशन सिस्टम
हर उपयोगकर्ता की मशीन पर चलने वाला बिल्ड टूल (जहां उपयोगकर्ता, इंजीनियर या ऑटोमेटेड बिल्ड सिस्टम होते हैं), एक सेंट्रल बिल्ड मास्टर को रिक्वेस्ट भेजता है. बिल्ड मास्टर, अनुरोधों को उनके कॉम्पोनेंट ऐक्शन में बांटता है और उन ऐक्शन को वर्कर के स्केलेबल पूल पर लागू करने के लिए शेड्यूल करता है. हर वर्कर्स, उपयोगकर्ता के दिए गए इनपुट के हिसाब से कार्रवाइयां करता है और नतीजे के तौर पर आर्टफ़ैक्ट लिखता है. ये आर्टफ़ैक्ट, उन सभी मशीनों के साथ शेयर किए जाते हैं जिन पर ऐसी कार्रवाइयां की जा रही हैं जिनमें इन आर्टफ़ैक्ट की ज़रूरत होती है. ऐसा तब तक किया जाता है, जब तक कि आखिरी आउटपुट तैयार नहीं हो जाता और उपयोगकर्ता को भेजा नहीं जाता.
इस तरह के सिस्टम को लागू करने में सबसे मुश्किल काम, वर्कर्स, मास्टर्स, और उपयोगकर्ता की लोकल मशीन के बीच कम्यूनिकेशन मैनेज करना होता है. हो सकता है कि वर्कर्स, दूसरे वर्कर्स के बनाए गए इंटरमीडिएट आर्टफ़ैक्ट पर निर्भर हों. साथ ही, फ़ाइनल आउटपुट को उपयोगकर्ता की लोकल मशीन पर वापस भेजना ज़रूरी हो. इसके लिए, हम पहले बताए गए डिस्ट्रिब्यूटेड कैश के ऊपर, एक नया कैश बना सकते हैं. इसके लिए, हर वर्कर्स को कैश में अपने नतीजे लिखने और अपनी डिपेंडेंसी पढ़ने की अनुमति देनी होगी. जब तक वर्कर्स के लिए ज़रूरी सभी काम पूरे नहीं हो जाते, तब तक मास्टर उन्हें आगे बढ़ने से रोकता है. ऐसा होने पर, वे कैश मेमोरी से अपने इनपुट पढ़ पाएंगे. फ़ाइनल प्रॉडक्ट को कैश मेमोरी में भी सेव किया जाता है, ताकि लोकल मशीन उसे डाउनलोड कर सके. ध्यान दें कि हमें उपयोगकर्ता के सोर्स ट्री में किए गए स्थानीय बदलावों को एक्सपोर्ट करने के लिए, एक अलग तरीके की भी ज़रूरत है, ताकि वर्कर्स, बिल्ड करने से पहले उन बदलावों को लागू कर सकें.
इसके लिए, पहले बताए गए आर्टफ़ैक्ट पर आधारित बिल्ड सिस्टम के सभी हिस्सों को एक साथ काम करना होगा. बिल्ड एनवायरमेंट के बारे में पूरी जानकारी अपने-आप मिलनी चाहिए, ताकि हम बिना किसी मानवीय हस्तक्षेप के वर्कर्स को स्पिन अप कर सकें. बिल्ड प्रोसेस पूरी तरह से अपने-आप काम करनी चाहिए, क्योंकि हर चरण को किसी अलग मशीन पर चलाया जा सकता है. आउटपुट पूरी तरह से तय होने चाहिए, ताकि हर वर्कर दूसरे वर्कर से मिले नतीजों पर भरोसा कर सके. टास्क पर आधारित सिस्टम के लिए, ऐसी गारंटी देना काफ़ी मुश्किल होता है. इसलिए, एक के ऊपर भरोसेमंद रिमोट एक्सीक्यूशन सिस्टम बनाना काफ़ी मुश्किल होता है.
Google पर डिस्ट्रिब्यूट किए गए बिल्ड
Google, 2008 से डिस्ट्रिब्यूटेड बिल्ड सिस्टम का इस्तेमाल कर रहा है. इसमें रिमोट कैश मेमोरी और रिमोट तरीके से प्रोग्राम चलाने की सुविधाएं इस्तेमाल की जाती हैं. इन सुविधाओं के बारे में तीसरी इमेज में बताया गया है.
तीसरी इमेज. Google का डिस्ट्रिब्यूटेड बिल्ड सिस्टम
Google के रिमोट कैश मेमोरी को ObjFS कहा जाता है. इसमें एक बैकएंड होता है, जो बिगटेबल में बिल्ड आउटपुट को सेव करता है. ये आउटपुट, हमारी सभी प्रोडक्शन मशीनों पर डिस्ट्रिब्यूट किए जाते हैं. साथ ही, इसमें एक फ़्रंटएंड FUSE डेमन होता है, जिसका नाम objfsd होता है. यह हर डेवलपर की मशीन पर चलता है. FUSE डेमन की मदद से, इंजीनियर, बिल्ड आउटपुट को ऐसे ब्राउज़ कर सकते हैं जैसे वे वर्कस्टेशन पर सेव की गई सामान्य फ़ाइलें हों. हालांकि, फ़ाइल का कॉन्टेंट सिर्फ़ उन कुछ फ़ाइलों के लिए मांग पर डाउनलोड किया जाता है जिनका अनुरोध उपयोगकर्ता ने सीधे तौर पर किया हो. फ़ाइल कॉन्टेंट को मांग के हिसाब से उपलब्ध कराने से, नेटवर्क और डिस्क, दोनों का इस्तेमाल काफ़ी कम हो जाता है. साथ ही, सिस्टम को डेवलपर की लोकल डिस्क पर सभी बिल्ड आउटपुट सेव करने की तुलना में, दो गुना तेज़ी से बिल्ड करने में मदद मिलती है.
Google के रिमोट एक्ज़ीक्यूशन सिस्टम को Forge कहा जाता है. Blaze (Bazel का इंटरनल वर्शन) में एक Forge क्लाइंट होता है, जिसे डिस्ट्रिब्यूटर कहा जाता है. यह हमारे डेटासेंटर में चल रही हर कार्रवाई के लिए रिक्वेस्ट भेजता है. इस कार्रवाई को शेड्यूलर कहा जाता है. शेड्यूलर, कार्रवाई के नतीजों का कैश मेमोरी बनाए रखता है. इससे, अगर सिस्टम के किसी दूसरे उपयोगकर्ता ने पहले ही कार्रवाई कर ली है, तो शेड्यूलर तुरंत जवाब दे पाता है. अगर ऐसा नहीं होता है, तो यह कार्रवाई को सूची में डाल देता है. एक बड़ा पूल, इस सूची से लगातार कार्रवाइयां पढ़ता है, उन्हें लागू करता है, और नतीजों को सीधे ObjFS Bigtable में सेव करता है. ये नतीजे, आने वाले समय में की जाने वाली कार्रवाइयों के लिए, एग्ज़ीक्यूटर के लिए उपलब्ध होते हैं. इसके अलावा, इन्हें आखिरी उपयोगकर्ता, objfsd की मदद से डाउनलोड कर सकता है.
इसका नतीजा एक ऐसा सिस्टम है जो Google पर किए जाने वाले सभी बिल्ड के साथ बेहतर तरीके से काम करता है. Google के बिल्ड का स्केल बहुत बड़ा है: Google, लाखों टेस्ट केस को पूरा करने के लिए लाखों बिल्ड चलाता है. साथ ही, हर दिन सोर्स कोड की अरबों लाइनों से, बिल्ड के पेटाबाइट आउटपुट जनरेट करता है. इस तरह के सिस्टम की मदद से, हमारे इंजीनियर जटिल कोडबेस को तेज़ी से बना सकते हैं. साथ ही, हम अपने बिल्ड पर आधारित ऑटोमेटेड टूल और सिस्टम की एक बड़ी संख्या को लागू कर सकते हैं.