DynamoDBテーブルでデータの追加や更新をするとき、条件を指定することがよくあります。
- データがすでに存在するなら、更新しない
- 新しいデータの場合だけ、更新したい
- など
この条件を満たさない場合は、ConditionalCheckFailedException
のエラーが発生します。
そこで、ふと気になったのです。条件を満たさないとき(エラー発生したとき)、キャパシティユニットは消費されるのか?と。
というわけで、試してみました。
おすすめの方
- 掲題について知りたい方
- DynamoDBテーブルの条件指定操作の方法を知りたい方
適当なDynamoDBテーブルを用意する
適当なDynamoDBテーブルを用意します。インデックスも存在しません。
実験用のスクリプトを作成し、実行する
それぞれ、作成・更新・削除を「条件指定なし」「条件指定あり(エラー発生する)」で試します。
app.py
import boto3
from botocore.exceptions import ClientError
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("boto3-test")
def main():
put_item1()
put_item2()
update_item1()
update_item2()
delete_item1()
delete_item2()
def put_item1():
resp = table.put_item(
Item={"todoId": "t0001", "name": "りんご飴を買う"}, ReturnConsumedCapacity="TOTAL"
)
print(resp["ConsumedCapacity"])
def put_item2():
try:
table.put_item(
Item={"todoId": "t0001", "name": "わたがしを買う"},
ReturnConsumedCapacity="TOTAL",
ConditionExpression="attribute_not_exists(todoId)",
)
print(resp["ConsumedCapacity"])
except ClientError as e:
if e.response["Error"]["Code"] == "ConditionalCheckFailedException":
print(e)
# not raise
else:
raise
def update_item1():
resp = table.update_item(
Key={"todoId": "t0001"},
UpdateExpression="set #name = :name",
ExpressionAttributeNames={
"#name": "name",
},
ExpressionAttributeValues={
":name": "たこやきを買う",
},
ReturnConsumedCapacity="TOTAL",
)
print(resp["ConsumedCapacity"])
def update_item2():
try:
resp = table.update_item(
Key={"todoId": "t0001"},
UpdateExpression="set #name = :name",
ConditionExpression="attribute_not_exists(todoId)",
ExpressionAttributeNames={
"#name": "name",
},
ExpressionAttributeValues={
":name": "かき氷を買う",
},
ReturnConsumedCapacity="TOTAL",
)
print(resp["ConsumedCapacity"])
except ClientError as e:
if e.response["Error"]["Code"] == "ConditionalCheckFailedException":
print(e)
# not raise
else:
raise
def delete_item1():
resp = table.delete_item(
Key={"todoId": "t0001"},
ReturnConsumedCapacity="TOTAL",
)
print(resp["ConsumedCapacity"])
def delete_item2():
try:
resp = table.delete_item(
Key={"todoId": "t0001"},
ConditionExpression="attribute_exists(todoId)",
ReturnConsumedCapacity="TOTAL",
)
print(resp["ConsumedCapacity"])
except ClientError as e:
if e.response["Error"]["Code"] == "ConditionalCheckFailedException":
print(e)
# not raise
else:
raise
if __name__ == "__main__":
main()
スクリプトを実行する
下記でスクリプトを実行します。少なくとも3つのキャパシティユニットが消費されました。
$ python app.py
{'TableName': 'boto3-test', 'CapacityUnits': 1.0}
An error occurred (ConditionalCheckFailedException) when calling the PutItem operation: The conditional request failed
{'TableName': 'boto3-test', 'CapacityUnits': 1.0}
An error occurred (ConditionalCheckFailedException) when calling the UpdateItem operation: The conditional request failed
{'TableName': 'boto3-test', 'CapacityUnits': 1.0}
An error occurred (ConditionalCheckFailedException) when calling the DeleteItem operation: The conditional request failed
エラー発生の場合は、Responseを受け取れないので、消費したキャパシティユニットが不明です。 そのため、CloudWatchメトリクスを確認します。
CloudWatchメトリクスで消費キャパシティユニットを確認する
CloudWatchメトリクスで次のメトリクスを確認します。
- ConsumedWriteCapacityUnits
キャパシティユニットの消費合計数は、6でした。 このうち、3つのキャパシティユニットは、条件指定が無い場合の操作です。 つまり、条件指定でエラー発生した場合でも、キャパシティユニットは消費されています。
さいごに
DynamoDBテーブルのアイテムに対する操作で条件を指定し、エラー発生した場合のキャパシティユニット消費数を確認してみました。 「条件が不一致ならキャパシティユニットが表示されない!」と思っていたので、新たな発見でした。 どなたかの参考になれば幸いです。