ایجاد کارگران پایدار

کارگران مداوم می توانند ساخت شما را سریعتر کنند. اگر اقدامات مکرری در ساخت خود دارید که هزینه راه‌اندازی بالایی دارند یا از ذخیره‌سازی متقاطع سود می‌برند، ممکن است بخواهید کارگر مداوم خود را برای انجام این اقدامات پیاده‌سازی کنید.

سرور Bazel با استفاده از stdin / stdout با کارگر ارتباط برقرار می کند. از استفاده از بافرهای پروتکل یا رشته های JSON پشتیبانی می کند.

پیاده سازی کارگر دارای دو بخش است:

ساختن کارگر

یک کارگر مداوم چند الزام را رعایت می کند:

  • WorkRequests را از stdin خود می خواند.
  • WorkResponses (و فقط WorkResponse s) را در stdout خود می نویسد.
  • پرچم --persistent_worker را می پذیرد. wrapper باید پرچم خط فرمان --persistent_worker را بشناسد و فقط در صورت پاس شدن آن پرچم، خود را پایدار کند، در غیر این صورت باید یک کامپایل یک شات انجام دهد و از آن خارج شود.

اگر برنامه شما این الزامات را رعایت کند، می توان از آن به عنوان یک کارگر مداوم استفاده کرد!

درخواست های کاری

یک WorkRequest شامل فهرستی از آرگومان‌ها به کارگر، فهرستی از جفت‌های path-digest است که نشان‌دهنده ورودی‌هایی است که کارگر می‌تواند به آن دسترسی داشته باشد (این مورد اعمال نمی‌شود، اما می‌توانید از این اطلاعات برای ذخیره‌سازی در حافظه پنهان استفاده کنید) و یک شناسه درخواست، که 0 است. برای کارگران تک پلکس

توجه: در حالی که مشخصات بافر پروتکل از "snake case" ( request_id ) استفاده می کند، پروتکل JSON از "camel case" ( 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 اختیاری می توان برای درخواست خروجی رفع اشکال اضافی از کارگر استفاده کرد. این کاملاً به کارگر بستگی دارد که چه چیزی و چگونه خروجی بدهد. مقادیر بالاتر نشان دهنده خروجی پرمخاطب تر است. با ارسال پرچم --worker_verbose به Bazel، قسمت verbosity روی 10 تنظیم می شود، اما مقادیر کوچکتر یا بزرگتر را می توان به صورت دستی برای مقادیر مختلف خروجی استفاده کرد.

فیلد اختیاری sandbox_dir فقط توسط کارگرانی استفاده می‌شود که از sandboxing چندگانه پشتیبانی می‌کنند.

پاسخ های کاری

یک 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,
}

request_id 0 یک درخواست "singlelex" را نشان می دهد، زمانی که این درخواست نمی تواند به موازات سایر درخواست ها پردازش شود استفاده می شود. سرور تضمین می‌کند که یک کارگر معین درخواست‌هایی را با درخواست‌های فقط request_id 0 یا فقط request_id بزرگ‌تر از صفر دریافت می‌کند. درخواست‌های Singleplex به صورت سریال ارسال می‌شوند، برای مثال اگر سرور درخواست دیگری را تا زمانی که پاسخی دریافت نکرده است ارسال نکند (به جز درخواست‌های لغو، به زیر مراجعه کنید).

یادداشت

  • قبل از هر بافر پروتکل طول آن در قالب varint است (به MessageLite.writeDelimitedTo() مراجعه کنید.
  • قبل از درخواست‌ها و پاسخ‌های JSON نشانگر اندازه وجود ندارد.
  • درخواست‌های JSON همان ساختار پروتوباف را دارند، اما از JSON استاندارد استفاده می‌کنند و برای همه نام‌های فیلد از camel case استفاده می‌کنند.
  • برای حفظ ویژگی‌های سازگاری به عقب و جلو مانند protobuf، کارگران JSON باید فیلدهای ناشناخته را در این پیام‌ها تحمل کنند و از پیش‌فرض‌های protobuf برای مقادیر از دست رفته استفاده کنند.
  • Bazel درخواست ها را به عنوان protobuf ذخیره می کند و با استفاده از فرمت JSON protobuf به JSON تبدیل می کند

لغو

کارگران می توانند به صورت اختیاری اجازه دهند درخواست های کاری قبل از اتمام آنها لغو شود. این به ویژه در ارتباط با اجرای پویا مفید است، جایی که اجرای محلی می تواند به طور منظم توسط یک اجرای از راه دور سریعتر قطع شود. برای اجازه لغو، supports-worker-cancellation: 1 را به قسمت execution-requirements اضافه کنید (به زیر مراجعه کنید) و پرچم --experimental_worker_cancellation را تنظیم کنید.

درخواست لغو یک WorkRequest با مجموعه فیلد cancel است (و به طور مشابه یک پاسخ لغو یک WorkResponse با مجموعه فیلد was_cancelled است). تنها فیلد دیگری که باید در درخواست لغو یا پاسخ لغو باشد request_id است که نشان می‌دهد کدام درخواست لغو شود. فیلد request_id برای کارگران singleplex 0 یا غیر 0 request_id یک WorkRequest قبلا ارسال شده برای کارگران multiplex خواهد بود. سرور ممکن است برای درخواست هایی که کارگر قبلاً به آنها پاسخ داده است درخواست لغو ارسال کند، در این صورت درخواست لغو باید نادیده گرفته شود.

به هر پیام WorkRequest بدون لغو باید دقیقاً یک بار پاسخ داده شود، چه لغو شده باشد یا نه. هنگامی که سرور یک درخواست لغو ارسال کرد، کارگر ممکن است با یک WorkResponse با مجموعه request_id و فیلد was_cancelled به true پاسخ دهد. ارسال یک WorkResponse معمولی نیز پذیرفته می شود، اما فیلدهای output و exit_code نادیده گرفته می شوند.

هنگامی که پاسخی برای WorkRequest ارسال شد، کارگر نباید فایل های موجود در فهرست کاری خود را لمس کند. سرور برای پاکسازی فایل ها، از جمله فایل های موقت، رایگان است.

ایجاد قاعده ای که از کارگر استفاده می کند

شما همچنین باید یک قانون ایجاد کنید که اقداماتی را ایجاد کند که باید توسط کارگر انجام شود. ایجاد یک قانون Starlark که از یک کارگر استفاده می کند درست مانند ایجاد هر قانون دیگری است.

علاوه بر این، این قانون نیاز به ارجاع به خود کارگر دارد، و برخی الزامات برای اقداماتی که ایجاد می کند وجود دارد.

اشاره به کارگر

قاعده‌ای که از worker استفاده می‌کند باید حاوی فیلدی باشد که به خود کارگر اشاره دارد، بنابراین باید یک نمونه از یک قانون \*\_binary برای تعریف کارگر خود ایجاد کنید. اگر کارگر شما MyWorker.Java نام دارد، ممکن است این قانون مرتبط باشد:

java_binary(
    name = "worker",
    srcs = ["MyWorker.Java"],
)

این برچسب "کارگر" را ایجاد می کند که به باینری کارگر اشاره دارد. سپس قانونی را تعریف می کنید که از کارگر استفاده می کند. این قانون باید یک ویژگی را تعریف کند که به باینری کارگر اشاره دارد.

اگر باینری کارگری که ساخته اید در بسته ای به نام "work" است که در سطح بالای ساخت قرار دارد، ممکن است این تعریف ویژگی باشد:

"worker": attr.label(
    default = Label("//work:worker"),
    executable = True,
    cfg = "exec",
)

cfg = "exec" نشان می دهد که کارگر باید برای اجرا بر روی پلت فرم اجرای شما ساخته شود تا روی پلت فرم هدف (یعنی کارگر به عنوان ابزار در طول ساخت استفاده می شود).

الزامات اقدام کار

قاعده ای که از کارگر استفاده می کند، اقداماتی را برای کارگر ایجاد می کند. این اقدامات چند الزام دارند.

  • فیلد "برهان" . این لیستی از رشته ها را می گیرد که همه آنها به جز آخرین آنها آرگومان هایی هستند که هنگام راه اندازی به کارگر منتقل می شوند. آخرین عنصر در لیست "Arguments" یک آرگومان flag-file (@-preceded) است. کارگران آرگومان ها را از فایل پرچم مشخص شده بر اساس هر درخواست کاری می خوانند. قانون شما می‌تواند آرگومان‌های غیر راه‌اندازی را برای کارگر در این فایل پرچم بنویسد.

  • فیلد "اجرای الزامات" ، که یک فرهنگ لغت حاوی "supports-workers" : "1" ، "supports-multiplex-workers" : "1" یا هر دو را می گیرد.

    فیلدهای "Arguments" و "Execution-Requirements" برای تمامی اقدامات ارسالی به کارگران مورد نیاز است. بعلاوه، اقداماتی که باید توسط کارگران JSON اجرا شوند، باید شامل "requires-worker-protocol" : "json" در قسمت الزامات اجرا باشند. "requires-worker-protocol" : "proto" نیز یک الزام اجرایی معتبر است، اگرچه برای کارگران پروتو لازم نیست، زیرا آنها پیش فرض هستند.

    همچنین می توانید یک worker-key-mnemonic در الزامات اجرا تنظیم کنید. اگر از فایل اجرایی برای چندین نوع اقدام استفاده مجدد می کنید و می خواهید اقدامات این کارگر را تشخیص دهید، ممکن است مفید باشد.

  • فایل‌های موقتی که در طول عملیات تولید می‌شوند باید در فهرست کاربر ذخیره شوند. این سندباکس را فعال می کند.

با فرض یک تعریف قانون با ویژگی «worker» که در بالا توضیح داده شد، علاوه بر ویژگی «srcs» که ورودی‌ها را نشان می‌دهد، یک ویژگی «خروجی» نشان‌دهنده خروجی‌ها، و یک ویژگی «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 از کارگران کامپایلر جاوا ، علاوه بر یک مثال کارگر JSON استفاده می کند که در تست های یکپارچه سازی ما استفاده می شود.

شما می توانید از داربست آنها برای تبدیل هر ابزار مبتنی بر جاوا به یک کارگر با ارسال پاسخ تماس صحیح استفاده کنید.

برای مثالی از قاعده‌ای که از یک کارگر استفاده می‌کند، نگاهی به تست ادغام کارگران Bazel بیاندازید.

مشارکت‌کنندگان خارجی کارگران را به زبان‌های مختلف پیاده‌سازی کرده‌اند. نگاهی به پیاده سازی Polyglot کارگران پایدار Bazel بیندازید. می توانید نمونه های بسیار بیشتری را در GitHub پیدا کنید!