पर्सिस्टेंट वर्कर की मदद से, तेज़ी से घर बनाया जा सकता है. अगर आपके बिल्ड में ऐसी कार्रवाइयां बार-बार होती हैं जिनके लिए स्टार्टअप की लागत ज़्यादा होती है या जिन्हें क्रॉस-ऐक्शन कैश मेमोरी से फ़ायदा होता है, तो इन कार्रवाइयों को करने के लिए, अपना परसिस्टेंट वर्कर लागू किया जा सकता है.
Bazel सर्वर, वर्कर के साथ stdin/stdout का इस्तेमाल करके इंटरैक्ट करता है. यह प्रोटोकॉल बफ़ या JSON स्ट्रिंग के इस्तेमाल का समर्थन करता है.
वर्कर को लागू करने के दो हिस्से होते हैं:
कर्मचारी को
पर्सिस्टेंट वर्कर को कुछ ज़रूरी शर्तों का पालन करना होता है:
- यह अपने
stdinसे WorkRequests को पढ़ता है. - यह
stdoutमें WorkResponses (और सिर्फ़WorkResponse) लिखता है. - यह
--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 वर्कर को भी बैकवर्ड और फ़ॉरवर्ड कंपैटिबिलिटी की प्रॉपर्टी बनाए रखनी होंगी. इसके लिए, JSON वर्कर को इन मैसेज में मौजूद ऐसे फ़ील्ड को अनदेखा करना होगा जिनके बारे में उसे जानकारी नहीं है. साथ ही, उसे ऐसी वैल्यू के लिए प्रोटोबफ़ की डिफ़ॉल्ट वैल्यू का इस्तेमाल करना होगा जो मौजूद नहीं हैं.
- Bazel, अनुरोधों को प्रोटोबफ़ के तौर पर सेव करता है और प्रोटोबफ़ के JSON फ़ॉर्मैट का इस्तेमाल करके उन्हें JSON में बदलता है
रद्द किया जाना
कर्मचारी, काम के अनुरोध पूरे होने से पहले उन्हें रद्द करने की अनुमति दे सकते हैं. हालांकि, ऐसा करना ज़रूरी नहीं है.
यह सुविधा, डाइनैमिक एक्ज़ीक्यूशन के लिए खास तौर पर काम आती है. इसमें लोकल एक्ज़ीक्यूशन को, रिमोट एक्ज़ीक्यूशन की मदद से बार-बार रोका जा सकता है. बुकिंग रद्द करने की अनुमति देने के लिए, execution-requirements फ़ील्ड (नीचे देखें) में supports-worker-cancellation: 1 जोड़ें और --experimental_worker_cancellation फ़्लैग सेट करें.
रद्द करने का अनुरोध, WorkRequest होता है, जिसमें cancel फ़ील्ड सेट होता है. इसी तरह, रद्द करने का जवाब, WorkResponse होता है, जिसमें was_cancelled फ़ील्ड सेट होता है. रद्द करने के अनुरोध या रद्द करने के जवाब में, सिर्फ़ एक और फ़ील्ड होना चाहिए. यह फ़ील्ड request_id है. इससे पता चलता है कि किस अनुरोध को रद्द करना है. सिंगलप्लेक्स वर्कर के लिए request_id फ़ील्ड की वैल्यू 0 होगी. वहीं, मल्टीप्लेक्स वर्कर के लिए, request_id फ़ील्ड की वैल्यू, पहले भेजे गए WorkRequest की नॉन-ज़ीरो वैल्यू होगी. सर्वर, उन अनुरोधों को रद्द करने के अनुरोध भेज सकता है जिनके जवाब वर्कर पहले ही दे चुका है. ऐसे में, रद्द करने के अनुरोध को अनदेखा किया जाना चाहिए.
रद्द न किए गए 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(@-preceded) आर्ग्युमेंट है. वर्कर, हर WorkRequest के हिसाब से, तय की गई फ़्लैग फ़ाइल से आर्ग्युमेंट पढ़ते हैं. आपका नियम, इस फ़्लैगफ़ाइल में वर्कर के लिए नॉन-स्टार्टअप आर्ग्युमेंट लिख सकता है."execution-requirements" फ़ील्ड, जो
"supports-workers" : "1","supports-multiplex-workers" : "1"या दोनों को शामिल करने वाली डिक्शनरी लेता है.वर्कर को भेजे गए सभी अनुरोधों के लिए, "arguments" और "execution-requirements" फ़ील्ड ज़रूरी हैं. इसके अलावा, JSON वर्कर को जिन कार्रवाइयों को पूरा करना चाहिए उनके लिए, execution requirements फ़ील्ड में
"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 पर कई और उदाहरण मिल सकते हैं!