DynamoDBのTransactionをPynamodbで実装してみる
はじめに
データアナリティクス事業本部の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を使いたい場合に有用です。
最後まで読んで頂いてありがとうございました。