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

समस्या की शिकायत करें स्रोत देखें

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

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

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

स्थायी कर्मचारियों को कई भाषाओं में लागू किया जाता है. इनमें Java, Scala, Kotlin वगैरह शामिल हैं.

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

स्थायी कर्मचारियों का इस्तेमाल करना

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

स्थायी कर्मचारियों में बदलाव करना

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

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

--worker_sandboxing फ़्लैग को पास करने से, हर वर्कर के अनुरोध को उसके सभी इनपुट के लिए एक अलग सैंडबॉक्स डायरेक्ट्री का इस्तेमाल करने के लिए बनाया जाता है. sandbox को सेट अप करने में थोड़ा ज़्यादा समय लगता है, खास तौर पर 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 पर सेट किया गया है). इसके बाद, वर्कर कोई कार्रवाई करता है और Bzel को उसके stdout पर JSON-फ़ॉर्मैट का WorkResponse भेजता है. इसके बाद, Bazel इस रिस्पॉन्स को पार्स करके, मैन्युअल तरीके से WorkResponse प्रोटो में बदल देता है. JSON के बजाय बाइनरी कोड में बदले गए प्रोटोबफ़ का इस्तेमाल करके, संबंधित वर्कर से संपर्क करने के लिए, requires-worker-protocol को proto पर सेट किया जाएगा, जैसे कि:

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

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

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

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

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

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

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

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

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

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

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

स्थायी कर्मचारियों के बारे में ज़्यादा जानकारी के लिए, देखें: