DynamoDB にはトランザクションは無いしロック等無い。RDS 使へ。好い加減にしろ。
然し DynamoDB には魅力が在るし、少々トランザクション出來なからうが此れを使ひたいといふ欲求の在る場面もある。楽観的ロック位いは出來ないだらうか。
楽観的ロックと云へば私にとっては ActiveRecord の lock_version だ。UPDATE items SET name = "New Name", lock_version = 43 WHERE id = 1 AND lock_version = 42
等のやうに、比較と更新をアトミックに行なへれば此れは實裝出來る。
DynamoDB ではテーブルを跨がなければ、比較と更新がアトミックに出來る。從ってテーブルを跨がない楽観的ロックは實裝出來る。詰りテーブルを跨がないトランザクションは實裝出來る。
以下の DynamoDB テーブルが在るとする。id, name, lock_version を持たせやう。
resource "aws_dynamodb_table" "item" { attribute { name = "id" type = "S" } hash_key = "id" name = "item" read_capacity = 1 write_capacity = 1 }
記法は Terraform。
PutItem 時に condition-expression を附けると、condition-expression の結果が僞である時にエラーを起こし更新せぬやうに出來る。
cf. 条件式を使用した条件付きの書き込みの実行 - Amazon DynamoDB
Python でやると以下の如し。行が無い爲 lock_version 列も無い時か、或いは lock_version 列が變更されてゐなければ、PutItem を實行する。
import boto3 table = boto3.resource("dynamodb").Table("item") class Item(object): def __init__(id, **props): self.id = id self.name = props.get("name", None) self.lock_version = props.get("lock_version", 0) def get_item(id): item = Item(id=id) res = table.get_item( Key={"id": id} ) if "Item" in res: item.name = res["Item"]["name"] item.lock_version = res["Item"]["lock_version"] return item def put_item(item): lock_version_attr = boto3.dynamodb.conditions.Attr("lock_version") table.put_item( Item={ "id": item.id, "name": item.name, "lock_version": item.lock_version + 1 }, ConditionExpression=lock_version_attr.not_exists().__or__(lock_version_attr.eq(item.lock_version)) ) item.lock_version += 1 if __name__ == "__main__": item = get_item("mOmonga") item.name = "New name" put_item(item)
此の ConditionExpression=lock_version_attr.not_exists().__or__(lock_version_attr.eq(item.lock_version))
が条件式を組み立ててゐる。
もっと綺麗な組み立て方をしたい。