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

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

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 में, अनुरोध आईडी, ज़ीरो या नॉन-ज़ीरो एक्ज़िट कोड, और एक आउटपुट स्ट्रिंग शामिल होती है. इससे अनुरोध को प्रोसेस या एक्ज़िक्यूट करने में हुई किसी भी गड़बड़ी के बारे में पता चलता है. output फ़ील्ड में, कम शब्दों में जानकारी शामिल होती है. पूरी लॉग फ़ाइलें, वर्कर के stderr में लिखी जा सकती हैं. वर्कर सिर्फ़ WorkResponses को stdout में लिख सकते हैं. इसलिए, वर्कर के लिए यह आम बात है कि वह अपने इस्तेमाल किए जाने वाले किसी भी टूल के 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,
}

0 का मतलब है "सिंगलप्लेक्स" अनुरोध. इसका इस्तेमाल तब किया जाता है, जब इस अनुरोध को अन्य अनुरोधों के साथ-साथ प्रोसेस नहीं किया जा सकता.request_id सर्वर इस बात की गारंटी देता है कि किसी वर्कर को सिर्फ़ request_id 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 के नॉन-ज़ीरो request_id के बराबर होगी. सर्वर, उन अनुरोधों के लिए रद्द करने के अनुरोध भेज सकता है जिनका जवाब वर्कर ने पहले ही दे दिया है. ऐसे में, रद्द करने के अनुरोध को अनदेखा करना होगा.

रद्द न किए गए हर WorkRequest मैसेज का जवाब सिर्फ़ एक बार देना होगा. भले ही, उसे रद्द किया गया हो या नहीं. सर्वर के रद्द करने का अनुरोध भेजने के बाद, वर्कर request_id सेट करके और was_cancelled फ़ील्ड को 'सही' पर सेट करके, WorkResponse के साथ जवाब दे सकता है. सामान्य WorkResponse भेजना भी स्वीकार किया जाता है. हालांकि, output और exit_code फ़ील्ड को अनदेखा कर दिया जाएगा.

WorkRequest के लिए जवाब भेजने के बाद, वर्कर को अपनी वर्किंग डायरेक्ट्री में मौजूद फ़ाइलों में कोई बदलाव नहीं करना चाहिए. सर्वर के पास फ़ाइलों को साफ़ करने का विकल्प होता है. इसमें, कुछ समय के लिए सेव की गई फ़ाइलें भी शामिल हैं.

वर्कर का इस्तेमाल करने वाला नियम बनाना

आपको एक ऐसा नियम भी बनाना होगा जो वर्कर से की जाने वाली कार्रवाइयों को जनरेट करता है. स्टार्लार्क नियम बनाना, जो वर्कर का इस्तेमाल करता है, किसी अन्य नियम को बनाने जैसा ही है .

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

वर्कर को रेफ़र करना

वर्कर का इस्तेमाल करने वाले नियम में, एक ऐसा फ़ील्ड होना चाहिए जो वर्कर को रेफ़र करता हो. इसलिए, आपको अपने वर्कर को तय करने के लिए, \*\_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" से पता चलता है कि वर्कर को टारगेट प्लैटफ़ॉर्म के बजाय, आपके एक्ज़ीक्यूशन प्लैटफ़ॉर्म पर चलाने के लिए बनाया जाना चाहिए. इसका मतलब है कि बिल्ड के दौरान, वर्कर का इस्तेमाल टूल के तौर पर किया जाता है.

काम की कार्रवाई से जुड़ी ज़रूरी शर्तें

वर्कर का इस्तेमाल करने वाला नियम, वर्कर से की जाने वाली कार्रवाइयों को बनाता है. इन कार्रवाइयों के लिए कुछ ज़रूरी शर्तें हैं.

  • The "arguments" फ़ील्ड. इसमें स्ट्रिंग की सूची शामिल होती है. इनमें से आखिरी स्ट्रिंग को छोड़कर बाकी सभी स्ट्रिंग, स्टार्टअप के दौरान वर्कर को पास किए जाने वाले आर्ग्युमेंट होती हैं. "arguments" सूची में मौजूद आखिरी एलिमेंट, flag-file (@ से पहले) आर्ग्युमेंट होता है. वर्कर्स, हर WorkRequest के आधार पर, तय की गई फ़्लैगफ़ाइल से आर्ग्युमेंट पढ़ते हैं. आपका नियम, वर्कर के लिए नॉन-स्टार्टअप आर्ग्युमेंट को इस फ़्लैगफ़ाइल में लिख सकता है.

  • The "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 कंपाइलर वर्कर्स का इस्तेमाल किया जाता है. इसके अलावा, JSON वर्कर का एक उदाहरण भी दिया गया है. इसका इस्तेमाल हमारे इंटिग्रेशन टेस्ट में किया जाता है.

सही कॉलबैक पास करके, Java पर आधारित किसी भी टूल को वर्कर बनाने के लिए, उनके स्केफ़ोल्डिंग का इस्तेमाल किया जा सकता है.

वर्कर का इस्तेमाल करने वाले नियम का उदाहरण देखने के लिए, Bazel के वर्कर इंटिग्रेशन टेस्ट को देखें.

बाहरी योगदानकर्ताओं ने अलग-अलग भाषाओं में वर्कर्स लागू किए हैं. Bazel के पर्सिस्टेंट वर्कर्स के पॉलीग्लॉट लागू करने के तरीके देखें. GitHub पर और भी कई उदाहरण देखे जा सकते हैं!