みなさんこんにちは。
私は現在関わっている案件で、オンプレでRabbit MQを私用するシステムをSQSに置き換えてAWS移行するお仕事があります。
SQSの設計などは自分の担当ではないのですが、SQSについて最近自分もよくわかってないなという点が出てきたのでGW中に検証して詳しくなりたいなと思いました。
MessageGroupIDについて検証したい
Rabbit MQではキュー単位でFIFOになるのですが、SQSではMessageGroupID単位でのFIFOとなります。つまり同じキューにメッセージが送信されたとして、これまでのRabbit MQとの考え方と変わるポイントですね。何も考えずにメッセージを送信すれば良かったのが、MessageGroupIDを意識する必要が出てきました。
このMessageGroupIDが同一ならFIFOで処理されるということは頭ではわかっているのですが、実際に見える形で検証したことはなかったなと思いLambda関数を使ってパターンで検証してみたいと思います。
検証に先立って、SQSからメッセージを受け取るとMessageGroupIDとメッセージ本文を出力するLambda関数を作成しました。
package main
import (
"context"
"fmt"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
func handler(ctx context.Context, sqsEvent events.SQSEvent) error {
for _, message := range sqsEvent.Records {
groupID := message.Attributes["MessageGroupId"]
fmt.Printf("GroupID: %s, Body: %s\n", groupID, message.Body)
}
return nil
}
func main() {
lambda.Start(handler)
}
①単一のMessageGroupIDでメッセージをキューに送信してみる
検証①では、単一のMessageGroupIDでメッセージをキューに送信した際にFIFOで処理されるか検証してみたいと思います。
以下のように、TypeだけFIFOに設定し残りは標準設定でSQSのキューを作成します。
(AWSマネコンの言語変更がどこでできるから知らないので、ずっと英語環境のAWS使っています。)
キューの作成が完了したら、次はLambda関数側にSQSトリガーの設定を行います。
トリガーの追加からメニューを開いてTrigger TypeにSQSを指定、SQS queueの部分にキューのARNを入力して設定は完了です。
(標準ではBatch Sizeが10となっていますが、今回は1に変更しました。)
トリガー設定後にOKを押すとLambda関数にトリガーが設定されます。
トリガーが作成されたので実際にメッセージを送信してみます。
・Message bodyには適当な内容
・Message group IDにMsgGrp1
・Message duplication IDはインクリメントとするので一旦1を入れます。
メッセージの設定後にメッセージ送信ボタンを押してみます。
メッセージ送信ボタンを押した後に、CloudWatchからLambdaのログストリームを確認すると、メッセージ内容とMessage group IDが出力されていました。これでLambda関数とSQSトリガーの設定の確認が完了しました。
FIFOで処理されることの検証
LambdaのSQSトリガーをDisabledにした状態で、SQSキューにTest01 ~ Test10まで順番に10件のメッセージを送信しました。トリガーをEnabledにするとLambda側で処理が始まるのですが、CloudWatchでログを確認した際に01から10まで順番に出力されていればFIFOが視覚的に確認できますね。
以下の表が実行時のログを表にまとめたものとなります。
Message Bodyが01から10まで昇順となっているので、FIFOでキューにメッセージを流した順番に処理されていることが確認できました。
| Timestamp (Z) | Request ID | Group ID | Message Body |
|---|---|---|---|
| 13:23:01.122 | 4a5c0b33... | MsgGrp1 | Test01 |
| 13:23:01.146 | 03ca42ba... | MsgGrp1 | Test02 |
| 13:23:01.195 | babeb72c... | MsgGrp1 | Test03 |
| 13:23:01.242 | 2319a291... | MsgGrp1 | Test04 |
| 13:23:01.269 | ff387718... | MsgGrp1 | Test05 |
| 13:23:01.334 | a589b2a8... | MsgGrp1 | Test06 |
| 13:23:01.382 | 4ac53ab8... | MsgGrp1 | Test07 |
| 13:23:01.405 | 3761d225... | MsgGrp1 | Test08 |
| 13:23:01.469 | 3d643301... | MsgGrp1 | Test09 |
| 13:23:01.487 | e3b469e0... | MsgGrp1 | Test10 |
これで①の検証は終わります。Message Group IDに基づきFIFOで処理されることは頭では知っていたのですが、実際にFIFOで処理されることまで検証できて嬉しみです。
②複数のMessageGroupIDでメッセージをキューに送信してみる
次は複数のMessage Group IDが混在の状態でキューにメッセージを送信してみました。途中でどのIDが何件送られているかカウントを忘れたので件数自体は適当になっていますが、Message Bodyの数字はMessage Group ID単位でインクリメントされています。
メッセージ自体は14件あり、Lambdaの同時実行数も5に設定され3インスタンス使用されていたのですが、いい感じに頑張ってログを表にまとめました。
| タイムスタンプ (Z) | インスタンス | Message Group ID | Body | Request ID (省略) |
|---|---|---|---|---|
| 13:51:30.699 | A | MsgGrp1 | Test01 | b9ac630b... |
| 13:51:30.739 | A | MsgGrp2 | Test01 | 75f40b84... |
| 13:51:30.779 | A | MsgGrp3 | Test01 | 5af23a4f... |
| 13:51:30.800 | A | MsgGrp1 | Test02 | 1f185477... |
| 13:51:30.886 | A | MsgGrp3 | Test02 | 217b1d2d... |
| 13:51:30.910 | A | MsgGrp3 | Test03 | 6d6b1ea3... |
| 13:51:30.951 | B | (INIT) | Cold Start | - |
| 13:51:30.953 | C | (INIT) | Cold Start | - |
| 13:51:30.965 | A | MsgGrp3 | Test04 | 4d9950e3... |
| 13:51:31.024 | C | MsgGrp1 | Test03 | e3ea2fd4... |
| 13:51:31.028 | B | MsgGrp2 | Test02 | 0779a2dd... |
| 13:51:31.042 | C | MsgGrp1 | Test04 | f040a51c... |
| 13:51:31.047 | A | MsgGrp2 | Test03 | c5fb0bd5... |
| 13:51:31.073 | C | MsgGrp1 | Test05 | dbb9cfed... |
| 13:51:31.106 | C | MsgGrp2 | Test04 | c225e914... |
| 13:51:31.128 | C | MsgGrp2 | Test05 | 8847fa26... |
| 13:51:31.153 | C | MsgGrp2 | Test06 | 2a0f4450... |
どうでしょうか?ややみづらいかもしれませんが、Message Group IDを基準としてみてみるとこちらはFIFOで処理されていることが確認できるかと思います。
自分の勘違いが解消された
自分は先輩から、「同一のMessageGroupIDを持つメッセージは、一度に1つのLambdaインスタンスでのみ処理される」と聞いていたのですが、この1つのLambdaインスタンスという点で同一のインスタンスが使われると勘違いしていました。
しかし、今回の検証から分かるようにこの1つというのは絶対値を表しており、「ある瞬間において、そのMessage Group IDを処理しているLambdaは世界に1つだけ」という絶対的な並列数(同時実行数)の制約のことですね。
つまり実行されるインスタンス自体はEvent Source Mapping側での判断となるので、Lambdaのメモリ内に状態を持たせてはダメというサーバーレス設計の掟がより深く理解できました。
この記事を書きながら作業用BGM的に聴いてた曲を紹介します。
番長シリーズの中でステージBGMの中で一番かっこいいと個人的に思っています。
[頂RUSH ~渓谷ステージ~/Daito Music (押忍!番長2 SOUND TRACK より)]
https://www.youtube.com/watch?v=W1Fdl2tErjk&list=RDW1Fdl2tErjk&start_radio=1




