オプティミスティック同時実行制御を実装する

完了

SDK を使用して項目を読み取り、それ以降の操作で同じ項目を更新することには、いくつかの本質的なリスクが伴います。 別のクライアントから別の操作が行われ、最初のクライアントの更新操作が完了する前に、基になるドキュメントが変更される可能性があります。 この競合によって、"失われた更新" 状態が発生する可能性があります。 例を使用してこの競合について説明します。

個別の読み取りと更新の操作を含む一般的な C# コード例を次に示します。

string categoryId = "9603ca6c-9e28-4a02-9194-51cdb7fea816";
PartitionKey partitionKey = new (categoryId);

Product product = await container.ReadItemAsync<Product>("01AC0", partitionKey);

product.price = 50d;

await container.UpsertItemAsync<Product>(product, partitionKey);

この例では読み取りと書き込みが個別の操作であるため、これらの操作の間には待機時間があります。 この待機時間は、この図では n として表されます。

読み取りと更新の間の N 待機時間の図。

この待機時間は、コンピューター コードではミリ秒または秒のように短いですが、潜在的な更新が失われるのに十分なほど致命的である可能性があります。 ユーザー入力によって読み取りと更新操作の間の待機時間が長くなる一部のユーザー向けアプリケーションでは、 n 個の値が長くなり、更新が失われる可能性が高くなる可能性があります。 この問題は、 オプティミスティック コンカレンシー制御を実装することで解決できます。

ItemResponse<Product> response = await container.ReadItemAsync<Product>("01AC0", partitionKey);

各項目には ETag 値があります。 この値は、項目が更新されたときに更新されます。 要求の 読み取 り操作からの応答を監視することで、項目の ETag 値を取得できます。

ItemResponse クラスには、対応する文字列値を含む ETag プロパティがあります。

Product product = response.Resource;
string eTag = response.ETag;

更新が失われないようにするには、 if-match ルールを使用して、 ETag が更新要求の一部としてアイテム サーバー側の現在の ETag ヘッダーと一致するかどうかを確認できます。

ItemRequestOptions options = new ItemRequestOptions { IfMatchEtag = eTag };
await container.UpsertItemAsync<Product>(product, partitionKey, requestOptions: options);

ETag 値がアイテム サーバー側の現在の ETag ヘッダーと一致しない場合、操作は失敗し、アイテムを再び更新する前に、アイテムの最新バージョンを取得するためにアイテムを再読み取りする必要があります。 エラー コードは 412 Precondition Failed です。

C# コードの更新では、オプティミスティック コンカレンシー制御を実装するためにわずかな変更のみが必要でした。これにより、更新操作で、競合するクライアントによって以前に保存されたサーバー側の変更が失われることはありません。

string categoryId = "9603ca6c-9e28-4a02-9194-51cdb7fea816";
PartitionKey partitionKey = new (categoryId);

ItemResponse<Product> response = await container.ReadItemAsync<Product>("01AC0", partitionKey);
Product product = response.Resource;
string eTag = response.ETag;

product.price = 50d;

ItemRequestOptions options = new ItemRequestOptions { IfMatchEtag = eTag };
await container.UpsertItemAsync<Product>(product, partitionKey, requestOptions: options);

個別の読み取りと更新の操作を含む一般的な Python コード例を次に示します。

item_id = "01AC0"
partition_key = "9603ca6c-9e28-4a02-9194-51cdb7fea816"

# Read the item
item_response = container.read_item(item=item_id, partition_key=partition_key)
product = item_response

# Update the product price
product["price"] = 50.0

# Upsert the item back to the container
container.upsert_item(body=product)

この例では読み取りと書き込みが個別の操作であるため、これらの操作の間には待機時間があります。 この待機時間は、この図では n として表されます。

読み取りと更新の間の N 待機時間の図。

この待機時間は、コンピューター コードではミリ秒または秒のように短いですが、潜在的な更新が失われるのに十分なほど致命的である可能性があります。 ユーザー入力によって読み取りと更新操作の間の待機時間が長くなる一部のユーザー向けアプリケーションでは、 n 個の値が長くなり、更新が失われる可能性が高くなる可能性があります。 この問題は、 オプティミスティック コンカレンシー制御を実装することで解決できます。

item_response = container.read_item(item=item_id, partition_key=partition_key)
product = item_response

各項目には ETag 値があります。 この値は、項目が更新されたときに更新されます。 読み取り操作からヘッダーを観察することで、項目の ETag 値を取得できます。

アイテムの ETag 値にアクセスする方法は 2 つあります。 1 つの方法は、 get_response_headers メソッドを使用する方法です。 このメソッドでは、ETag 値の名前は etag です。

headers = item_response.get_response_headers()
etag = headers.get("etag")

もう 1 つの方法は、項目の応答で get メソッドを使用することです。 このメソッドでは、ETag 値の名前は _etag です。

etag = item_response.get("_etag")

更新が失われないようにするには、 if-match 条件を使用して、 ETag が更新要求の一部としてアイテム サーバー側の現在の ETag ヘッダーと一致するかどうかを確認できます。

headers = {"If-Match": etag}
container.upsert_item(body=product, headers=headers)

ETag 値がアイテム サーバー側の現在の ETag ヘッダーと一致しない場合、操作は失敗し、アイテムを再び更新する前に、アイテムの最新バージョンを取得するためにアイテムを再読み取りする必要があります。 エラー コードは 412 Precondition Failed です。

Python コードの更新では、オプティミスティック コンカレンシー制御を実装するためにわずかな変更のみが必要でした。これにより、更新操作で、競合するクライアントによって以前に保存されたサーバー側の変更が失われることはありません。

item_id = "01AC0"
partition_key = "9603ca6c-9e28-4a02-9194-51cdb7fea816"

# Read the item
item_response = container.read_item(item=item_id, partition_key=partition_key)
product = item_response
etag = item_response.get("_etag")

# Update the product price
product["price"] = 50.0

# Use optimistic concurrency control
headers = {"If-Match": etag}
container.upsert_item(body=product, headers=headers)

個別の読み取り操作と更新操作を含む一般的な JavaScript コード例を次に示します。

const categoryId = "9603ca6c-9e28-4a02-9194-51cdb7fea816";
const itemId = "01AC0";

// Read the item
const { resource: product } = await container.item(itemId, categoryId).read();

// Update the price
product.price = 50.0;

// Upsert the updated item
await container.items.upsert(product);

この例では読み取りと書き込みが個別の操作であるため、これらの操作の間には待機時間があります。 この待機時間は、この図では n として表されます。

読み取りと更新の間の N 待機時間の図。

この待機時間は、コンピューター コードではミリ秒または秒のように短いですが、潜在的な更新が失われるのに十分なほど致命的である可能性があります。 ユーザー入力によって読み取りと更新操作の間の待機時間が長くなる一部のユーザー向けアプリケーションでは、 n 個の値が長くなり、更新が失われる可能性が高くなる可能性があります。 この問題は、 オプティミスティック コンカレンシー制御を実装することで解決できます。

各項目には ETag 値があります。 この値は、項目が更新されたときに更新されます。 読み取り操作から応答ヘッダーを監視することで、項目の ETag 値を取得できます。

const { resource: product, headers } = await container.item(itemId, categoryId).read();
const eTag = headers.etag;

更新が失われないようにするには、 if-match ルールを使用して、 ETag が更新要求の一部としてアイテム サーバー側の現在の ETag ヘッダーと一致するかどうかを確認できます。

// Update the product
product.price = 50.0;

// Use the if-match header for optimistic concurrency control
await container.items.upsert(product, {
  accessCondition: { type: "IfMatch", condition: eTag }
});

ETag 値がアイテム サーバー側の現在の ETag ヘッダーと一致しない場合、操作は失敗し、アイテムを再び更新する前に、アイテムの最新バージョンを取得するためにアイテムを再読み取りする必要があります。 エラー コードは 412 Precondition Failed です。

JavaScript コードの更新では、オプティミスティック コンカレンシー制御を実装するためにわずかな変更のみが必要でした。これにより、更新操作で、競合するクライアントによって以前に保存されたサーバー側の変更が失われることはありません。

const categoryId = "9603ca6c-9e28-4a02-9194-51cdb7fea816";
const itemId = "01AC0";

// Read the item
const { resource: product, headers } = await container.item(itemId, categoryId).read();
const eTag = headers.etag;

// Update the product
product.price = 50.0;

// Use the if-match header for optimistic concurrency control
await container.items.upsert(product, {
  accessCondition: { type: "IfMatch", condition: eTag }
});