スケジュールベースのライフサイクルイベントには特に嬉しいEventBridge Schedulerを使ってみた

こんにちは、中山( @k1nakayama ) です。

今回は、昨年の11月にリリースされたAmazon EventBridgeの新機能EventBridge Schedulerについて、使う機会があったのでレポートしたいと思います。

  • Amazon EventBridge Schedulerについて知りたい方
  • 複数のターゲットに対するスケジュールベースでの処理を実装したい方
  • スケーラブルなアプリケーション構築を行いたい方

Amazon EventBridge Schedulerとは

従来のCloudWatch Event、または現在のEventBridgeルールによる時間指定のイベント発火を使われている方は多いのではないでしょうか?
EventBridge Schedulerは、このEventBridgeルールでの時間指定イベントとは別の機能として提供されるもので、EventBridgeルールと比較すると主に下記のような特徴があります。

  • 1回限りの時間指定イベントを登録できる
  • 100万以上のスケジュールを登録することができる(EventBridgeルールは300まで)
  • 270以上のAWS SDKを利用したターゲットと、6,000以上のAPIアクションをイベントターゲットに設定可能
  • 全てのタイムゾーンとサマータイムに対応
  • タイムウィンドウスケジュール(RDS等で馴染みのあるメンテナンスウィンドウのようなもの)に対応
  • EventBridgeルールでも利用できるCron式、Rate式での反復スケジュールの登録が可能

もうこれを見ただけでも、今後のスケジュールベースのイベント発火はEventBridge Schedulerを使っていく形で良さそうですね。
ちなみにコストについては、1,400万回の実行まで無料利用枠内で実行でき、その後1万回毎に$1.25というのが、現時点の東京リージョンでの料金であり、EventBridgeルールでの実行は100万回毎に$1なので、月間1,412万回以上のスケジュールイベントを実行する場合は、EventBridgeルールのほうが安いと思いましたが、EventBridgeルールは毎分実行させるルールを300個登録することが最大であり、結果的に1,339万回の実行が月間上限になりそうなので、やはりどのケースに置いても今後はEventBridge Schedulerを選択していくで良さそうです。

実際に使ってみた

今回の想定シナリオ

今回は、プロジェクトのIssue管理を含め全般的に使用しているGitLabのイシューを管理するボットを作りました。多くのプロジェクトにおいて、カンバンやスクラムで開発を進めていきますが、基本的にイシューの粒度は1日以内に完了する程度の大きさにしています。そのように作られているイシューが、WIPステータスに変わってから2日間以上ステータスが変わらないとすると、何か問題が発生していそう。更に2日間(合計4日間)以上ステータスが変わっていなければ、プロジェクトの進行に危険な状態と考えることにしたいと思い、今回はこのヘルスステータスを管理する処理を実装したいと思いました。

GitLabでは、WebHookを利用することで、イシューに対する様々なイベントを通知させることができ、APIを使用することで、イシューに対する様々な操作を行うことが可能です。ただし、イシューの属性情報として管理されているもののうち、日時を表す属性はイシューが作成された日時、イシューに対する更新が最後に行われた日時、イシューがクローズされた日時の主にこの3項目となります。また、GitLabには残念ながらカスタムフィールド的なものは作れず、ラベルとして設定することぐらいしか行なえません。

さて、このような機能を実装する際、あなたならどの様に実装していきますか?
私が最初に思いついたのは、WIPステータスのイシューをAPIで取得し、最終更新日時が48時間より前のイシューに対しヘルスステータスをNeeds Attentionに更新する処理を、毎分や10分毎に実行していく方法ですが、これには問題があり、WIPステータスに入った後、何らかの属性が更新されていると最終更新日時は更新されるため、実際には48時間経過しているイシューであっても、Needs Attentionに移行させることが出来ません。
次に思いつく方法は、WIPステータスに移行した時点で、外部のDB(DynamoDBなど)にWIPステータスに移行した日時やNeeds Attentionに移行する予定の日時を記録しておき、それらの情報を対象に更新を行う処理を毎分実行していく方法です。これについては、十分現実的ではありそうですが、イシューの数がそれほど多くない場合、無駄に処理が実行されコスト増に繋がります。

このような処理対象が増減し、定時実行ではないスケジュールイベントを処理する際には、今回のEventBridge Schedulerはとても使い勝手のいいサービスとなります。

アーキテクチャ

今回のヘルスステータスを更新する処理について、EventBridge Schedulerを利用したアーキテクチャは上記の図の通りです。

  1. イシューがWIPステータスに移行すると、WebHookによりイベント通知が行われる
  2. 48時間後の日時でNeeds Attentionのヘルスステータスに移行させるイベントをEventBridge Schedulerに登録する
  3. 48時間が経過すると、EventBridge SchedulerによりカスタムEventBusに対しPutEventが実行される
  4. EventBridge Ruleによりイベントを検知し、API Destinationsに対してAPIコールを促す通知を行う
  5. API DestinationsからGitLab Issue APIに対し、対象イシューのヘルスステータスラベルを更新するイベントを送信する

上記の後、Needs Attentionヘルスステータスに更新されたイベント通知を受けて、At Riskヘルスステータスに移行するスケジュールも同様に処理できます。

これにより無駄なく処理が実行でき、仮にイシューが何万、何十万と作られていた場合でも(それはそれで問題ですが)、イシューが数個のときと変わりなく処理を実行することが出来ます。
※API Destinationsを使用してAPIコールすることにより、同タイミングで多くの更新が重なってしまいGitLab API側のレートリミットに引っかかるような場合があっても、リトライが自動化されており、良しなに処理を行ってもらえます。

使ってみて気づいた点

今回この実装を行ってみて、注意するべき点として気づきを得たものが2点ありました。

1つは、今回の様に1回限りのスケジュールも、実行を終えた後もスケジュールは残り続けるという点です。登録可能なスケジュールは、デフォルトのクォータで100万スケジュールとなっており、実行済みスケジュールもこの100万スケジュールに含まれます。そのため、不要となったスケジュールは、実行後に削除することを検討する必要があります。

2つ目は、時間の問題で解決することですが、今回のLambdaはPython3.9で実装しましたが、現時点のLambdaがネイティブに提供するboto3バージョンは1.20.32であり、EventBridge SchedulerのAPIをサポートしたバージョンは1.26.7となるため、現時点ではboto3をLambda Layerなどで別途与える必要がありました。

スケーラブルなアプリケーション構築には欠かせない

冒頭のEventBridge Schedulerについての紹介の中でも記載した通り、EventBridge Schedulerは様々なスケジュールイベントを実行していく上でEventBridge Ruleに代わって使われていくことになるサービスです。
しかし、EventBridge Schedulerを使用していく上では、従来の発想とは異なる観点で、よりスケーラビリティを上げていく使い方をしやすいサービスです。この特徴を活かすことで、今回のような特定のイベント発生から一定時間後に実行するイベントや、定時に実行するイベントであっても対象が増減するイベントなどについては、イベントを細分化した個々のスケジュールを管理していくことで、よりスケーラブルに効率よくイベントを管理していくことができます。

一例として、下記のような処理に活かせます。

  • サービスの登録後1日目、2日目、1週間後などのタイミングで送信するイントロダクションのためのメール送信機能
  • SaaSのユーザー毎の月次利用料金を計上するための決済処理
  • 削除処理後、一定期間経過後に実データを削除する処理

皆さんの開発されているプロダクトなどにおいても、上記のようなスケジュールイベントは1つ、2つあるのではないでしょうか?この機会によりスケーラブルなアプリケーションとなるよう、見直してみてはいかがでしょうか?

無料相談実施中
AWSを使用したサーバーレスアプリケーションの構築
サーバーレス開発内製化、AWSへの移行等
様々な課題について無料でご相談お受けいたします。
Amazon EventBridge
最新情報をチェックしよう!