プッシュ通知のために使用しているDynamoDBのテーブルは、デバイス情報テーブルとプッシュトークンテーブルの2つがあり、それぞれ次のような設計になっています。
テーブル設計で特徴的なのは、デバイストークンの全走査用テーブルを用意している点です。このテーブルは、アプリに登録されたデバイスを全対象とするプッシュ通知配信機能などにおいて、GCM/APNsを介したプッシュ通知に必要なデバイストークンを全デバイス分取得するために利用しています。テーブルからトークンを全て取得する際には、後述する「Scan API」を利用しています。
Scan APIはデータサイズが大きくなるほど読み出し速度のパフォーマンスが低下するため、テーブルを分けることにより全件走査のパフォーマンスを効率化しているのです。
DynamoDBでは、読み書き性能をプロビジョンドスループットとして設定できるため、その設定値を大きくした分だけ高い性能を得ることができます。一方で、予測しない負荷集中が起きたときなどに読み書きの速度がプロビジョンドスループットを超える場合、DynamoDBはAPIリクエストにおいて「スループット超過エラー」を返却するようになります。エラー発生時のリクエストで行うはずだったデータの読み書きは、当然ながら実行されません。
プッシュ通知を行うシステムの特性上、スマートデバイスからのシステムへのデバイス情報更新リクエストは、各端末へのプッシュ通知を行った直後に集中し、DynamoDBのデバイス情報テーブルへの負荷も急増します。このとき、負荷がスループット設定値を超えることで発生する大量のスループット超過エラーへの対策を考える上で、以下のような問題があります。
そこでPusna-RSでは、「Amazon SQS」(以下、SQS)のメッセージキューシステムを活用し、DynamoDBへの書き込みを非同期化することでスループット超過エラーの大量発生を防いでいます。具体例として、スマートデバイスからシステムへのデバイス情報登録処理を行う“デバイス登録API”周りの構成を下に示します。
具体的な動作イメージは下のような順になります。SQSを使うことで、DynamoDBへの書き込み負荷を設定スループットに沿った形での平滑化を実現できています。
この構成では、スループット超過エラーによるDynamoDBへ読み書き失敗を防ぐことができますが、スループット値を超える負荷が続いた場合は、書き込みリクエストに対し処理が追いつかないのでSQSにメッセージがたまり続けてしまう状況になってしまいます。
このように、SQSへメッセージが滞留するのを防ぐために、キューにたまったメッセージ数が一定を超えるとアラートとなる監視設定を「AWS CloudWatch」で行っています。アラートの頻度や傾向に応じてスループット値の設定を見直すことで、データ登録が遅延し過ぎることによってプッシュ通知システム全体に影響が出ることのないよう安定した運用ができています。
前述の通り、DynamoDBでデバイス情報を登録するテーブルは、Pusna-RSを利用するクライアントアプリごとにそれぞれ存在しています。初めは複数テーブルに対し、処理用のキューは1本しか用意しておらず、サービス開始時、Pusna-RSの利用頻度がそれほど高くない時期は複数アプリテーブルに対する書き込み処理でも1つのキューで処理を十分に平滑化できていました。
ところが、Pusna-RSの利用頻度が高くなってくると、処理が詰まってしまう問題が起き始めました。キューが1本しかないために、書き込み処理が遅延しているアプリ別情報テーブルが1つでもあると、キューに滞留したメッセージの詰まりが他のアプリ情報テーブルへの書き込み処理に波及し、雪だるま式にキューが詰まってしまうためです。
1つのアプリへの負荷がシステム全体の処理を遅延させてしまう状況は絶対に避けなければならないので、キューをアプリケーションごとに用意するようにして、アプリ単体の負荷が局所的になるように対策しました。この教訓から言えることとして、処理時間や負荷の異なる処理は1つのメッセージキューにまとめず、複数のメッセージキューに分けるようにするのがSQSで処理を平滑化するためのコツだと思いました。
Copyright © ITmedia, Inc. All Rights Reserved.