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 }
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": "%s" % id} ) if "Item" in res: item.name = res["Item"]["name"] item.lock_version = res["Item"]["lock_version"] return item def put_item(item, name): lock_version = boto3.dynamodb.conditions.Attr("lock_version") table.put_item( Item={ "id": "%s" % item.id, "name": name, "lock_version": item.lock_version + 1 }, ConditionExpression=lock_version_attr.not_exists().__or__(lock_version_attr.eq(item.lock_version)) ) item.name = name item.lock_version += 1 if __name__ == "__main__": item = get_item(42) put_item(42, "New Name")
もっと綺麗な組み立て方をしたい。