लगातार काम करने वाले वर्कर की मदद से, बिल्ड तेज़ी से हो सकता है. अगर आपके पास ऐसे कई ऐक्शन हैं जिनके शुरू होने में ज़्यादा समय लगता है या जिन्हें कई ऐक्शन के लिए कैश मेमोरी में सेव किया जा सकता है, तो इन ऐक्शन को करने के लिए, अपना पर्सिस्टेंट वर्कर्स लागू करें.
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
}
प्रोटोबस के नियमों के मुताबिक, सभी फ़ील्ड भरना ज़रूरी नहीं है. हालांकि, एक ही अनुरोध आईडी के लिए WorkRequest
और उससे जुड़े WorkResponse
की ज़रूरत होती है. इसलिए, अगर अनुरोध आईडी शून्य नहीं है, तो उसे बताना ज़रूरी है. यह मान्य
WorkResponse
है.
{
"requestId" : 12,
}
request_id
के 0 होने का मतलब है कि "सिंगलप्लेक्स" अनुरोध किया गया है. इसका इस्तेमाल तब किया जाता है, जब इस अनुरोध को अन्य अनुरोधों के साथ प्रोसेस नहीं किया जा सकता. सर्वर यह पक्का करता है कि किसी वर्कर्स को सिर्फ़ request_id
0 या सिर्फ़ request_id
ज़्यादा से ज़्यादा अनुरोध मिलें. सिंगलप्लेक्स अनुरोध, सीरियल में भेजे जाते हैं. उदाहरण के लिए, अगर सर्वर तब तक कोई दूसरा अनुरोध नहीं भेजता, जब तक उसे जवाब नहीं मिल जाता (रद्द करने के अनुरोधों को छोड़कर, नीचे देखें).
ज़रूरी जानकारी
- हर प्रोटोकॉल बफ़र के आगे, उसकी लंबाई
varint
फ़ॉर्मैट में होती है (देखेंMessageLite.writeDelimitedTo()
. - JSON अनुरोधों और जवाबों के पहले, साइज़ का इंडिकेटर नहीं होता.
- JSON अनुरोध, protobuf के जैसे ही स्ट्रक्चर में होते हैं. हालांकि, इनमें स्टैंडर्ड JSON का इस्तेमाल किया जाता है. साथ ही, सभी फ़ील्ड के नामों के लिए कैमल केस का इस्तेमाल किया जाता है.
- protobuf की तरह ही, पुराने और नए वर्शन के साथ काम करने की प्रॉपर्टी बनाए रखने के लिए, JSON वर्कर्स को इन मैसेज में अनजान फ़ील्ड को बर्दाश्त करना होगा. साथ ही, वैल्यू मौजूद न होने पर protobuf की डिफ़ॉल्ट वैल्यू का इस्तेमाल करना होगा.
- Bazel, अनुरोधों को प्रोटोबुक के तौर पर सेव करता है और प्रोटोबुक के JSON फ़ॉर्मैट का इस्तेमाल करके उन्हें JSON में बदल देता है
रद्द किया जाना
दूसरा तरीका यह है कि वर्कर, काम खत्म होने से पहले काम के अनुरोधों को रद्द करने की अनुमति दे सकते हैं.
यह सुविधा, डाइनैमिक तरीके से प्रोसेस करने के लिए खास तौर पर काम की है. इसकी वजह यह है कि रीमोट तरीके से तेज़ी से प्रोसेस करने पर, स्थानीय तरीके से प्रोसेस करने में रुकावट आ सकती है. रद्द करने की अनुमति देने के लिए, execution-requirements
फ़ील्ड में supports-worker-cancellation: 1
जोड़ें (नीचे देखें) और --experimental_worker_cancellation
फ़्लैग सेट करें.
रद्द करने का अनुरोध, cancel
फ़ील्ड सेट वाला WorkRequest
होता है. इसी तरह, रद्द करने का जवाब, was_cancelled
फ़ील्ड सेट वाला WorkResponse
होता है. रद्द करने के अनुरोध या रद्द करने के रिस्पॉन्स में, सिर्फ़ request_id
फ़ील्ड होना चाहिए. इससे पता चलता है कि किस अनुरोध को रद्द करना है. सिंगलप्लेक्स वर्कर के लिए request_id
फ़ील्ड में 0 दिखेगा. वहीं, मल्टीप्लेक्स वर्कर के लिए, पहले भेजे गए WorkRequest
के request_id
में 0 के बजाय कोई वैल्यू दिखेगी. सर्वर, उन अनुरोधों के लिए रद्द करने के अनुरोध भेज सकता है जिनका जवाब वर्कफ़्लो पहले ही दे चुका है. ऐसे में, रद्द करने के अनुरोध को अनदेखा किया जाना चाहिए.
रद्द नहीं किए गए हर WorkRequest
मैसेज का जवाब एक बार देना ज़रूरी है, भले ही उसे रद्द किया गया हो या नहीं. सर्वर से रद्द करने का अनुरोध मिलने के बाद, वर्कर WorkResponse
के साथ जवाब दे सकता है. इसमें request_id
सेट होगा और was_cancelled
फ़ील्ड को 'सही' पर सेट किया जाएगा. सामान्य WorkResponse
भेजने पर भी स्वीकार किया जाता है, लेकिन
output
और exit_code
फ़ील्ड को अनदेखा कर दिया जाएगा.
किसी WorkRequest
के लिए जवाब भेजने के बाद, वर्कर्स को अपनी वर्किंग डायरेक्ट्री में मौजूद फ़ाइलों में बदलाव नहीं करना चाहिए. सर्वर, फ़ाइलों को मिटा सकता है. इनमें कुछ समय के लिए सेव की गई फ़ाइलें भी शामिल हैं.
वर्कफ़्लो का इस्तेमाल करने वाला नियम बनाना
आपको एक ऐसा नियम भी बनाना होगा जिससे वर्कफ़्लो में शामिल टास्क को पूरा करने के लिए कार्रवाइयां जनरेट हों. वर्कर्स का इस्तेमाल करने वाला Starlark नियम बनाना, कोई दूसरा नियम बनाने जैसा ही है.
इसके अलावा, नियम में वर्कफ़्लो के बारे में जानकारी होनी चाहिए. साथ ही, वर्कफ़्लो से होने वाली कार्रवाइयों के लिए कुछ ज़रूरी शर्तें भी होती हैं.
कर्मचारी को रेफ़र किया जा रहा है
वर्कफ़्लो में वर्कर्स का इस्तेमाल करने वाले नियम में, वर्कर्स का रेफ़रंस देने वाला फ़ील्ड होना चाहिए. इसलिए, आपको अपने वर्कर्स की जानकारी देने के लिए, \*\_binary
नियम का एक इंस्टेंस बनाना होगा. अगर आपके वर्कर को MyWorker.Java
कहा जाता है, तो यह जुड़ा हुआ नियम हो सकता है:
java_binary(
name = "worker",
srcs = ["MyWorker.Java"],
)
इससे "कर्मचारी" लेबल बनता है, जो वर्कर बाइनरी के बारे में बताता है. इसके बाद, वह नियम तय किया जाएगा जो वर्कर का इस्तेमाल करता है. इस नियम में एक ऐसा एट्रिब्यूट तय करना चाहिए जो वर्कर्स बाइनरी के बारे में बताता हो.
अगर आपने जो वर्कअराउंड बाइनरी बनाई है वह "work" नाम के पैकेज में है, जो बिल्ड के सबसे ऊपरी लेवल पर है, तो एट्रिब्यूट की परिभाषा यह हो सकती है:
"worker": attr.label(
default = Label("//work:worker"),
executable = True,
cfg = "exec",
)
cfg = "exec"
से पता चलता है कि वर्कर्स को टारगेट प्लैटफ़ॉर्म के बजाय, आपके रनिंग प्लैटफ़ॉर्म पर चलाने के लिए बनाया जाना चाहिए. इसका मतलब है कि वर्कर्स का इस्तेमाल, बिल्ड के दौरान टूल के तौर पर किया जाता है.
काम से जुड़ी कार्रवाई की ज़रूरी शर्तें
वर्कर का इस्तेमाल करने वाला नियम, वर्कर के लिए कार्रवाइयां बनाता है. इन कार्रवाइयों के लिए, कुछ ज़रूरी शर्तें होती हैं.
"तर्क" फ़ील्ड. यह स्ट्रिंग की सूची लेता है. इसमें आखिरी स्ट्रिंग को छोड़कर, सभी स्ट्रिंग, स्टार्टअप के समय वर्कर्स को भेजी जाने वाली आर्ग्युमेंट होती हैं. "आर्ग्युमेंट" सूची का आखिरी एलिमेंट,
flag-file
(@-पहले) आर्ग्युमेंट होता है. वर्कर्स, हर वर्क रिक्वेस्ट के आधार पर, तय की गई फ़्लैगफ़ाइल से आर्ग्युमेंट पढ़ते हैं. आपका नियम इस फ़्लैगफ़ाइल में वर्कर के लिए, नॉन-स्टार्टअप आर्ग्युमेंट लिख सकता है."execution-requirements" फ़ील्ड, जिसमें
"supports-workers" : "1"
,"supports-multiplex-workers" : "1"
या दोनों वाली डिक्शनरी होती है.कर्मचारियों को भेजी गई सभी कार्रवाइयों के लिए "तर्क" और "लागू करने की ज़रूरी शर्तें" फ़ील्ड ज़रूरी हैं. इसके अलावा, 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 कंपाइलर वर्कर्स के साथ-साथ, उदाहरण के तौर पर JSON वर्कर्स का भी इस्तेमाल किया जाता है. इनका इस्तेमाल, इंटिग्रेशन टेस्ट में किया जाता है.
सही कॉलबैक पास करके, किसी भी Java-based टूल को वर्कर्स में बदलने के लिए, स्केफ़ल्डिंग का इस्तेमाल किया जा सकता है.
कर्मचारी का इस्तेमाल करने वाले नियम के उदाहरण के लिए, बेज़ेल का वर्कर इंटिग्रेशन टेस्ट देखें.
बाहरी योगदान देने वाले लोगों ने अलग-अलग भाषाओं में वर्कर्स लागू किए हैं. Bazel के पर्सिस्टेंट वर्कर्स के लिए, कई भाषाओं में लागू किए गए तरीके देखें. GitHub पर कई और उदाहरण देखे जा सकते हैं!