DynamoDBのトランザクション機能を検証してみた

エンジニアの内山です。
今回は DynamoDB についてです。

トランザクション機能の検証

New – Amazon DynamoDB Transactions
https://aws.amazon.com/jp/blogs/aws/new-amazon-dynamodb-transactions/

ついにDynamoDBがトランザクション機能がつきました。
この機能を使う場合は、以下のAPIを利用することになります。

  • TransactGetItems (読み取り)
  • TransactWriteItems (書き込み)

今回は TransactWriteItems について検証してみました。
さっそくですが、以下のコードでロールバックされるか確認してみました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
func rollbackTest(svc *dynamodb.DynamoDB, tableName string) {
_, err := svc.TransactWriteItems(&dynamodb.TransactWriteItemsInput{
TransactItems: []*dynamodb.TransactWriteItem{
// 更新できる
{
Put: &dynamodb.Put{
TableName: aws.String(tableName),
Item: map[string]*dynamodb.AttributeValue{
"id": {
S: aws.String("Item-1"),
},
},
},
},
// 更新エラーとなる
{
Put: &dynamodb.Put{
TableName: aws.String(tableName),
Item: map[string]*dynamodb.AttributeValue{
"id": {
S: aws.String("Item-2"),
},
},
ConditionExpression: aws.String("ForceError = :true"), // この条件で更新処理を失敗させる
ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
":true": {BOOL: aws.Bool(true)},
},
},
},
},
})
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
log.Println("Error:", awsErr.Code(), awsErr.Message())
}
}
}

以下のように、トランザクションのエラーが出ます。

1
2
Error: TransactionCanceledException Transaction cancelled,
please refer cancellation reasons for specific reasons [None, ConditionalCheckFailed]

1個目のアイテムが None で問題なく通ってますが、
2個目のアイテムが ConditionalCheckFailed でエラーが起こっているので、
トランザクションがキャンセルされたことになっています。
期待通りですね。

トランザクションが適用される範囲について

トランザクションが適用される範囲は以下のとおりです。

  • 同じパーティションキー でも 異なるパーティションキー でもOK
  • 同じテーブル でも 異なるテーブル でもOK
  • 同じリージョンのみ
  • 同じアカウントのみ
  • DynamoDB テーブルのみ

制限について

以下のような制限があるので、注意が必要です。

同じアイテムを同時に更新できない

以下のコードで、同じアイテム(Item-1)を同時に更新してみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
func dupTest(svc *dynamodb.DynamoDB, tableName string) {
dupID := "Item-1"

_, err := svc.TransactWriteItems(&dynamodb.TransactWriteItemsInput{
TransactItems: []*dynamodb.TransactWriteItem{
{
Put: &dynamodb.Put{
TableName: aws.String(tableName),
Item: map[string]*dynamodb.AttributeValue{
"id": {
S: aws.String(dupID),
},
},
},
},
{
Put: &dynamodb.Put{
TableName: aws.String(tableName),
Item: map[string]*dynamodb.AttributeValue{
"id": {
S: aws.String(dupID),
},
},
},
},
},
})
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
log.Println("Error:", awsErr.Code(), awsErr.Message())
}
}
}

以下のようなエラーが発生します。

1
Error: ValidationException Transaction request cannot include multiple operations on one item

同時に更新できるアイテム数は 10個まで

以下のコードで、11個のアイテムを更新する処理を試してみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func lengthTest(svc *dynamodb.DynamoDB, tableName string) {
n := 11
var items []*dynamodb.TransactWriteItem
for i := 0; i < n; i++ {
items = append(items, &dynamodb.TransactWriteItem{
Put: &dynamodb.Put{
TableName: aws.String(tableName),
Item: map[string]*dynamodb.AttributeValue{
"id": {
S: aws.String(fmt.Sprintf("Item-%d", i+1)),
},
},
},
})
}

_, err := svc.TransactWriteItems(&dynamodb.TransactWriteItemsInput{
TransactItems: items,
})
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
log.Println("Error:", awsErr.Code(), awsErr.Message())
}
}
}

以下のようなエラーが発生します。

1
Error: ValidationException 1 validation error detected: Value '[com.amazonaws.dynamodb.v20120810.TransactWriteItem@73f1877d, com.amazonaws.dynamodb.v20120810.TransactWriteItem@73e36ffc, com.amazonaws.dynamodb.v20120810.TransactWriteItem@73d5587b, com.amazonaws.dynamodb.v20120810.TransactWriteItem@73c740fa, com.amazonaws.dynamodb.v20120810.TransactWriteItem@7429e581, com.amazonaws.dynamodb.v20120810.TransactWriteItem@741bce00, com.amazonaws.dynamodb.v20120810.TransactWriteItem@740db67f, com.amazonaws.dynamodb.v20120810.TransactWriteItem@73ff9efe, com.amazonaws.dynamodb.v20120810.TransactWriteItem@7380cb75, com.amazonaws.dynamodb.v20120810.TransactWriteItem@6c0b735b, com.amazonaws.dynamodb.v20120810.TransactWriteItem@6bfd5bda]' at 'transactItems' failed to satisfy constraint: Member must have length less than or equal to 10

この制限は意識していないと違反してしまいそうなので、注意が必要ですね。

まとめ

実装されたDynamoDBのトランザクション機能について、実際に動かしながら検証してみました。

今までは、アトミック性を保ちたい場合1レコードに収まるようにデータモデリングする必要がありました。
これがなかなか大変だったのですが、トランザクション機能が実装されたことにより、少しは楽になるのではないかなと思っています。

これからもDynamoDBをどんどん活用していきたいです。

このエントリーをはてなブックマークに追加