Bencher Runner プロトコル
runner バイナリ と API サーバーは、単一の WebSocket 接続を通じて
通信します。このリファレンスでは、そのプロトコル、すなわちやり取りされるメッセージ、それらが
駆動する Job のライフサイクル、そしてタイムアウトと再接続によって Job が停止しないように
する仕組みについて説明します。これは セルフホスト型 Runner ガイドの詳細な補足です。
runner up で Runner を運用するためにこれらを知る必要はありません。
これは透明性のため、また API の周辺でツールを構築する人のために提供されています。
接続
Runner はそのライフサイクル全体を通じて、API サーバーへの単一の WebSocket 接続を維持します。 同じ接続が Job の割り当てと Job の実行の両方を処理し、 多数の Job にわたって開いたままになるため、Job ごとの再接続とハンドシェイクを回避します。
- エンドポイント:
/v0/runners/{runner}/channel - 認証: 接続が確立される際に、Runner キーが
Authorization: Bearer bencher_runner_<key>ヘッダーとして送信されます。 - メッセージサイズ: 各メッセージは、サーバーの
request_body_max_bytes制限によって上限が定められます (最大メッセージサイズとフレームサイズの両方に適用されます)。大きなstdout、stderr、または出力ファイルを運ぶcompletedペイロードのように、この制限を超えるメッセージは、WebSocket プロトコルレベルで拒否されます。
すべてのメッセージは、その種類を識別する event フィールドを持つ JSON オブジェクトです。
Runner メッセージ
Runner からサーバーに送信されるメッセージ。
| イベント | 説明 | ペイロード |
|---|---|---|
ready | Runner はアイドル状態で Job を要求している | オプションの poll_timeout (1〜900 秒) と runner メタデータ (os、arch、version) |
running | Job のセットアップが完了し、ベンチマークが開始する | なし |
heartbeat | 定期的な生存シグナル (約 1 秒に 1 回) | なし |
completed | ベンチマークが正常に完了した | job (Job UUID) と results (反復ごとの出力) |
failed | ベンチマークが失敗した | job (Job UUID)、results、error |
canceled | サーバーからのキャンセルを確認応答する | job (Job UUID) |
サーバーメッセージ
サーバーから Runner に送信されるメッセージ。
| イベント | 説明 | ペイロード |
|---|---|---|
ack | 受信したメッセージを確認応答する | オプションの job (Job UUID) |
job | 要求された Job を Runner に割り当てる | 要求された Job: その Spec、Job 設定、そして短命の OCI プルトークン |
no_job | ポーリングタイムアウトが切れ、利用可能な Job がなかった | なし |
cancel | Job がキャンセルされたかタイムアウトした。実行を停止する | なし |
update | Runner は新しいバージョンへ自己更新すべき | version、url (ダウンロード URL)、checksum (SHA-256) |
job メッセージ内の OCI プルトークンは、Job が要求された際に生成され、保存されることはありません。
これは Job が属する単一のプロジェクトにスコープが限定され、プル専用かつ短命であるため、
侵害された Runner は、自身が要求した Job のプロジェクトの Image しかプルできません。
接続フロー
接続後、Runner はアイドルのポーリングループに入り、
サーバーが job を割り当てる
(またはポーリングがタイムアウトすると no_job を、新しいバージョンが利用可能になると update を返す)
まで ready を送信し続けます。
Job を取得すると、Runner は running を送信し、
ベンチマークの実行中は heartbeat メッセージをストリーミングし、
最終的に終端メッセージである completed または failed で終了します。
サーバーは各メッセージを ack で確認応答し、
接続は開いたままになるため、Runner は次の Job のためにアイドルループへ戻ります。
Job がキャンセルされると、サーバーは heartbeat に対して cancel で応答します。
Runner はベンチマークを停止して canceled で応答し、サーバーはそれを確認応答します。
Job のライフサイクル
各 Job は、要求、実行、処理される過程で、決まった一連の状態を遷移します。
| 遷移元 | 遷移先 | トリガー |
|---|---|---|
| pending | claimed | Runner が Job を要求する |
| pending | canceled | ユーザーが Job をキャンセルする |
| claimed | running | Runner が running を送信する |
| claimed | failed | Runner が failed を送信する、またはハートビートがタイムアウトする |
| claimed | canceled | ユーザーが Job をキャンセルする |
| running | completed | Runner が completed を送信する |
| running | failed | Runner が failed を送信する、またはハートビートがタイムアウトする |
| running | canceled | ユーザーが Job をキャンセルする、またはハードな Job タイムアウトを超過する |
| completed | processed | サーバーが結果を正常に処理する |
| failed | completed | Runner が completed を再送し、ハートビートタイムアウトによる失敗を上書きする |
processed と canceled は終端状態です。
completed と failed は準終端状態です。
completed は結果が解析されると processed に遷移し、
failed は Runner が completed を再送すると completed に遷移します。
すべての遷移は、データベース更新時にステータスフィルターを使用するため、
並行して変更された Job は上書きされるのではなく、再読み込みされます。
タイムアウトと回復
Runner がクラッシュしたり接続を失ったりした場合でも、 Job が決して停止しないようにする 3 つの補完的な仕組みがあります。
ハートビートタイムアウト
接続が開いている間、読み取りタイムアウトが、接続されているが応答のない Runner を検出します。
有効なプロトコルメッセージのみがタイマーをリセットします。
無効な JSON、ping/pong フレーム、バイナリメッセージはリセットしません。
タイムアウト時、自身のタイムアウトに猶予期間を加えた時間を超えて実行された Job は canceled とマークされ、
そうでない場合は failed とマークされます (Runner との接続が失われたため)。
ハードな Job タイムアウト
サーバーは Runner の挙動とは独立して、ハードな最大実行時間を強制します。
これにより、バグのある、または侵害された Runner が、ハートビートを送信し続けて無期限に実行することはできません。
制限 (Job のタイムアウトに猶予期間を加えた時間) を超えると、
Job は canceled とマークされ、Runner は cancel メッセージを受信します。
切断からの回復
Job がまだ進行中に接続が切断された場合、
サーバーはハートビートタイムアウト後にチェックをスケジュールします。
Runner が再接続してハートビートを再開していれば、Job は継続されます。
そうでない場合、Job は failed とマークされるか、ハードタイムアウトを超過していた場合は canceled とマークされます。
起動時には、サーバーは孤立した claimed の Job も回復し、
進行中の Job のタイムアウトを再スケジュールし、
結果は保存されたがまだ解析されていない completed の Job を再処理します。
再接続と結果の配信
再接続はサポートされており、冪等です。
すでに実行中の Job に対して running を再送しても、その生存状態が更新されるだけであり、
終端メッセージである completed、failed、canceled の再送は常に安全です。
終端メッセージは Job UUID を運び、ack を受け取ります。
ack が届く前に接続が切断された場合、
Runner は結果を保存し、次の接続でアイドルに戻る前にそれを再送します。
Runner の実際の completed 結果は、ハートビートタイムアウトによる failed ステータスを上書きすることさえできます。
自動リトライなし
failed の Job は自動的にリトライされません。
失敗したベンチマークは隠すべきエラーではなくシグナルであるため、
再実行はあなたに委ねられています。
Job の出力
Runner が completed または failed を送信すると、
完全な出力は、コンテナ Image に使用されるのと同じ OCI ストレージバックエンドの
{project}/output/v0/jobs/{job} というパスに保存されます。
保存された出力には、反復ごとの results 配列と、失敗時には error 文字列が含まれます。
各反復は、その exit_code、stdout、stderr、
そして収集された出力ファイルとその内容のマップを記録します。
出力が保存された後、サーバーは結果に対して ベンチマークハーネスアダプター を実行し、
Metric と Alert を解析して Report に取り込み、Job を processed へ遷移させます。
出力は、GET /v0/projects/{project}/jobs/{job} API で Job を照会したときに返されます。
WebSocket メッセージの上限を定めるのと同じ request_body_max_bytes 制限が、
Runner が配信できる出力のサイズを制限します。