概要
ElasticBeanstalk の worker environment には VisibilityTimeout, ErrorVisibilityTimeout, InactivityTimeout というよく似た名前の設定値があり,それぞれ何を意味するのかについて整理しました.
公式ドキュメントの説明
公式ドキュメントには以下のように記載されています:
パラメータ | 説明 |
---|---|
VisibilityTimeout | 「Amazon SQS キューからの着信メッセージが処理のためにロックされる時間」 |
ErrorVisibilityTimeout | 「明示的なエラーで処理が失敗した後、Elastic Beanstalk が Amazon SQS キューにメッセージを返すまでの経過時間」 |
InactivityTimeout | 「アプリケーションへの既存の接続が応答するまでの待機時間」 |
VisibilityTimeout
VisibilityTimeout とは SQS API のパラメータで, sqsd が GetMessage APIを呼び出すときに引数として設定します.設定値の意味は,メッセージが可視(visible) である時間の上限です.
message の可視性(visibility) というのは SQS の概念で,メッセージの排他処理を実現するための機構です.message が visible なときはすべての worker がメッセージを見ることができますが,ある worker が GetMessage すると,メッセージは一定の期間不可視(invisible) となり,他の worker からは取得できなくなります.このメッセージが不可視となる期間が VisibilityTimeout です.
メッセージが取得されてから VisibilityTimeout を経過したメッセージは,SQS マネージドサービスによって再び可視(visible) 状態となり,ほかの worker によって再処理されます.後述する ErrorVisibilityTimeout が設定されていない場合,VisibilityTimeout がメッセージの再処理間隔と同義になります.(詳しい説明は公式ドキュメントを参照)
ErrorVisibilityTimeout
次に,ErrorVisibilityTimeout というのは VisibilityTimeout と似ていますが,こちらはエラー処理のときに使われる設定値です.デフォルトでは未設定です.
ErrorVisibilityTimeout が設定されている場合,アプリケーションが明示的にエラーを返した場合に, sqsd は SQS の API ChangeMessageVisibility を呼び出し,メッセージの可視期間を ErrorVisibilityTimeout に設定された値に変更します.
すなわち,明示的なエラーとなった場合に,あらかじめ設定された VisibilityTimeout の値とは独立して再処理間隔を設定することができます.VisibilityTimeout は worker の処理時間よりも十分長い値が設定されているので(デフォルト値は 300 sec),一度エラーを返したメッセージが次に再処理されるまでに長く待つ必要があります.一方で, ErrorVisibilityTimeout が設定されていれば,最初に設定した VisibilityTimeout の経過を待たずに,エラーが起きた時刻を起点として再処理間隔を設定することができます.
また,注意しなければならないのは,アプリケーション側のミドルウェアがタイムアウトした場合です.通常,sqsd とアプリケーションの間には nginx や WSGI のようなミドルウェアが存在する構成にすると思いますが,例えば nginx でタイムアウトした場合,sqsd は nginx のステータスコードをもとにエラーとして処理しますが,アプリケーションは処理を継続しています.したがって,ErrorVisibilityTimeout が短いと,アプリケーションが処理中にも関わらず,アプリケーションを2重に起動してしまう可能性が高くなります.(通常はアプリケーションを冪等に設計するはずなので,実際には2重起動は問題にならないかもしれません.)
InactivityTimeout
最後に, InactivityTimeout ですが,他の2つが SQS API を呼び出すときのパラメータなのに対し,こちらは SQS の worker daemon (sqsd) に閉じたパラメータです.
sqsd が既に確立されたアプリ側とのコネクションに対して POST メッセージを送ってから,アプリケーションが応答を返すまでの待ち時間です.(同様に,アプリ側とのコネクションを確立するときの待ち時間として ConnectionTimeout があります)
シーケンス図による説明
以上が言葉による説明ですが,具体的なケースで実際に sqsd がどのように振る舞うかを理解すると,
よりいっそう理解が深まると思うので,以下では 4つのケースについてシーケンス図で説明します.
- アプリケーションが正常に応答する場合
- アプリケーションがエラーレスポンスを返す場合(ErrorVisibilityTimeout が設定されていない場合)
- アプリケーションがエラーレスポンスを返す場合(ErrorVisibilityTimeout が設定されている場合)
- アプリケーションが InactivityTimeout を超えても応答しなかった場合
1. アプリケーションが正常に応答する場合
まず,アプリケーションが正常な応答を直ちに(タイムアウトよりも十分短い時間で)返す場合です.
sqsd は SQS のキューをポーリングし,メッセージがキューに入ると,GetMessage API を呼び出してメッセージを取得します.取得されたメッセージはSQS上では不可視(invisible)状態,すなわち,他の daemon プロセスからは処理できない状態となります.
次に,sqsd はこのメッセージをパースして HTTP リクエストに変換し,アプリ側に POST リクエストとして送信します.アプリ側が正常なレスポンス(ステータス200)を返した場合は,sqsd は処理が成功したとみなし,DeleteMessage API を呼び出して SQS 上からメッセージを削除します.
以上が最も基本的なシーケンスです.
2. アプリケーションがエラーレスポンスを返す場合(ErrorVisibilityTimeout が設定されていない場合)
次に,アプリケーションが明示的にエラー応答を返す場合で,かつ,ErrorVisibilityTimeout が設定されていない場合について説明します.
sqsd がアプリケーションに POST リクエストを投げるまでは,1つめのシーケンスと同じです.アプリケーションが明示的なエラー(5xx など)を返した場合,sqsd は処理に失敗したとみなしますが,ErrorVisibilityTimeout が設定されていないため,可視性の変更はせず,VisibilityTimeout が経過するのを待ちます.VisibilityTimeout が経過すると,メッセージはSQS上から再び可視(visible)状態になるため,worker プロセスによって再処理されます.
3. アプリケーションがエラーレスポンスを返す場合(ErrorVisibilityTimeout が設定されている場合)
以下は,アプリケーションが明示的なエラー応答と返す場合で,かつ,ErrorVisibilityTimeout が設定されている場合のシーケンスです.
アプリケーションが明示的なエラーレスポンスを返すまでは2つめのシーケンスと同じです.sqsd は処理に失敗したとみなし,ChangeMessageVisibility API を呼び出して,ErrorVisibilityTimeout で設定した値でメッセージの可視性タイムアウト(VisibilityTimeout) を再設定します.ChangeMessageVisibility API が呼び出されてから ErrorVisibilityTimeout だけ経過したら,メッセージは再び可視(visible) 状態となり,worker プロセスによって再処理されます.
4. アプリケーションが InactivityTimeout を超えても応答しなかった場合
最後に,アプリケーションが InactivityTimeout を超えて応答を返さなかった場合のシーケンスを説明します.
sqsd がアプリケーションに POST リクエストを送るまでは1-3つめのシーケンスと同じですが,アプリケーションが応答を返すまでに, InactivityTimeout 以上経過した場合,sqsd はメッセージの処理に失敗したとみなします.その後の処理は,ErrorVisibilityTimeout が設定されているかどうかによって決まり,設定されていない場合は2つめ,設定されている場合は3つめのシーケンスにしたがって処理されます.
終わりに
説明は以上ですが,最後に,sqsd のソースコードの場所を共有しておきます.ubuntu 16.04 の場合,EC2インスタンスの以下のディレクトリにあります.
/opt/elasticbeanstalk/lib/ruby/lib/ruby/gems/2.2.0/gems/aws-sqsd-2.3
ruby の gem で提供されているようですが, public なリポジトリではないようです.
AWSのドキュメントを読みながらだと,細かい仕様が理解しずらかったのですが,ソースコードを見ながらだとすんなり理解することができました.