永続ワーカーの作成

問題を報告する ソースを表示

永続ワーカーはビルドを高速化できます。ビルドコストが高い起動コストのアクションがある場合や、アクションのキャッシュ保存によるメリットを得られる場合は、独自の永続ワーカーを実装して、これらのアクションを実行することをおすすめします。

Bazel サーバーは、stdin/stdout を使用してワーカーと通信します。プロトコル バッファまたは JSON 文字列の使用をサポートします。

ワーカー実装は 2 つの部分で構成されます。

ワーカーの作成

永続ワーカーは、いくつかの要件を満たします。

  • stdin から WorkRequest を読み取ります。
  • WorkResponsestdout に書き込みます(WorkResponse のみ)。
  • --persistent_worker フラグを使用できます。ラッパーは --persistent_worker コマンドライン フラグを認識し、そのフラグが渡された場合にのみ永続化する必要があります。それ以外の場合は、ワンショット コンパイルと終了を行う必要があります。

プログラムがこれらの要件を遵守している場合は、永続ワーカーとして使用できます。

処理リクエスト

WorkRequest には、ワーカーへの引数のリスト、ワーカーがアクセスできる入力を表すパス ダイジェスト ペアのリスト(強制ではありませんが、この情報を使用してキャッシュを使用できます)、リクエスト ID は単一プレックス ワーカーの場合は 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 フィールドを使用して、ワーカーからの追加のデバッグ出力をリクエストできます。何をどのように出力するかはワーカー次第です。値が大きいほど、詳細な出力になります。--worker_verbose フラグを Bazel に渡すと、verbosity フィールドが 10 に設定されますが、出力の量に応じて、より小さい値または大きい値を手動で使用できます。

オプションの sandbox_dir フィールドは、多重サンドボックスをサポートするワーカーによってのみ使用されます。

仕事用の応答

WorkResponse には、リクエスト ID、ゼロまたはゼロ以外の終了コード、そしてリクエストの処理または実行で発生したエラーを説明する出力文字列が含まれます。output フィールドには、簡単な説明が含まれています。完全なログは、ワーカーの stderr に書き込まれることがあります。ワーカーは WorkResponsesstdout にのみ書き込むことができるため、ワーカーは使用するツールの stdoutstderr にリダイレクトするのが一般的です。

{
  "exitCode" : 1,
  "output" : "Action failed with the following message:\nCould not find input
    file \"/path/to/my/file/1\"",
  "requestId" : 12
}

protobuf の標準に従って、すべてのフィールドはオプションです。ただし、Bazel では WorkRequest と、対応する WorkResponse に同じリクエスト ID が必要なため、ゼロでない場合はリクエスト ID を指定する必要があります。有効な WorkResponse です。

{
  "requestId" : 12,
}

request_id が 0 の場合、単一リクエストであり、このリクエストを他のリクエストと同時に処理できない場合に使用されます。サーバーは、特定のワーカーが request_id 0 のみ、または 0 より大きい request_id のみのリクエストを受信することを保証します。Singleplex リクエストは連続して送信されます。たとえば、サーバーがレスポンスを受信するまで別のリクエストを送信しない場合(キャンセル リクエストを除き、以下を参照)。

注意事項

  • 各プロトコル バッファの先頭には、varint の形式の長さが必要です(MessageLite.writeDelimitedTo() を参照)。
  • JSON のリクエストとレスポンスの先頭にはサイズ インジケーターがありません。
  • JSON リクエストは protobuf と同じ構造を維持しますが、標準の JSON を使用し、すべてのフィールド名にキャメルケースを使用します。
  • protobuf と同じ下位互換性と上位互換性のプロパティを維持するため、JSON ワーカーは、これらのメッセージ内の不明なフィールドを許容し、欠損値に対して protobuf デフォルトを使用する必要があります。
  • Bazel は、リクエストを protobuf として保存し、protobuf の JSON 形式を使用して JSON に変換します。

キャンセル

ワーカーは、終了前に処理リクエストをキャンセルできるようにすることもできます。これは、ローカル実行が高速化され、リモート実行が定期的に中断される可能性がある動的実行で特に役立ちます。キャンセルできるようにするには、execution-requirements フィールドに supports-worker-cancellation: 1 を追加し(下記を参照)、--experimental_worker_cancellation フラグを設定します。

キャンセル リクエストは、cancel フィールドが設定された WorkRequest です(同様に、キャンセル レスポンスは、was_cancelled フィールドが設定された WorkResponse です)。キャンセル リクエストまたはキャンセル レスポンスに含める必要があるその他のフィールドは request_id のみで、キャンセル リクエストを示します。request_id フィールドは、シングルプレックス ワーカーの場合は 0、マルチプレックス ワーカーの場合は以前に送信した WorkRequest の 0 以外の request_id になります。ワーカーがすでに応答したリクエストに対して、サーバーからキャンセル リクエストを送信できます。その場合、キャンセル リクエストは無視する必要があります。

キャンセルされていない WorkRequest メッセージには、キャンセルの有無に関係なく、1 回だけ応答する必要があります。サーバーがキャンセル リクエストを送信すると、ワーカーは request_idwas_cancelled フィールドを true に設定して WorkResponse を返すことができます。通常の 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" フィールド。これは文字列のリストを受け取ります。最後のもの以外は、起動時にワーカーに渡される引数です。「引数」リストの最後の要素は flag-file(@ で始まる)引数です。ワーカーは、指定されたフラグファイルから引数を WorkRequest ごとに読み取ります。ルールは、ワーカーの起動時引数をこのフラグ ファイルに書き込むことができます。

  • "execution-requirements" フィールド。"supports-workers" : "1""supports-multiplex-workers" : "1"、またはその両方を含む辞書を受け取ります。

    ワーカーに送信されるすべてのアクションで、「引数」フィールドと「実行要件」フィールドが必要です。また、JSON ワーカーで実行されるアクションには、実行要件フィールドに "requires-worker-protocol" : "json" を含める必要があります。"requires-worker-protocol" : "proto" も有効な実行要件です。ただし、proto ワーカーはデフォルトであるため、必須ではありません。

    実行要件で worker-key-mnemonic を設定することもできます。これは、実行可能ファイルを複数のアクション タイプで再利用し、このワーカーによってアクションを区別する場合に役立ちます。

  • アクションの過程で生成された一時ファイルは、ワーカーのディレクトリに保存する必要があります。これにより、サンドボックス化が有効になります。

前述の「ワーカー」属性を持つルール定義の場合、入力を表す「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 コードベースでは、統合テストで使用する JSON ワーカーの例に加えて、Java コンパイラ ワーカーを使用しています。

そのスキャフォールディングを使用して、正しいコールバックを渡すことで、Java ベースのツールをワーカーにできます。

ワーカーを使用するルールの例については、Bazel のワーカー統合テストをご覧ください。

外部の協力者がワーカーをさまざまな言語で実装しています。Bazel 永続ワーカーの Polyglot 実装をご覧ください。GitHub で他の例もご覧いただけます