परमानेंट वर्कर

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

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

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

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

काम करने वाले लोगों की संख्या चुनना

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

कर्मचारी सैंडबॉक्स कैसे करते हैं?

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

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

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

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

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

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