DynamoDBのスループットエラーを意図的に発生させてみた
こんにちは、CX事業本部の若槻です。
DynamoDBのテーブルにはキャパシティユニットというパラメーターがあり、テーブルに対するデータのWrite/Readスループットがキャパシティユニットを超えると、クライアントに対してProvisionedThroughputExceededException
エラーを返却します。
今回は、監視の設定の動作確認をする目的で、DynamoDBでスループットエラーProvisionedThroughputExceededException
を意図的に発生させてみました。
スループットエラーを起こすためのチューニング
スループットエラーを意図的に発生させるために、今回はBoto3でテーブルにデータの書き込み処理を行うPythonスクリプトを利用しようと思いますが、その際に効率的にエラーを発生させるために、設定項目のチューニングをいくつか行っていきます。
テーブルはプロビジョニングモードとしWCUを最小とする
DynamoDBのテーブルには2つのキャパシティモードがあります。
プロビジョニングモードの場合はテーブルのキャパシティユニットの上限を指定することができ、最小の設定は1
となります。
よって、キャパシティモードはプロビジョニングモード
、書き込みキャパシティユニット(WCU)は1
としたテーブルを作成することにします。
バーストキャパシティを考慮する
DynamoDBには"バーストキャパシティ"という仕様があり、テーブルに設定されたキャパシティユニットをスループットが超過した際に、過去300秒で利用されなかったキャパシティユニットを消費して充てることができます。
よって、テーブルに設定された1
WCU(最大でサイズが 1 KB までの項目について、1 秒あたり 1 回の書き込み)を超過する書き込み処理を行うだけではバーストキャパシティによりスループットエラーが発生しない可能性があるため、Pythonスクリプトでは1KBより十分に大きいサイズのデータを連続で書き込む処理を実装することにします。
ちなみに、同ドキュメントにはバーストキャパシティについて下記のようにもあり、
During an occasional burst of read or write activity, these extra capacity units can be consumed quickly—even faster than the per-second provisioned throughput capacity that you've defined for your table.
DynamoDB can also consume burst capacity for background maintenance and other tasks without prior notice.
「過去300秒で利用されなかったキャパシティユニットを利用可能」という仕様は額面通りには受け取らず、バーストキャパシティは必ずしも期待動作とはならないと考えた方が良さそうです。
Boto3のリトライ回数をゼロにする
下記のドキュメントによると、AWS SDKには実行時のエラーを受け取った際の再試行ロジックが実装されているとのことです。
Boto3の場合は、ソースコードによれば、既定のリトライ回数は5回とのことです。よって、最短でスループットエラーを発生させるためにこのリトライ回数を0回に変更する設定をPythonスクリプト内で行います。
リトライ回数の設定方法については下記の記事が参考となりました。
スループットエラーを起こしてみる
まず、WCU1
のDynamoDBテーブルsample-table
を作成します。
$ aws dynamodb create-table \ --table-name sample-table \ --attribute-definitions AttributeName=id,AttributeType=S \ --key-schema AttributeName=id,KeyType=HASH \ --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1
データ書き込みを行うPythonスクリプトdata_put.py
は以下のようになります。
import json from botocore.client import Config from boto3.session import Session profile = 'profile' session = Session( profile_name=profile, region_name='ap-northeast-1' ) config = Config( retries=dict( max_attempts=0 ) ) dynamodb_resource=session.resource( 'dynamodb', config=config ) data = open('./large_data').read() i = 0 while i < 10: table = dynamodb_resource.Table('sample-table') Item = { 'id': 'aaa', 'foo': data } table.put_item(Item=Item) print(i) i += 1
テーブルに書き込むためのサイズの大きいデータlarge_data
(12KB)を作成しておきます。
$ ls -lh | grep large_data -rw-rw-r-- 1 ec2-user ec2-user 12K May 22 14:16 large_data
Pythonスクリプトを実行します。バーストキャパシティにより1,2,3回目の書き込みではエラーとならず、4回目の実行でProvisionedThroughputExceededException
が発生させられました。
$ python data_put.py 0 1 2 Traceback (most recent call last): File "data_put.py", line 29, in <module> table.put_item(Item=Item) File "/usr/local/lib/python3.6/site-packages/boto3/resources/factory.py", line 520, in do_action response = action(self, *args, **kwargs) File "/usr/local/lib/python3.6/site-packages/boto3/resources/action.py", line 83, in __call__ response = getattr(parent.meta.client, operation_name)(**params) File "/usr/local/lib/python3.6/site-packages/botocore/client.py", line 272, in _api_call return self._make_api_call(operation_name, kwargs) File "/usr/local/lib/python3.6/site-packages/botocore/client.py", line 576, in _make_api_call raise error_class(parsed_response, operation_name) botocore.errorfactory.ProvisionedThroughputExceededException: An error occurred (ProvisionedThroughputExceededException) when calling the PutItem operation (reached max retries: 0): The level of configured provisioned throughput for the table was exceeded. Consider increasing your provisioning level with the UpdateTable API.
参考までに、sample-table
テーブルのCloudWatchメトリクスを見ると、[書き込みキャパシティー]のモニタリングではPythonスクリプトを実行した時間帯に「消費したキャパシティユニット」が急上昇していることが確認できます。
[スロットル書き込みリクエスト]のモニタリングでは同時間帯に「PUT」によるスロットルが1回発生していることが確認できます。
おわりに
DynamoDBでスループットエラーProvisionedThroughputExceededException
を意図的に発生させてみました。
最初はバーストキャパシティの仕様をよく知らずスループットエラーを発生させるのに苦労しましたが、今回の取り組みを経て結果的にDynamoDBの仕様理解につながったので良かったです!
参考
以上