問題
DynamoDB をオンデマンドモードにして運用していればテーブルへのアクセス集中による対処(スロットリング)は気にしなくても良い。 というのは間違った認識である。
オンデマンドの DynamoDB テーブルを使用していますが、まだスロットルされています。なぜでしょうか? にあるように、例えオンデマンドモードであってもスロットリングは発生する。
そもそもリクエスト単位って何?
ここの説明に書いてある「リクエスト単位」というのは何を意味するのだろうか? 読み込み・書き込みがあるが、今回は書き込み側に絞ってみていく。
定義は ここ に書いてある。 同ページ内に書き込みキャパシティ単位(WCU)の説明もあるので混同に注意。
書き込みリクエスト単位 (Write Request Unit, WRU)
One write request unit represents one write for an item up to 1 KB in size. If you need to write an item that is larger than 1 KB, DynamoDB needs to consume additional write request units. Transactional write requests require 2 write request units to perform one write for items up to 1 KB. The total number of write request units required depends on the item size. For example, if your item size is 2 KB, you require 2 write request units to sustain one write request or 4 write request units for a transactional write request.
要約すると以下の通り。
- 「どれだけ使ったか(=課金に関する値)」を意味する
- ap-northeast-1 の場合は 100 万 WRU あたり 1.4269 USD (記事執筆時点)
- 1WRUは 最大1KB のデータを 1つ 書き込んだことを意味する
- 1KBより大きいデータを書き込むときは、1つ上のサイズに切り上げた分のWRUを消費する (1.5KBのデータなら2 WRU)
- トランザクションで書き込むときはさらに2倍
各種ドキュメントを探してもこういう結果になるのだが、この定義だと オンデマンドの DynamoDB テーブルを使用していますが、まだスロットルされています。なぜでしょうか? で説明されている
テーブルの各パーティションは、最大 3,000 の読み取りリクエスト単位または 1,000 の書き込みリクエスト単位、またはその両方を線形的に組み合わせることができます。
という内容を説明できない。 素直に読むと "1000件しか書き込みができない" となってしまう。
なので、おそらくではあるが、これらの文脈では per second が省略されている とみてよいと思う。 というのも、いくつかの記事を見る中では、WRU/sec などとして用いられていたためである。
- https://tarepan.hatenablog.com/entry/2018/02/12/AWS_DynamoDB
- https://pages.awscloud.com/rs/112-TZM-766/images/Session%204%20-%2020200825_CyberZ_OPENRECTV_Purpose-Built-Databases-Week-v2.pdf
なので、定義のページを読んでも読み取れないので不安ではあるが、
- 文章で書き込みリクエスト単位の数値が提供されている場合、それは1秒間に書き込み処理できる最大数である (/secが省略されている)
と、捉えることとする。
それを踏まえると、AWS アカウントのオンデマンドのデフォルトでは、40,000 RRU / 40,000 WRU となっている ため、秒間に 40,000 単位の読み込み・書き込みに対応しているが、上限緩和をしない限りはいくらうまく使ったとしても、これ以上の速度で処理を行えないということではある。
調査するに至ったきっかけ
今回出くわしたのは「同一パーティション の大量のデータ書き込みを SQS + Lambda の組み合わせで遅延処理で実施」した。 その結果スロットリングが発生してデータが書き込めない状況が発生した。
テーブル自体は最初からオンデマンドで作成しており、このデータ書き込みが発生するまではほとんどアクセスがない状態だったので、2倍に引き上げたところで性能が追い付かず、また、同一パーティションの書き込みだったので同一シャードにのみ負荷をかけることとなり、結果として上述で示されていた限界よりもかなり小さい 300 ~ 500 WRU / sec あたりでスロットリングの発生を確認した。 少し調整して、100 ~ 125 WRU / sec あたりを維持することでスロットリングは発生しなくなった。
ここを動的に調整しようと思うと、トラブルシューティングの記事にならって 、 Provisionモードで事前に高いCapacity Unitを持つテーブルを作成し、これをOn-Demandに変更する 方法や、30分前からウォームアップをしてトラフィックピークを予見する といった方法がある。
ただ今回自分が起こした問題については「同一パーティション」にしてしまったことがそもそもの問題である。 この手の処理で並列投入する予定はなかったが必要になってしまった。
ので、「DynamoDBをオンデマンドで使っているから、スロットリングによってエラーが返ってくることはない」と誤った認識で開発するのではなく、大量書き込み・読み込みが行われるかをちゃんと設計段階で考慮して使わなければならない。