第12回のオンライン輪読会に備えて読んだ内容を自分なりにまとめておく。
今回の記事はこちら。
[期待/予想する内容]
この記事の目次は次のとおりである。
- はじめに:生命を模倣するアルゴリズム
- キューの二重の性質
- キューベースのシステム
- 非同期システムの障害
- 可用性とレイテンシーの測定方法
- マルチテナント非同期システムのバックログ
- 復元力のあるマルチテナント非同期システムを作成するための Amazon の戦略
- まとめ
- 参考文献
今回は目次が割と詳細に書かれている。というか今週月曜(2020/04/20)夜にちょうどSQSやLambdaの非同期実行で障害がおきたところなので、とても興味深い。
「キューの二重の性質」……二重ってなんだろう?FIFOとFILOとかのはなしだろうか。あまり想像がつかない。
「キューベースのシステム」……抽象的。キューをベースにして非同期に処理が行われていくってことなんだろうけど、ポイントはなんだろう。
「非同期システムの障害」……障害。キューが処理されない、キューが積めない、キューが重複して取得される、などなど色々思いつくけれど、そういう話だろうか。それとも、障害がおきたときの対応方法?
「可用性とレイテンシーの測定方法」……この間のバックオフの回でも、非同期システムの話が出てきていたけど、可用性とかレイテンシーは難しそう。最終的に成功していればOK?それとも、システムごとに定める目標時間に収まっていなかったら障害?そもそも基準とする時間はどうすればいいんだろう。キューに積んだら開始?処理を始めたら開始?ここは重点的に読もう。
「マルチテナント非同期システムのバックログ」……マルチテナントだと、そもそもテナントが異なるメッセージをどう処理するのがいいんだろう。テナントごとのワーカーで処理したりするんだろうか。テナントごとにキューを受け付けるスレッド、プロセスを待たせておく?わからない……。
「復元力のあるマルチテナント非同期システムを作成するための Amazon の戦略」……ここはいつものベストプラクティスのパートっぽい。復元力のある、だから耐障害性があるというか、障害から復旧しやすいアーキテクチャとか、パターンが書かれていそう。要チェック。
[記事の要約]
次に、実際に記事を読んだ内容を箇条書きでまとめる。
はじめに:生命を模倣するアルゴリズム
キューの二重の性質
- キューは高信頼性の非同期システムのためのツール:耐久性と可用性を高める
- ただし、再試行によりレイテンシ増加するリスクがある
- 処理停止時、大量のバックログが発生し、回復後の処理時間が劇的に増加し、可用性が低下する
キューベースのシステム
- AWS Lambdaの例:イベントに応じて関数を実行する
- リトライのために、イベントを永続化するキューが必要
- AWS IoT Coreの例:PubSubメッセージトピックに複数デバイスが接続
- デバイスは往々にしてオフラインなので、メッセージを永続化し非同期処理する
- SQSは耐久性がありスケーラブルなので、LambdaもIoT Coreもこれを利用
非同期システムの障害
- Lambdaの呼び出しイベントがバックログに積まれる場合を考える
- メッセージ処理中に1時間停止したとする:回復後の1時間は処理能力が2倍必要
- Lambda関数の依存関係によっては、スケールしない可能性もあり、さらに回復に時間がかかる
- 反対に、同期システムは障害中の処理をドロップするが回復は早い
- 非同期システムでも1秒以下のレイテンシが期待されるが、大量のバックログが生じるとレイテンシが増加するリスクが隠れている
可用性とレイテンシーの測定方法
- 可用性はプロデューサーから見るコンシューマーから見るかで変わる
- DLQで可用性を考えることもできるが一般的には遅すぎる
- メッセージの経過時間をもとにレイテンシを測定する
- AWS IoTではAgeOfFirstAttempt、AgeOfFirstSubscriberFirstAttemptなどのメトリクスを利用
マルチテナント非同期システムのバックログ
- 多くの非同期システムはマルチテナントであり、レイテンシと可用性の問題がより複雑になる
- リソースを効率的に利用できるがシングルテナントのように動作する必要がある
- つまり、他の顧客のワークロードに影響を与えない公平性=制限が必要
- 1顧客がシステム全体のバックログを生成した場合:コンシューマーがスケールしないと、短時間の障害プロデューサーでも復旧に時間がかかる
- AWSが備えるキューバックログ対策がいくつかある
- 1つめ:あらゆるレイヤで1つのワークロードがリソースを独占しないよう保護する
- 2つめ:複数のキューを利用する
- 3つめ:リアルタイムシステムではLIFO風の動作が好まれる
復元力のあるマルチテナント非同期システムを作成するための Amazon の戦略
- ワークロードを個別のキューに分離する:ポーリングコストによっては分離しない
- シャッフルシャーディング:シャード内でメッセージが少ないキューに入れる
- 過剰なトラフィックを別のキューに並べる:設定されたレートを超えた場合、スピルオーバーキューへ移動。優先順位が付けられている。
- 古いトラフィックを別のキューに並べる:古い場合はバックログキューへ移動。LIFO風に近づけられる。
- 古いメッセージのドロップ(生存期間):完全同期が実行されたタイミング以前のメッセージであればドロップする、など。
- ワークロードごとのスレッドなどリソースの制限:ノンブロッキングI/Oを利用したり、並行処理量をセマフォで制限
- バックプレッシャーをアップストリームに送信する:ただし、バックログがあっても処理を受付けたい場合もある
- 遅延キューを使用して処理を先送りする:サージキューへ入れる際に遅延パラメータを設定する
- 処理中のメッセージを多くしすぎない:FIFOのSQSの場合、20000件制限がある
- 処理できないメッセージはDLQを使う:バグが修正されたのちにこれを処理すべし
- ワークロードごとのポーリングスレッドで追加バッファを確保:空のレスポンスを受け取っているかどうか
- 長時間メッセージの場合はハートビートを送る:過負荷時はレイテンシが増加傾向にあるので重要
- クロスホストデバッグを計画する:トレースIDを利用してX-Rayと統合するか、Step Functionsを利用する
まとめ
[予想のふりかえりなど]
最初の、「生命」は「生活」とかの誤訳じゃないか。生命はちょっと文中の例とは合っていない気がする。それはそれとして。
これだけの知見がありつつも、やはり障害は避けられないのだな、と痛感した。と同時に、これだけの対策をやっておけば、少なくともこのレベルでの障害はそうそう発生しないのだな、と安心できる面もある。
これまで、自分は、SQSの機能で使いこなせていない部分や、使い方(とそれによるメリット)を理解できていない部分があったように思う。これを気に見直しを進めていきたい。
[まとめ]
- 「The Amazon Builder's Library」の『乗り越えられないキューバックログの回避』を読んだ
- キューのレイテンシや可用性の評価はむずかしい
- SQSに標準で備わっている機能の目的・メリットや、ちょっとした使い方のコツを理解することができた
これで12/13記事を読んだことになる。あと1つ!
引き続きAmazon Builder's Library読んでいくぞ。