CIにおける機械的な失敗とは?解説
機械的な失敗とは、コードではなく、out-of-memory killやプリエンプトされたインスタンスのように、jobを実行しているマシンやインフラに根ざした失敗です。
人が build が「失敗した」と言うとき、たいていはテストが失敗したという意味です。しかし、失敗のかなりの割合はコードにまったく到達しません。runnerがメモリ不足になった、ディスクが一杯になった、インスタンスが回収されたなどです。これらの機械的な失敗は環境的なものであり、ロジックのエラーとはまったく異なる振る舞いをします。
何が機械的とみなされるか
- out-of-memory kill(プロセスがSIGKILLされ、多くの場合exit code 137として現れる)。
- runner上のディスクフルやinode枯渇の状態。
- job途中でのspotまたはpreemptibleインスタンスの回収(多くの場合SIGTERM、exit 143)。
- runnerのプロビジョニングの不調や、プロバイダーの短時間のインシデント。
機械的な失敗 vs 論理的な失敗
論理的な失敗はコードやテストから生じます。assertionが偽である、型が合わない、ファイルが欠落しているなどです。機械的な失敗はマシンから生じます。リソースが尽きた、あるいは取り上げられたのです。論理的な失敗は毎回再現しますが、機械的な失敗は一時的なリソース状態に依存するため通常は再現しません。
なぜ紛らわしく見えるのか
機械的な失敗は、役立つエラーではなく難解なシグナルとして現れることがよくあります。out-of-memory killは「Killed」という単語とexit code 137だけを残し、プリエンプションは一般的なキャンセルを表示することがあります。コンテキストがないと、これらはflakyなテストやランダムな失敗として誤分類されます。
正しい対応:リトライ
機械的な失敗は環境的なものであるため、正しい対応は通常リトライで、理想的には十分なリソースを持つ新しいrunnerで行います。論理的な失敗をリトライしても無意味ですが、機械的な失敗のリトライは、一時的なリソース状態が過ぎ去っているため、しばしば成功します。
Latchkeyの視点
機械的な失敗は、まさにLatchkeyの自己修復managed runnerが捕捉するために作られたものです。プラットフォームはout-of-memory kill、ネットワークの瞬断、registryのタイムアウトといった機械的および一時的な失敗を検出し、自動的にリトライします。そのため一度きりの瞬断でbuildが失敗することはなく、一方で本物のコードの失敗は依然として素早く失敗します。
重要なポイント
- 機械的な失敗は、コードではなくマシンやインフラにあります。
- out-of-memory kill、ディスクフル、プリエンプションが典型例です。
- 論理的(コード)な失敗と異なり、これらはめったに再現しません。
- 正しい対応は通常リトライで、多くの場合は新しいrunnerで行います。