
DynamoDBのTransactionをPynamodbで実装してみる
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
データアナリティクス事業本部のkobayashiです。
Amazon DynamoDB Transactionsを使うことでDynamoDBテーブルに対して複数の操作を一括で実行し、一貫性のある状態を保つ事できますがこの機能をPynamoDBでも簡単に取り扱うことができるのでその内容をまとめます。
- Amazon DynamoDB Transactions: 仕組み - Amazon DynamoDB
- Welcome to PynamoDB’s documentation! — PynamoDB 6.0.0 documentation
Amazon DynamoDB Transactions
Amazon DynamoDB Transactionsは、DynamoDBテーブルに対して複数の操作を一括で実行し、複数のテーブル・項目に対する操作がすべて成功するか、またはすべて失敗することを保証できます。この機能は複雑なビジネスロジックを持つアプリケーションで特に有用です。
PynamoDBはDynamoDB用のObject-Document Mapper(ODM)であり、DynamoDBの様々な処理を簡単に記述できるインターフェースを提供しています。詳しくはPynamoDBについてのエントリは下記をご一読ください。
PynamoDBはトランザクション処理も簡単に扱えるため、トランザクションが必要な複雑なビジネスロジックを持つアプリケーションにも組み込んで使用できます。
Transactionを試してみる
では早速PynamoDBでのTransactionを試してみたいと思います。
環境
- Python 3.11.8
- PynamoDB 6.0.1
実装してみる
基本的にはPynamoDBのドキュメントに書かれている方法で実装が行えるのでドキュメントに従って記述していきます。
はじめにPynamoDBのモデルを以下のように定義します。
from pynamodb.models import Model
from pynamodb.attributes import BooleanAttribute, NumberAttribute, UnicodeAttribute
class BankStatement(Model):
    class Meta:
        region = 'ap-northeast-1'
        table_name = 'BankStatement'
    user_id = UnicodeAttribute(hash_key=True)
    account_balance = NumberAttribute(default=0)
    is_active = BooleanAttribute()
これはap-northeast-1リージョンのBankStatementテーブルをuser_idをパーティションキーとして作成し、属性としてaccount_balanceとis_activeを持つような構成になります。
ではこのテーブルに以下のデータを登録しておきます。
| user_id | account_balance | is_active | 
|---|---|---|
| user1 | 2000 | True | 
| user2 | 0 | True | 
user1_statement = BankStatement('user1', account_balance=2000, is_active=True)
user2_statement = BankStatement('user2', account_balance=0, is_active=True)
user1_statement.save()
user2_statement.save()
この項目に対してトランザクションを使って更新を行いたいと思います。具体的にはuser1からuser2に対してaccount_balanceを1000送る処理になります。
import uuid
from pynamodb.connection import Connection
from pynamodb.transactions import TransactWrite
try:
    # コネクションを張る
    connection = Connection('ap-northeast-1')
    
    # with句内に処理を記述し抜ける際にcommitして確定させる
    with TransactWrite(connection=connection, client_request_token=str(uuid.uuid4())) as transaction:
        transfer_amount = 1000
    
        # user1のaccount_balanceから1000を引く
        transaction.update(
            BankStatement(user_id='user1'),
            actions=[BankStatement.account_balance.add(transfer_amount * -1)],
            condition=(
                (BankStatement.account_balance >= transfer_amount) &
                (BankStatement.is_active == True)
            )
        )
    
        # user2のaccount_balanceに1000を足す
        transaction.update(
            BankStatement(user_id='user2'),
            actions=[BankStatement.account_balance.add(transfer_amount)],
            condition=(BankStatement.is_active == True)
        )
except TransactWriteError as err:
    print('トランザクションエラーが発生しました。')
ポイントは3点あります。
- コネクションを張る際にConnection('ap-northeast-1')でDynamoDBのリージョンを指定する
- with句でTransactWrite(connection=connection, client_request_token=str(uuid.uuid4()))を使う- client_request_tokenは一意の識別子を生成するためにUUIDを使用する
 
- updateメソッドはTransactWriteのメソッドを使って- transaction.update()で記述する- トランザクションを使わない場合だとモデルのメソッドを使って記述するbank_statement.update()
 
- トランザクションを使わない場合だとモデルのメソッドを使って記述する
実行するとデータが更新されていることがわかります。
この様にPynamoDBの簡単な記述でAmazon DynamoDB Transactionsを使ってトランザクション処理を実装できるようになります。
まとめ
Amazon DynamoDB Transactionsを使うためにPynamoDBのTransaction操作を使って実装を行ってみました。PynamoDBの特性でもあるシンプルな記述でTransaction処理を実装できるので複雑なビジネスロジックを持つアプリケーションでAmazon DynamoDB Transactionsを使いたい場合に有用です。
最後まで読んで頂いてありがとうございました。













