DynamoDBのPutItemでReturnValuesOnConditionCheckFailureが指定できるようになったので試してみた

DynamoDBのPutItemでReturnValuesOnConditionCheckFailureが指定できるようになったので試してみました。 これは指定しておいた方が幸せになりそう。
2023.07.07

DynamoDBのPutItem, UpdateItem, DeleteItem, ExecuteStatement, BatchExecuteStatement, ExecuteTransaction APIReturnValuesOnConditionCheckFailure パラメータが指定できるようになったので今回はPutItemを試してみました。

TL;DR

  • これまでは失敗した原因のItemを別途読み取りして確認する必要があったのが、レスポンスに含まれるようになったので原因調査が簡単になった。
  • 読み込みキャパシティーユニットを消費しないので今まで読み取りしていた場合に比べコスト削減が期待できる。

ほぼ上記更新記事の内容そのままですが、個人的には今回の検証で具体的な挙動を確認した結果、条件付き書き込みをしている場合は基本指定しておくと良さそうだと思いました。

やってみた

Pythonで試してみました。

環境

Python: 3.10
boto3: 1.27.1

※ boto3では1.26.164から利用できます

1. テーブル作成

import boto3

dynamodb = boto3.client("dynamodb")
table_name = "sample_table"

response = dynamodb.create_table(
    TableName=table_name,
    KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
    AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "N"}],
    BillingMode="PAY_PER_REQUEST",
)

dynamodb.get_waiter("table_exists").wait(TableName=table_name)
print("テーブル 作成成功")

2. Itemの追加

import boto3

dynamodb = boto3.client("dynamodb")
table_name = "sample_table"

item = {
    "id": {"N": "1"},
    "name": {"S": "yamada taro"},
    "email": {"S": "yamada.taro@example.com"}, 
}

dynamodb.put_item(
    TableName=table_name,
    Item=item,
)

3. ReturnValuesOnConditionCheckFailure=ALL_OLD指定有りとその結果

ReturnValuesOnConditionCheckFailure パラメータには現在"ALL_OLD""NONE"が指定できます。

import boto3
from botocore.exceptions import ClientError

dynamodb = boto3.client("dynamodb")
table_name = "sample_table"

# idのみが同一のitem
item = {
    "id": {"N": "1"}, 
    "name": {"S": "satou hanako"},
    "email": {"S": "satou.hanako@example.com"},
}

try:
    dynamodb.put_item(
        TableName=table_name,
        Item=item,
        ConditionExpression="attribute_not_exists(id)",  # idが存在しない場合にのみ書き込み
                ReturnValuesOnConditionCheckFailure="ALL_OLD",
    )
    print("Item 追加成功")
except ClientError as e:
    if e.response["Error"]["Code"] == "ConditionalCheckFailedException":
        print(e.response)

下記のように"Item"がresponseに含まれるようになりました。また、含まれる値は更新される前のItemが含まれている事が確認できます。

e.response.json

{
  "Error": { "Message": "The conditional request failed", "Code": "ConditionalCheckFailedException" },
  "ResponseMetadata": {
    "RequestId": "JBTNKFGR24HC6UDO0NJ7RKKF4RVV4KQNSO5AEMVJF66Q9ASUAAJG",
    "HTTPStatusCode": 400,
    "HTTPHeaders": {
      "server": "Server",
      "date": "Thu, 06 Jul 2023 10:02:48 GMT",
      "content-type": "application/x-amz-json-1.0",
      "content-length": "211",
      "connection": "keep-alive",
      "x-amzn-requestid": "JBTNKFGR24HC6UDO0NJ7RKKF4RVV4KQNSO5AEMVJF66Q9ASUAAJG",
      "x-amz-crc32": "3372565735"
    },
    "RetryAttempts": 0
  },
  "Item": { "email": { "S": "yamada.taro@example.com" }, "id": { "N": "1" }, "name": { "S": "yamada taro" } }
}

補足(指定無しの結果)

指定無しだと"Item"は含まれていません。

e.response.json

{
  "Error": { "Message": "The conditional request failed", "Code": "ConditionalCheckFailedException" },
  "ResponseMetadata": {
    "RequestId": "KVSRAD26KP9VPEUH6JPJIPR10BVV4KQNSO5AEMVJF66Q9ASUAAJG",
    "HTTPStatusCode": 400,
    "HTTPHeaders": {
      "server": "Server",
      "date": "Thu, 06 Jul 2023 09:57:12 GMT",
      "content-type": "application/x-amz-json-1.0",
      "content-length": "120",
      "connection": "keep-alive",
      "x-amzn-requestid": "KVSRAD26KP9VPEUH6JPJIPR10BVV4KQNSO5AEMVJF66Q9ASUAAJG",
      "x-amz-crc32": "396270901"
    },
    "RetryAttempts": 0
  },
  "message": "The conditional request failed"
}