पर्सिस्टेंट वर्कर की मदद से, बिल्ड को तेज़ी से बनाया जा सकता है. अगर आपके बिल्ड में ऐसी कार्रवाइयां बार-बार होती हैं जिनके लिए स्टार्टअप की लागत ज़्यादा होती है या जिन्हें क्रॉस-ऐक्शन कैश मेमोरी से फ़ायदा होता है, तो इन कार्रवाइयों को पूरा करने के लिए, अपना परसिस्टेंट वर्कर लागू किया जा सकता है.
Bazel सर्वर, वर्कर के साथ stdin/stdout का इस्तेमाल करके इंटरैक्ट करता है. यह प्रोटोकॉल बफ़र या JSON स्ट्रिंग के इस्तेमाल का समर्थन करता है.
वर्कर को लागू करने के दो हिस्से होते हैं:
उपयोगकर्ता को
पर्सिस्टेंट वर्कर को कुछ ज़रूरी शर्तों का पालन करना होता है:
- यह अपने
stdinसे WorkRequests को पढ़ता है. - यह अपने
stdoutमें WorkResponses लिखता है. साथ ही, सिर्फ़WorkResponses लिखता है. - यह
--persistent_workerफ़्लैग स्वीकार करता है. रैपर को--persistent_workerकमांड-लाइन फ़्लैग को पहचानना चाहिए. साथ ही, अगर वह फ़्लैग पास किया जाता है, तो उसे सिर्फ़ खुद को लगातार चालू रखना चाहिए. ऐसा न होने पर, उसे एक बार कंपाइल करके बंद हो जाना चाहिए.
अगर आपका प्रोग्राम इन ज़रूरी शर्तों को पूरा करता है, तो इसका इस्तेमाल परसिस्टेंट वर्कर के तौर पर किया जा सकता है!
काम के अनुरोध
WorkRequest में वर्कर के लिए आर्ग्युमेंट की सूची होती है. साथ ही, इसमें पाथ-डाइजेस्ट पेयर की सूची होती है. यह सूची उन इनपुट को दिखाती है जिन्हें वर्कर ऐक्सेस कर सकता है. हालांकि, इसे लागू नहीं किया जाता, लेकिन इस जानकारी का इस्तेमाल कैश मेमोरी के लिए किया जा सकता है. इसमें अनुरोध आईडी भी होता है, जो सिंगलप्लेक्स वर्कर के लिए 0 होता है.
ध्यान दें: प्रोटोकॉल बफ़र स्पेसिफ़िकेशन में "स्नेक केस" (request_id) का इस्तेमाल किया जाता है, जबकि JSON प्रोटोकॉल में "कैमल केस" (requestId) का इस्तेमाल किया जाता है. इस दस्तावेज़ में, JSON के उदाहरणों में कैमल केस का इस्तेमाल किया गया है. हालांकि, फ़ील्ड के बारे में बात करते समय, प्रोटोकॉल के बावजूद स्नेक केस का इस्तेमाल किया गया है.
{
"arguments" : ["--some_argument"],
"inputs" : [
{ "path": "/path/to/my/file/1", "digest": "fdk3e2ml23d"},
{ "path": "/path/to/my/file/2", "digest": "1fwqd4qdd" }
],
"requestId" : 12
}
verbosity फ़ील्ड का इस्तेमाल, वर्कर से डीबग करने के लिए अतिरिक्त आउटपुट का अनुरोध करने के लिए किया जा सकता है. वर्कर को यह तय करना होता है कि उसे क्या और कैसे आउटपुट देना है. ज़्यादा वैल्यू का मतलब है कि आउटपुट में ज़्यादा जानकारी शामिल होगी. Bazel को --worker_verbose फ़्लैग पास करने पर, verbosity फ़ील्ड की वैल्यू 10 पर सेट हो जाती है. हालांकि, आउटपुट की अलग-अलग मात्रा के लिए, मैन्युअल तरीके से छोटी या बड़ी वैल्यू का इस्तेमाल किया जा सकता है.
sandbox_dir फ़ील्ड का इस्तेमाल सिर्फ़ उन वर्कर के लिए किया जाता है जो मल्टीप्लेक्स सैंडबॉक्सिंग की सुविधा के साथ काम करते हैं. हालांकि, इस फ़ील्ड का इस्तेमाल करना ज़रूरी नहीं है.
काम से जुड़े जवाब
WorkResponse में अनुरोध आईडी, शून्य या गैर-शून्य एग्ज़िट कोड, और आउटपुट मैसेज होता है. इस मैसेज में, अनुरोध को प्रोसेस करने या उसे पूरा करने के दौरान हुई गड़बड़ियों के बारे में बताया जाता है. किसी टूल को कॉल करने वाले वर्कर को, उस टूल के stdout और stderr को कैप्चर करना चाहिए. साथ ही, WorkResponse के ज़रिए उनकी रिपोर्ट करनी चाहिए. इसे वर्कर प्रोसेस के stdout में लिखना सुरक्षित नहीं है, क्योंकि इससे वर्कर प्रोटोकॉल में रुकावट आएगी.
इसे वर्कर प्रोसेस के stderr में लिखना सुरक्षित है. हालांकि, नतीजे को हर वर्कर की लॉग फ़ाइल में इकट्ठा किया जाता है. इसे अलग-अलग कार्रवाइयों के लिए नहीं लिखा जाता.
{
"exitCode" : 1,
"output" : "Action failed with the following message:\nCould not find input
file \"/path/to/my/file/1\"",
"requestId" : 12
}
प्रोटोबफ़ के स्टैंडर्ड के मुताबिक, सभी फ़ील्ड ज़रूरी नहीं हैं. हालांकि, Bazel को WorkRequest और उससे जुड़े WorkResponse के लिए, एक ही अनुरोध आईडी की ज़रूरत होती है. इसलिए, अगर अनुरोध आईडी शून्य नहीं है, तो उसे बताना ज़रूरी है. यह एक मान्य WorkResponse है.
{
"requestId" : 12,
}
request_id की वैल्यू 0 होने का मतलब है कि यह "सिंगलप्लेक्स" अनुरोध है. इसका इस्तेमाल तब किया जाता है, जब इस अनुरोध को अन्य अनुरोधों के साथ एक साथ प्रोसेस नहीं किया जा सकता. सर्वर यह पक्का करता है कि किसी वर्कर को ऐसे अनुरोध मिले जिनमें request_id की वैल्यू या तो सिर्फ़ 0 हो या सिर्फ़ 0 से ज़्यादा हो.request_id सिंगलप्लेक्स अनुरोधों को क्रम से भेजा जाता है. उदाहरण के लिए, अगर सर्वर को तब तक कोई दूसरा अनुरोध नहीं भेजा जाता, जब तक उसे जवाब नहीं मिल जाता (रद्द करने के अनुरोधों को छोड़कर, नीचे देखें).
ज़रूरी जानकारी
- हर प्रोटोकॉल बफ़र से पहले, उसकी लंबाई
varintफ़ॉर्मैट में दी जाती है. इसके बारे में जानने के लिए,MessageLite.writeDelimitedTo()देखें. - JSON फ़ॉर्मैट में किए गए अनुरोधों और मिले जवाबों से पहले, साइज़ इंडिकेटर नहीं होता.
- JSON अनुरोधों का स्ट्रक्चर, प्रोटोबफ़ के जैसा ही होता है. हालांकि, इनमें स्टैंडर्ड JSON का इस्तेमाल किया जाता है. साथ ही, सभी फ़ील्ड के नामों के लिए कैमल केस का इस्तेमाल किया जाता है.
- प्रोटोबफ़ की तरह ही, बैकवर्ड और फ़ॉरवर्ड कंपैटिबिलिटी की प्रॉपर्टी बनाए रखने के लिए, JSON वर्कर को इन मैसेज में मौजूद ऐसे फ़ील्ड को अनदेखा करना होगा जिनके बारे में उन्हें जानकारी नहीं है. साथ ही, उन्हें मौजूद न होने वाली वैल्यू के लिए प्रोटोबफ़ की डिफ़ॉल्ट वैल्यू का इस्तेमाल करना होगा.
- Bazel, अनुरोधों को प्रोटोबफ़ के तौर पर सेव करता है और उन्हें प्रोटोबफ़ के JSON फ़ॉर्मैट का इस्तेमाल करके JSON में बदलता है
रद्द किया जाना
कर्मचारी, काम पूरा होने से पहले अनुरोधों को रद्द करने की अनुमति दे सकते हैं. हालांकि, ऐसा करना ज़रूरी नहीं है.
यह सुविधा, डाइनैमिक एक्ज़ीक्यूशन के लिए खास तौर पर काम आती है. इसमें लोकल एक्ज़ीक्यूशन को, तेज़ी से होने वाले रिमोट एक्ज़ीक्यूशन से नियमित तौर पर रोका जा सकता है. बुकिंग रद्द करने की अनुमति देने के लिए, execution-requirements फ़ील्ड (नीचे देखें) में supports-worker-cancellation: 1 जोड़ें और --experimental_worker_cancellation फ़्लैग सेट करें.
रद्द करने का अनुरोध, WorkRequest होता है. इसमें cancel फ़ील्ड सेट होता है. इसी तरह, रद्द करने का जवाब, WorkResponse होता है. इसमें was_cancelled फ़ील्ड सेट होता है. रद्द करने के अनुरोध या रद्द करने के जवाब में, सिर्फ़ एक और फ़ील्ड होना चाहिए. यह फ़ील्ड request_id है. इससे पता चलता है कि किस अनुरोध को रद्द करना है. सिंगलप्लेक्स वर्कर के लिए request_id फ़ील्ड की वैल्यू 0 होगी. वहीं, मल्टीप्लेक्स वर्कर के लिए, WorkRequest फ़ील्ड की वैल्यू, पहले भेजे गए WorkRequest की नॉन-ज़ीरो वैल्यू होगी.request_id सर्वर, उन अनुरोधों को रद्द करने के अनुरोध भेज सकता है जिनके जवाब वर्कर पहले ही दे चुका है. ऐसे में, रद्द करने के अनुरोध को अनदेखा किया जाना चाहिए.
रद्द न करें WorkRequest वाले हर मैसेज का जवाब सिर्फ़ एक बार दिया जाना चाहिए. भले ही, उसे रद्द किया गया हो या नहीं. जब सर्वर, रद्द करने का अनुरोध भेज देता है, तो वर्कर WorkResponse के साथ जवाब दे सकता है. इसमें request_id सेट होता है और was_cancelled फ़ील्ड को सही पर सेट किया जाता है. सामान्य WorkResponse भेजने की अनुमति भी है. हालांकि, output और exit_code फ़ील्ड को अनदेखा कर दिया जाएगा.
WorkRequest के लिए जवाब भेजे जाने के बाद, वर्कर को अपनी वर्किंग डायरेक्ट्री में मौजूद फ़ाइलों को नहीं छूना चाहिए. सर्वर के पास फ़ाइलों को हटाने का विकल्प होता है. इसमें कुछ समय के लिए सेव की गई फ़ाइलें भी शामिल हैं.
वर्कर का इस्तेमाल करने वाला नियम बनाना
आपको एक ऐसा नियम भी बनाना होगा जो वर्कर को कार्रवाइयां करने के लिए निर्देश दे. वर्कर का इस्तेमाल करने वाला Starlark नियम बनाना, कोई अन्य नियम बनाने जैसा ही है.
इसके अलावा, नियम में वर्कर का रेफ़रंस होना चाहिए. साथ ही, इससे जनरेट होने वाली कार्रवाइयों के लिए कुछ ज़रूरी शर्तें हैं.
कर्मचारी के बारे में जानकारी
वर्कर का इस्तेमाल करने वाले नियम में, वर्कर से जुड़ा फ़ील्ड होना चाहिए. इसलिए, आपको \*\_binary नियम का एक इंस्टेंस बनाना होगा, ताकि वर्कर को तय किया जा सके. अगर आपके वर्कर को MyWorker.Java कहा जाता है, तो इससे जुड़ा नियम यह हो सकता है:
java_binary(
name = "worker",
srcs = ["MyWorker.Java"],
)
इससे "worker" लेबल बनता है, जो वर्कर बाइनरी को दिखाता है. इसके बाद, आपको एक ऐसा नियम तय करना होगा जो वर्कर का इस्तेमाल करता हो. इस नियम में, एक ऐसा एट्रिब्यूट तय किया जाना चाहिए जो वर्कर बाइनरी को रेफ़र करता हो.
अगर आपने "work" नाम का वर्कर बाइनरी पैकेज बनाया है, जो बिल्ड के टॉप लेवल पर है, तो एट्रिब्यूट की परिभाषा यह हो सकती है:
"worker": attr.label(
default = Label("//work:worker"),
executable = True,
cfg = "exec",
)
cfg = "exec" से पता चलता है कि वर्कर को आपके एक्ज़ीक्यूशन प्लैटफ़ॉर्म पर चलाने के लिए बनाया जाना चाहिए, न कि टारगेट प्लैटफ़ॉर्म पर. इसका मतलब है कि वर्कर का इस्तेमाल बिल्ड के दौरान टूल के तौर पर किया जाता है.
काम से जुड़ी कार्रवाई करने की ज़रूरी शर्तें
वर्कर का इस्तेमाल करने वाला नियम, वर्कर के लिए कार्रवाइयां बनाता है. इन कार्रवाइयों के लिए, कुछ ज़रूरी शर्तें हैं.
"arguments" फ़ील्ड. यह स्ट्रिंग की एक सूची लेता है. इसमें आखिरी स्ट्रिंग को छोड़कर बाकी सभी स्ट्रिंग, स्टार्टअप के दौरान वर्कर को पास किए गए आर्ग्युमेंट होते हैं. "arguments" सूची में मौजूद आखिरी एलिमेंट,
flag-file(इससे पहले @ का इस्तेमाल किया जाता है) आर्ग्युमेंट है. वर्कर, हर WorkRequest के हिसाब से, बताई गई फ़्लैगफ़ाइल से आर्ग्युमेंट पढ़ते हैं. आपका नियम, इस फ़्लैगफ़ाइल में वर्कर के लिए नॉन-स्टार्टअप आर्ग्युमेंट लिख सकता है."execution-requirements" फ़ील्ड, जो
"supports-workers" : "1","supports-multiplex-workers" : "1"या दोनों को शामिल करने वाला डिक्शनरी लेता है.वर्कर को भेजे गए सभी कार्रवाइयों के लिए, "arguments" और "execution-requirements" फ़ील्ड ज़रूरी हैं. इसके अलावा, JSON वर्कर को जिन कार्रवाइयों को पूरा करना चाहिए उनके लिए, एक्ज़ीक्यूशन की ज़रूरी शर्तों वाले फ़ील्ड में
"requires-worker-protocol" : "json"शामिल करना ज़रूरी है."requires-worker-protocol" : "proto"भी एक मान्य शर्त है. हालांकि, यह प्रोटो वर्कर के लिए ज़रूरी नहीं है, क्योंकि वे डिफ़ॉल्ट रूप से उपलब्ध होते हैं.ज़रूरी शर्तों में
worker-key-mnemonicभी सेट किया जा सकता है. अगर आपको कई तरह की कार्रवाइयों के लिए, एक ही एक्ज़ीक्यूटेबल का फिर से इस्तेमाल करना है और इस वर्कर के हिसाब से कार्रवाइयों को अलग-अलग करना है, तो यह विकल्प आपके लिए काम का हो सकता है.कार्रवाई के दौरान जनरेट हुई अस्थायी फ़ाइलों को वर्कर की डायरेक्ट्री में सेव किया जाना चाहिए. इससे सैंडबॉक्सिंग की सुविधा चालू होती है.
ऊपर बताए गए "worker" एट्रिब्यूट के साथ नियम की परिभाषा के आधार पर, इनपुट दिखाने वाले "srcs" एट्रिब्यूट, आउटपुट दिखाने वाले "output" एट्रिब्यूट, और वर्कर स्टार्टअप आर्ग्युमेंट दिखाने वाले "args" एट्रिब्यूट के अलावा, ctx.actions.run को इस तरह कॉल किया जा सकता है:
ctx.actions.run(
inputs=ctx.files.srcs,
outputs=[ctx.outputs.output],
executable=ctx.executable.worker,
mnemonic="someMnemonic",
execution_requirements={
"supports-workers" : "1",
"requires-worker-protocol" : "json"},
arguments=ctx.attr.args + ["@flagfile"]
)
एक और उदाहरण के लिए, परसिस्टेंट वर्कर्स लागू करना लेख पढ़ें.
उदाहरण
Bazel का कोड बेस, Java कंपाइलर वर्कर का इस्तेमाल करता है. साथ ही, इसमें example JSON worker भी शामिल होता है. इसका इस्तेमाल इंटिग्रेशन टेस्ट में किया जाता है.
सही कॉलबैक पास करके, Java पर आधारित किसी भी टूल को वर्कर में बदलने के लिए, उनके स्केफ़ोल्डिंग का इस्तेमाल किया जा सकता है.
वर्कर का इस्तेमाल करने वाले नियम का उदाहरण देखने के लिए, Bazel के वर्कर इंटिग्रेशन टेस्ट को देखें.
बाहरी योगदानकर्ताओं ने कई भाषाओं में वर्कर लागू किए हैं. Bazel के परसिस्टेंट वर्कर के पॉलीग्लॉट इंप्लीमेंटेशन देखें. आपको GitHub पर कई और उदाहरण मिल सकते हैं!