DynamoDBのTransactionをPynamodbで実装してみる

DynamoDBのTransactionをPynamodbで実装してみる

Amazon DynamoDB TransactionsをPynamoDBで試した記事です。
Clock Icon2024.06.30

はじめに

データアナリティクス事業本部のkobayashiです。

Amazon DynamoDB Transactionsを使うことでDynamoDBテーブルに対して複数の操作を一括で実行し、一貫性のある状態を保つ事できますがこの機能をPynamoDBでも簡単に取り扱うことができるのでその内容をまとめます。

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_balanceis_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を使いたい場合に有用です。

最後まで読んで頂いてありがとうございました。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.