DynamoDB およびバージョン番号を使用した楽観的ロック - Amazon DynamoDB

DynamoDB およびバージョン番号を使用した楽観的ロック

オプティミスティックロックとは、更新 (または削除) しているクライアント側の項目と、Amazon DynamoDB の項目を確実に同じになるようにするための手段です。この方法を使用すると、データベースの書き込みは、他のユーザーの書き込みによって上書きされないように保護されます。逆の場合も同様に保護されます。

オプティミスティックロックを使用する場合、各項目には、バージョン番号として機能する属性があります。項目をテーブルから取り出すと、アプリケーションは、その項目のバージョン番号を記録します。サーバー側のバージョン番号が変更されていない場合のみ、項目を更新できます。バージョンの不一致がある場合は、前に他のユーザーによってそのアイテムが変更されたことを意味します。アイテムの古いバージョンがあるため、更新の試行は失敗します。その場合は更新をやり直します。もう一度項目を取得して、更新してください。オプティミスティックロックでは、他のユーザーが行った変更を誤って上書きできないようにします。また、お客様が行った変更を他のユーザーが誤って変更することを防ぐこともできます。

独自の楽観的ロック戦略を実装することもできますが、AWS SDK for Java には @DynamoDBVersionAttribute アノテーションが用意されています。テーブルのマッピングクラスで、バージョン番号を保存する 1 つのプロパティを指定し、この注釈を使用してそのプロパティをマーキングします。オブジェクトを保存すると、DynamoDB テーブル内の対応する項目に、バージョン番号を格納するための属性が追加されます。DynamoDBMapper では、最初にオブジェクトを保存したときにバージョン番号が割り当てられ、項目を更新するたびにバージョン番号が自動的にインクリメントされます。更新または削除リクエストは、クライアント側オブジェクトのバージョン番号が、DynamoDB テーブルで対応する項目のバージョン番号に一致する場合のみ成功します。

ConditionalCheckFailedException以下の場合、 はスローされます。

  • オプティミスティックロックを @DynamoDBVersionAttribute と共に使用しており、サーバーのバージョンの値が、クライアント側の値とは異なる場合。

  • DynamoDBMapperDynamoDBSaveExpression を使用してデータを保存するときに、独自の条件付き制約を指定し、それらの制約に障害が生じた場合。

注記
  • DynamoDB のグローバルテーブルはグローバルテーブルでは、同時更新間の「最新書き込み」の照合を行います。グローバルテーブルを使用する場合、最後に書き込まれたポリシーが採用されます。したがってこの場合、ロック戦略は想定通りに機能しません。

  • DynamoDBMapper トランザクションの書き込みオペレーションは、同じオブジェクトでの @DynamoDBVersionAttribute 注釈と条件式をサポートしていません。トランザクション書き込み内のオブジェクトに @DynamoDBVersionAttribute の注釈が付けられ、条件式も含まれている場合、SdkClientException がスローされます。

たとえば次の Java コードでは、複数のプロパティを持つ CatalogItem クラスを定義しています。Version プロパティは @DynamoDBVersionAttribute 注釈でタグ付けされています。

@DynamoDBTable(tableName="ProductCatalog") public class CatalogItem { private Integer id; private String title; private String ISBN; private Set<String> bookAuthors; private String someProp; private Long version; @DynamoDBHashKey(attributeName="Id") public Integer getId() { return id; } public void setId(Integer Id) { this.id = Id; } @DynamoDBAttribute(attributeName="Title") public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } @DynamoDBAttribute(attributeName="ISBN") public String getISBN() { return ISBN; } public void setISBN(String ISBN) { this.ISBN = ISBN;} @DynamoDBAttribute(attributeName = "Authors") public Set<String> getBookAuthors() { return bookAuthors; } public void setBookAuthors(Set<String> bookAuthors) { this.bookAuthors = bookAuthors; } @DynamoDBIgnore public String getSomeProp() { return someProp;} public void setSomeProp(String someProp) {this.someProp = someProp;} @DynamoDBVersionAttribute public Long getVersion() { return version; } public void setVersion(Long version) { this.version = version;} }

@DynamoDBVersionAttribute 注釈は、LongInteger などの、null が許容された型を提供するプリミティブラッパークラスによって得られた、null が許容された型に適用できます。

オプティミスティックロックは、次の DynamoDBMapper メソッドに対して次のような影響があります。

  • saveDynamoDBMapper は、新しい項目に対して初期バージョン番号 1 を割り当てます。項目を取得し、その項目の 1 つ以上のプロパティを更新して変更を保存する場合には、クライアント側とサーバー側のバージョン番号が一致する場合のみ、保存が成功します。DynamoDBMapper によってバージョン番号が自動的にインクリメントされます。

  • deletedelete メソッドは 1 つのオブジェクトをパラメータとして指定し、項目を削除する前に DynamoDBMapper によるバージョンチェックが実行されます。バージョンチェックは、リクエスト内で DynamoDBMapperConfig.SaveBehavior.CLOBBER を指定して無効にすることができます。

    DynamoDBMapper 内のオプティミスティックロックの内部実装では、DynamoDB の条件付き更新と条件付き削除機能が使用されます。

  • transactionWrite

    • PutDynamoDBMapper は、新しい項目に対して初期バージョン番号 1 を割り当てます。項目を取得し、その項目の 1 つ以上のプロパティを更新して変更を保存する場合には、クライアント側とサーバー側のバージョン番号が一致する場合のみ、put オペレーションが成功します。DynamoDBMapper によってバージョン番号が自動的にインクリメントされます。

    • UpdateDynamoDBMapper は、新しい項目に対して初期バージョン番号 1 を割り当てます。項目を取得し、その項目の 1 つ以上のプロパティを更新して変更を保存する場合には、クライアント側とサーバー側のバージョン番号が一致する場合のみ、更新オペレーションが成功します。DynamoDBMapper によってバージョン番号が自動的にインクリメントされます。

    • DeleteDynamoDBMapper は、項目を削除する前にバージョンチェックを実行します。削除オペレーションは、クライアント側とサーバー側のバージョン番号が一致する場合にのみ成功します。

    • ConditionCheck@DynamoDBVersionAttribute アノテーションは、ConditionCheck オペレーションではサポートされていません。ConditionCheck 項目に @DynamoDBVersionAttribute の注釈が付いている場合、SdkClientException がスローされます。

楽観的ロックの無効化

オプティミスティックロックを無効にするには、DynamoDBMapperConfig.SaveBehavior 列挙値を UPDATE から CLOBBER に変更します。その場合は、バージョンチェックを省略する DynamoDBMapperConfig インスタンスを作成して、そのインスタンスをすべてのリクエストで使用します。DynamoDBMapperConfig.SaveBehavior とその他のオプションの DynamoDBMapper パラメータの詳細については、「DynamoDBMapper のオプションの構成設定 」を参照してください。

ロック動作は、特定のオペレーションだけに設定することもできます。たとえば次の Java コードスニペットではDynamoDBMapper を使用してカタログ項目を保存しています。オプションの DynamoDBMapperConfig.SaveBehavior パラメータを DynamoDBMapperConfig メソッドに追加することで、save を指定しています。

注記

transactionWrite メソッドは、DynamoDBMapperConfig.SaveBehavior 構成をサポートしていません。transactionWrite のオプティミスティックロックの無効化はサポートされていません。

DynamoDBMapper mapper = new DynamoDBMapper(client); // Load a catalog item. CatalogItem item = mapper.load(CatalogItem.class, 101); item.setTitle("This is a new title for the item"); ... // Save the item. mapper.save(item, new DynamoDBMapperConfig( DynamoDBMapperConfig.SaveBehavior.CLOBBER));