Amazon DynamoDB Local で新たに2つの DynamoDB API をサポートしました

2023.12.19

こんにちは、森田です。

以下のアップデートで Amazon DynamoDB Local にて新しい DynamoDB API を利用することができるようになりました。

今回新たにサポートされたAPIは以下の通りです。

  • deletion protection(削除保護)
  • ReturnValuesOnConditionCheckFailure(条件付き書き込み失敗時のレスポンス設定)

これら2つの機能は、DynamoDBで2023年に発表されたものですが、今まで DynamoDB Local では対応しておらず、テストを行うことができませんでした。

今回のアップデートで、これらのAPIに対しても対応できるようになったため、DynamoDB Local でも動作テストができるようになります。

やってみた

では、実際に Dynamo DB Local を利用して新しいAPIを叩いてみます。

DynamoDB Local の起動

本記事では、dynamodb-local イメージを利用して DynamoDB Local をコンテナ上で起動させます。

docker pull amazon/dynamodb-local
docker run -d --name dynamodb-local -p 8000:8000 amazon/dynamodb-local

削除保護

今回は、boto3を利用してAPIを叩いてみます。

削除保護を有効にしてテーブルの作成

以下のコードを実行して、テーブルの作成を行います。

DynamoDB Local を利用するために、endpoint_urlを指定します。

create.py

import boto3

# DynamoDBリソースを作成し、エンドポイントURLを指定します。
dynamodb = boto3.resource('dynamodb', endpoint_url='http://localhost:8000/')

# テーブルを作成するためのパラメータを設定します。
table_name = 'User'
attribute_definitions = [
    {
        'AttributeName': 'userId',
        'AttributeType': 'S' 
    }
]
key_schema = [
    {
        'AttributeName': 'userId',
        'KeyType': 'HASH'  
    }
]
provisioned_throughput = {
    'ReadCapacityUnits': 5,
    'WriteCapacityUnits': 5
}

# テーブルを作成します。
table = dynamodb.create_table(
    TableName=table_name,
    AttributeDefinitions=attribute_definitions,
    KeySchema=key_schema,
    ProvisionedThroughput=provisioned_throughput,
    DeletionProtectionEnabled=True #削除保護
)

# テーブルの作成が完了するまで待ちます。
table.meta.client.get_waiter('table_exists').wait(TableName=table_name)

print(f"Table {table_name} has been created.")

実行結果

$ python3 create.py
Table User has been created.

削除保護を有効にしたテーブルの削除

以下のコードを実行しても、削除することができず、エラーが発生します。

delete.py

import boto3

table_name = "User"

# DynamoDBクライアントを作成します。
dynamodb = boto3.client('dynamodb', endpoint_url='http://localhost:8000/')

dynamodb.delete_table(TableName=table_name)

実行結果

$ python3 delete.py
botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the DeleteTable operation: Resource cannot be deleted as it is currently protected against deletion. Disable deletion protection first.

削除保護を無効後、テーブルの削除

では、削除できるように、削除保護を無効化して、テーブルの削除を行います。

delete2.py

import boto3

table_name = "User"

# DynamoDBクライアントを作成します。
dynamodb = boto3.client('dynamodb', endpoint_url='http://localhost:8000/')

# 削除保護を無効化
response = dynamodb.update_table(
    TableName=table_name,
   DeletionProtectionEnabled=False #削除保護
)

dynamodb.delete_table(TableName=table_name)

実行結果

$ python3 delete2.py

今度はエラーなく実行ができ、テーブルの削除が行えます。

ReturnValuesOnConditionCheckFailure

今回は以下の記事のコードを参考に動作検証を行います。

テーブル作成とアイテムの追加

以下のコードで動作確認用のテーブル作成とアイテム追加を行います。

item_create.py

import boto3

dynamodb = boto3.client("dynamodb",  endpoint_url='http://localhost:8000/')

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("テーブル 作成成功")

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

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

print("アイテム 作成成功")

実行結果

$ python3 item_create.py 
テーブル 作成成功
アイテム 作成成功

条件付き書き込み

続いて以下のコードを実行して、条件付き書き込みを行います。

idが存在しない場合に書き込みが成功する指定を行っていますが、先ほどid=1のアイテムを追加しているためエラーとなります。

put.py

import boto3
from botocore.exceptions import ClientError

dynamodb = boto3.client("dynamodb", endpoint_url='http://localhost:8000/')
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.get("Item"))

実行結果

$ python3 put.py
{'name': {'S': 'yamada taro'}, 'email': {'S': 'yamada.taro@example.com'}, 'id': {'N': '1'}}

実行結果からも、期待した通りに、元のアイテムをエラー時に返却しています。

さいごに

今回新しいAPIにも対応したため、ますます DynamoDB Local が使いやすくなりました。

DynamoDB をテスト用で利用したい場合には、 DynamoDB Local は非常に便利なツールですので、ぜひチェックしてみてください!