これは何?
SQS+PostgreSQL+Lambdaとその間をつなぐデーモンで構成されたジョブキューシステムです。 入り口のSQLにメッセージを投げると、メッセージで指定した関数名のLambdaが実行されます。 Lambdaの実行に失敗した場合は、適当な感覚でリトライされます。
Ruby製のジョブキューシステムQueとそのクローンであるqgを模した作りになっています。
モチベーションとか
コンテナ環境で使いやすいジョブキューシステムが欲しかったのが、モチベーションです。
Resqueのようにプロセスとしてジョブを起動するタイプだと、コンテナのライフサイクルとのミスマッチでスケールイン・スケールアウトが難しので、コンテナを使うならなるべくクラウド上の何か(ECS・Lambda…etc)でジョブを実行したいのですが、一方で完全にAWSリソースだけでジョブキューシステムを作ると、特にSQSの使い勝手が悪くキューの可観測性が低いので、もう少し使い勝手の良い何かが欲しいなーと。
アーキテクチャ
特徴
- スケーラビリティとメンテナンシビリティを高くすることを目標に設計しています
- 各デーモンはプロセスレベル・Goroutineレベルでスケールアウトできます
- ジョブの実行は遅れますが各デーモンは停止可能です
- たとえば「デーモンを一時的に停止してDBのアップグレードを行う」といったことが可能です
- SQSのメッセージIDをDBのPK・構造化ログ・Lambdaのeventに出力しているので、メッセージIDでジョブの動作をトレースできます
- ジョブキューとしてPostgreSQLを使っているので、SQSに比べて検索性が良いです
Getting Started
READMEに書いたとおりなんですが
1. docker-composeの起動
$ docker-compose up
2. terraformの適用
$ make tf-init $ make tf-apply
※LocakStackに対してterraformを適用します
3. DBのセットアップ
$ make db
4. デーモンの再起動
初期状態ではデーモンが起動に失敗しているので、再起動します。
$ make restart
以上でサーバのセットアップは完了です。
最後にSQSにメッセージを投げるとLambdaが実行されることを確認できます。
$ make message
サンプルのLambdaはこんな感じです:
make failure
を実行すると、Lambdaが失敗するメッセージをSQSに投げるので、ジョブがリトライする様子を確認できます。
$ make failure
DBに残っているジョブは http://localhost:8081 から確認できます。
QA
なんでLambdaなの?(Step Functions / ECSではないの?)
- Lambda Destinationsが便利だったので…
- SFn / ECSを実行しようとすると、AWSのリソースが増えてやや複雑になる/Destinationsのような処理を自前でやることになりそう
- Lambdaだと15分の時間制限があるが、それを超えるようなジョブはバッチ処理に移譲してほしい気持ち
- Dockerイメージが使えるのでECSとの併用はそんなに難しくはない…はず
クライアントから直接Lambdaを非同期で実行すればよいのでは?
- Lambdaを非同期に実行するときに使われる内部的なキューが可視化されていないようなので、キューの状態を確認するのが難しそう
- Lambdaの並列数上限に達してクライアントからのAPIコールが失敗するのが嫌
SQSからLambdaを実行すればよいのでは?
- SQSの検索性が非常に悪いので、メッセージをSQSにためておきたくない
- キューのメッセージの確認のために、ReceiveMessageでメッセージを受信して確認し、VisibilityTimeoutで再送を待つような破壊的な動作をやりたくない
DynamoDBを使えばよいのでは?
- SKIP LOCKEDを使ったPostgreSQLのキューのように、排他制御を実装できるかわからなかった
- PostgreSQLのかっちりとした型制約がほしかった
どこかのプロダクションで使ってるの?
- まだです。そのうち突っ込みたい…