स्थायी कर्मचारी

7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

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

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

पर्सिस्टेंट वर्कर्स को कई भाषाओं के लिए लागू किया गया है. इनमें Java, Scala, Kotlin वगैरह शामिल हैं.

NodeJS रनटाइम का इस्तेमाल करने वाले प्रोग्राम, वर्कर प्रोटोकॉल को लागू करने के लिए, @bazel/worker हेल्पर लाइब्रेरी का इस्तेमाल कर सकते हैं.

पर्सिस्टेंट वर्कर का इस्तेमाल करना

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

bazel build //my:target --strategy=Javac=worker,local

लोकल रणनीति के बजाय वर्कर्स रणनीति का इस्तेमाल करने से, कॉम्पाइलेशन की स्पीड काफ़ी बढ़ सकती है. हालांकि, यह इस बात पर निर्भर करता है कि इसे कैसे लागू किया गया है. Java के लिए, बिल्ड 2 से 4 गुना तेज़ हो सकते हैं. कभी-कभी, इंक्रीमेंटल कंपाइलेशन के लिए ज़्यादा भी हो सकते हैं. वर्कर्स की मदद से, Bazel को इकट्ठा करने में करीब 2.5 गुना कम समय लगता है. ज़्यादा जानकारी के लिए, "कर्मचारियों की संख्या चुनना" सेक्शन देखें.

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

कर्मचारियों की संख्या चुनना

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

यह ग्राफ़ 64 जीबी रैम वाले 6-कोर हाइपर-थ्रेड वाले Intel Xeon 3.5 गीगाहर्ट्ज़ Linux वर्कस्टेशन पर, बेज़ल (टारगेट //src:bazel) के शुरू होने से शुरू होने वाले कंपाइलेशन समय को दिखाता है. हर वर्कर कॉन्फ़िगरेशन के लिए, पांच क्लीन बिल्ड चलाए जाते हैं और आखिरी चार का औसत निकाला जाता है.

क्लीन बिल्ड की परफ़ॉर्मेंस में हुए सुधारों का ग्राफ़

पहला डायग्राम. क्लीन बिल्ड के परफ़ॉर्मेंस में सुधार का ग्राफ़.

इस कॉन्फ़िगरेशन के लिए, दो वर्कर सबसे तेज़ कंपाइलेशन देते हैं. हालांकि, एक वर्कर की तुलना में सिर्फ़ 14% सुधार होता है. अगर आपको कम मेमोरी का इस्तेमाल करना है, तो एक वर्कर्स का विकल्प चुनें.

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

AbstractContainerizingSandboxedSpawn.java में मौजूद किसी इंटरनल स्ट्रिंग कॉन्स्टेंट को बदलने के बाद, सिर्फ़ Java सोर्स को फिर से कॉम्पाइल करने (//src/main/java/com/google/devtools/build/lib/bazel:BazelServer_deploy.jar) पर, प्रोसेस की स्पीड तीन गुना बढ़ जाती है. इसमें, एक वॉर्मअप बिल्ड को छोड़कर, औसतन 20 इंक्रीमेंटल बिल्ड होते हैं:

इंक्रीमेंटल बिल्ड की परफ़ॉर्मेंस में हुए सुधारों का ग्राफ़

दूसरी इमेज. इंक्रीमेंटल बिल्ड के परफ़ॉर्मेंस में सुधार का ग्राफ़.

स्पीड में होने वाली बढ़ोतरी, किए जा रहे बदलाव पर निर्भर करती है. ऊपर दी गई स्थिति में, आम तौर पर इस्तेमाल होने वाले कॉन्स्टेंट को बदलने पर, प्रोसेस की स्पीड में छह गुना की बढ़ोतरी होती है.

हमेशा चलने वाले वर्कर में बदलाव करना

आप काम करने वाले लोगों को स्टार्ट-अप फ़्लैग बताने के लिए, --worker_extra_flag फ़्लैग पास कर सकते हैं. इन फ़्लैग की पहचान याद रखने के लिए ज़रूरी होती है. उदाहरण के लिए, --worker_extra_flag=javac=--debug को पास करने पर, सिर्फ़ Javac के लिए डीबगिंग की सुविधा चालू हो जाती है. इस फ़्लैग के हर इस्तेमाल के लिए, सिर्फ़ एक वर्कर फ़्लैग सेट किया जा सकता है. साथ ही, इसे सिर्फ़ एक याद रखने के लिए इस्तेमाल किया जा सकता है. वर्कर्स को हर मेमोनिक के लिए अलग से ही नहीं बनाया जाता, बल्कि उनके स्टार्ट-अप फ़्लैग में होने वाले बदलावों के लिए भी बनाया जाता है. स्मोनेनिक और स्टार्ट-अप फ़्लैग का हर कॉम्बिनेशन एक WorkerKey में जोड़ा जाता है. साथ ही, हर WorkerKey के लिए, ज़्यादा से ज़्यादा worker_max_instances वर्कर बनाए जा सकते हैं. अगले सेक्शन में देखें कि ऐक्शन कॉन्फ़िगरेशन, सेट-अप फ़्लैग की जानकारी कैसे दे सकता है.

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

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

--worker_quit_after_build फ़्लैग का इस्तेमाल, मुख्य रूप से डीबग करने और प्रोफ़ाइल बनाने के लिए किया जाता है. यह फ़्लैग, बिल्ड पूरा होने के बाद सभी वर्कर्स को बंद कर देता है. --worker_verbose को पास करके भी, यह जानकारी मिल सकती है कि वर्कफ़्लो में शामिल लोग क्या कर रहे हैं. यह फ़्लैग, WorkRequest के verbosity फ़ील्ड में दिखता है. इससे वर्कर्स को लागू करने के बारे में ज़्यादा जानकारी मिलती है.

कर्मचारी अपने लॉग, <outputBase>/bazel-workers डायरेक्ट्री में सेव करते हैं. उदाहरण के लिए, /tmp/_bazel_larsrc/191013354bebe14fdddae77f2679c3ef/bazel-workers/worker-1-Javac.log. फ़ाइल के नाम में वर्कर्स आईडी और मेनिमोन शामिल होता है. हर याद में एक से ज़्यादा WorkerKey हो सकते हैं. इसलिए, आपको किसी स्मरणिक याद के लिए worker_max_instances से ज़्यादा लॉग फ़ाइलें दिख सकती हैं.

Android बिल्ड के लिए, Android बिल्ड की परफ़ॉर्मेंस वाले पेज पर जाएं.

स्थायी कर्मचारियों को लागू करना

कर्मचारी बनाने के तरीके के बारे में ज़्यादा जानकारी के लिए, परसिस्टेंट वर्कर बनाना पेज देखें.

इस उदाहरण में, JSON का इस्तेमाल करने वाले वर्कर्स के लिए Starlark कॉन्फ़िगरेशन दिखाया गया है:

args_file = ctx.actions.declare_file(ctx.label.name + "_args_file")
ctx.actions.write(
    output = args_file,
    content = "\n".join(["-g", "-source", "1.5"] + ctx.files.srcs),
)
ctx.actions.run(
    mnemonic = "SomeCompiler",
    executable = "bin/some_compiler_wrapper",
    inputs = inputs,
    outputs = outputs,
    arguments = [ "-max_mem=4G",  "@%s" % args_file.path],
    execution_requirements = {
        "supports-workers" : "1", "requires-worker-protocol" : "json" }
)

इस परिभाषा के साथ, इस ऐक्शन का पहला इस्तेमाल, कमांड लाइन /bin/some_compiler -max_mem=4G --persistent_worker को चलाने से शुरू होगा. इसके बाद, Foo.java को कंपाइल करने का अनुरोध कुछ ऐसा दिखेगा:

ध्यान दें: प्रोटोकॉल बफ़र स्पेसिफ़िकेशन में "स्नेक केस" (request_id) का इस्तेमाल किया जाता है, जबकि JSON प्रोटोकॉल में "कैमल केस" (requestId) का इस्तेमाल किया जाता है. इस दस्तावेज़ में, हम JSON के उदाहरणों में कैमल केस का इस्तेमाल करेंगे. हालांकि, प्रोटोकॉल के बावजूद फ़ील्ड के बारे में बात करते समय, स्नेक केस का इस्तेमाल करेंगे.

{
  "arguments": [ "-g", "-source", "1.5", "Foo.java" ]
  "inputs": [
    { "path": "symlinkfarm/input1", "digest": "d49a..." },
    { "path": "symlinkfarm/input2", "digest": "093d..." },
  ],
}

वर्कर को यह stdin पर, न्यूलाइन-डीलिमिटेड JSON फ़ॉर्मैट में मिलता है (क्योंकि requires-worker-protocol को JSON पर सेट किया गया है). इसके बाद, वर्कर्स ऐक्शन करता है और अपने स्टैंडआउट पर, Bazel को JSON फ़ॉर्मैट में WorkResponse भेजता है. इसके बाद, Bazel इस जवाब को पार्स करता है और मैन्युअल तरीके से उसे WorkResponse प्रोटो में बदल देता है. JSON के बजाय, बाइनरी कोड में बदले गए प्रोटोबफ़ का इस्तेमाल करके जुड़े कर्मचारी से संपर्क करने के लिए, requires-worker-protocol को proto पर सेट किया जाएगा. उदाहरण के लिए:

  execution_requirements = {
    "supports-workers" : "1" ,
    "requires-worker-protocol" : "proto"
  }

अगर आपने प्रोग्राम चलाने से जुड़ी ज़रूरी शर्तों में requires-worker-protocol शामिल नहीं किया है, तो Bazel डिफ़ॉल्ट रूप से, प्रोग्राम के बीच डेटा भेजने के लिए protobuf का इस्तेमाल करेगा.

Bazel, मेनिमोन और शेयर किए गए फ़्लैग से WorkerKey का पता लगाता है. इसलिए, अगर इस कॉन्फ़िगरेशन में max_mem पैरामीटर को बदलने की अनुमति दी जाती है, तो इस्तेमाल की गई हर वैल्यू के लिए एक अलग वर्कर्स स्पॉन किया जाएगा. अगर बहुत ज़्यादा वैरिएशन इस्तेमाल किए जाते हैं, तो इससे ज़्यादा मेमोरी खर्च हो सकती है.

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

इस GitHub रिपॉज़िटरी में, आपको Java और Python, दोनों में लिखे गए वर्कर्स रैपर के उदाहरण दिख सकते हैं. अगर आप JavaScript या TypeScript में काम कर रहे हैं, तो @ba इनाम/worker पैकेज और nodejs वर्कर उदाहरण आपके काम आ सकते हैं.

वर्कर्स, सैंडबॉक्सिंग पर कैसे असर डालते हैं?

डिफ़ॉल्ट रूप से worker रणनीति का इस्तेमाल करने पर, ऐक्शन local रणनीति की तरह ही सैंडबॉक्स में नहीं चलता. सभी कर्मियों को सैंडबॉक्स में चलाने के लिए, --worker_sandboxing फ़्लैग को सेट किया जा सकता है. इससे यह पक्का किया जा सकता है कि टूल के हर काम में सिर्फ़ वही इनपुट फ़ाइलें दिखें जो उसे चाहिए. हालांकि, यह टूल अब भी अनुरोधों के बीच, इंटरनल तौर पर जानकारी लीक कर सकता है. उदाहरण के लिए, कैश मेमोरी के ज़रिए. dynamic रणनीति का इस्तेमाल करने के लिए, सर्वर वर्कर को सैंडबॉक्स में रखना ज़रूरी है.

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

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

मल्टीप्लेक्स वर्कर्स को सिर्फ़ तब सैंडबॉक्स किया जा सकता है, जब वर्कर्स को लागू करने की सुविधा सैंडबॉक्सिंग के साथ काम करती हो. साथ ही, सैंडबॉक्सिंग को --experimental_worker_multiplex_sandboxing फ़्लैग की मदद से अलग से चालू करना होगा. डिज़ाइन दस्तावेज़ में ज़्यादा जानकारी देखें).

इसके बारे में और पढ़ें

लगातार काम करने वाले कर्मचारियों के बारे में ज़्यादा जानकारी के लिए, यह लेख देखें: