PRを自動作成するBotが並列で動いて、一部だけプレビューURLが出なかった話|GitHub Actionsのconcurrency設定で解決

概要

今回はGitHub Actionsでトリガーが同時多発すると、PRのプレビューURLコメントが一部だけ出なくなるという事象について紹介していきます。

自分のブログでは、GitHub Issueに特定のラベルを付けると、その内容を元にブランチを作成してPRを自動発行するワークフローを運用しています。

PRを作ると、Cloudflare Workers Buildsとのgit連携によって自動ビルドが走り、成功すればプレビューURLがコメントで届く仕組みです。

しかし、そのトリガーが1分弱の間に5件連続で来て、5件のPRがほぼ立て続けに作成されたのですが、そのうち2件だけプレビューURLコメントがいつまでも届がないという事象が発生しました…

しかもワークフロー自体はGitHub Actions上で「成功」表示で、エラーも何も出ていない(- -;

今回はその事象を頑張って解決したので、その方法を紹介します。

それではやっていきましょう!

目次

前提となるシステム構成

自分のブログでは、次のような仕組みでPRの自動発行とプレビュー環境を連携しています。

  • PR自動発行ワークフロー
    • GitHub Actions上で、Issueへのラベル付与などのトリガーを受けると、その内容を元にブランチを作成し、PRを自動発行する
  • Cloudflareのプレビュー連携
    • PRを作成すると、Cloudflare Workers Builds(GitHubリポジトリとのGit連携機能)が自動でビルドを実行し、成功すると「プレビューURL」を投稿するコメントがPRに自動で付く

普段はPRを作成すると数分以内にCloudflare Workers BuildsからプレビューURLがコメントで投稿されてました。

起きた症状:5件同時作成のうち2件だけコメントが来ない

ある時、複数のトリガーがほぼ同時(1分弱の間隔で5件連続)に起動し、5件のPRがほぼ同時に作成されました。

その後、5件のうち3件には通常通りプレビューURLコメントが付いたのですが、残り2件(間に挟まれた2件)にはいつまで経ってもコメントが付かないという事象がおきました。

しかもワークフロー自体はGitHub Actions上で「成功」表示になっていて、エラーは何も出ていません。

単発でPRを作成したときにはこの現象は一度も起きていなかったので、訳がわかりませんでした(- -;

調査:単体のログから、横並び比較に切り替える

まずコメントが付かなかった2件のPR単体を疑って調べたのですが、そこからは手がかりが見つかりませんでした。

  1. コメントが付かなかった2件について、GitHub Actions側のワークフロー実行ログと、PRのステータスチェック(statusCheckRollup)を確認しましたが、異常や失敗の痕跡は見当たりませんでした。
  2. そこで方針を変えて、直近で作成された他のPR(コメントが付いているもの/付いていないもの)を横並びで比較することにしました。次のようなコマンドで、Cloudflare bot(cloudflare-workers-and-pages)のコメントの有無と、各PRの作成時刻を突き合わせます。
1
gh pr view <番号> --json comments
  1. その結果、コメントが付かなかった2件は、いずれも短時間(1分弱)に連続作成された5件のPRの中に含まれており、かつ前後のPR(3件)には正常にコメントが付いていた、という規則性に気づきました。

単発で作成されたPRでは今回のような欠落は一度も発生していなかったのも、この規則性を裏付ける材料になりました。

根本原因:ワークフローの並列実行とCloudflare側のビルドキュー詰まり

原因はトリガーとなるワークフローに並行実行の制御が無かったため、複数のトリガーがほぼ同時に来ると、ワークフローのジョブが並行実行され、短時間のうちに立て続けにPRが作成されていたことです。

PR作成のたびにCloudflare Workers Buildsへ新しいビルドがトリガーされますが、短時間に集中してビルドトリガーが飛ぶと、Cloudflare側のビルドキュー処理(同時実行数の制限など)により、一部のビルドトリガーが処理されずに終わるケースがある、というのが根本原因でした。

常に失敗するわけではなく、「PRが短時間に集中して作られた場合にのみ、その一部で」たまに発生するという点が、なかなか苦しめられたポイントですね。

対応

Cloudflareのビルドキュー詰まり自体は、こちら側で直接制御できない外部サービスの挙動です。

そのため、即時対応と再発防止の2段構えで対応しました。

即時対応:空コミットで再トリガー

コメントが付かなかった2件のPRの各ブランチに空コミットをpushし、新しいコミットとしてCloudflareに再度ビルドを検知させ、プレビューURLコメントを再生成させました。

実際に両PRとも数分以内にデプロイ成功のコメントが付いたので、応急処置としてはこれで十分でした。

再発防止:concurrencyで直列化

再発防止として、トリガーとなるワークフローのジョブに、GitHub Actionsのconcurrency設定を追加し、リポジトリ全体で直列化しました。

1
2
3
concurrency:
group: <ワークフロー名>
cancel-in-progress: false
`cancel-in-progress: false`にしている点がポイントです。
これを`true`にすると「新しいトリガーが来たら実行中の古いジョブを中断する」動作になってしまい、先に走っていた分の処理が消えてしまいます。
今回は「複数の独立したPRを1件ずつ順番に作りたい」だけなので、キャンセルはせず待たせる(キューイングする)設定にしました。

これにより、複数のトリガーがほぼ同時に来ても、ジョブが1件ずつ順番に実行されるようになり、PR作成/Cloudflareへのビルドトリガーが自然に時間的に分散されます。

concurrency設定の有無で、プレビューURLコメントの届き方がどう変わるか(NG:並列実行 / OK:直列化)
concurrency未設定だと5件中2件でプレビューURLコメントが欠落し、concurrencyで直列化すると全件成功する比較フロー図

締め

今回は失敗したPR単体のログをいくら見ても手がかりが無く、正直そこで詰まっていました。

Botが自動でPR・ブランチを作る仕組みは、トリガーが1件ずつ来る前提で作ってしまいがちですが、複数件が同時に来た場合もちゃんと対応しないとダメですね。
キューは大切です。

外部サービス側の同時実行数やレート制限は自分では直接直せませんが、自分側のトリガーの並行度をconcurrencyでコントロールすることで、防げるというのも得られた点です。

以上となります。
微妙な設定で大きな障害が起きるので、注意しないとですね!
それではお疲れさまでした。