💻

Azure Cosmos DB の Partial Update を試してみる

2021/08/09に公開

この記事について

この記事では、2021/5/26 〜 2021/5/28 (日本時間) で開催された Microsoft Build 2021 にて公開された Azure Cosmos DB の部分ドキュメント更新 (Partial Update) について見ていきます。
端的に言うと、今回のアップデートで Patch がサポートされた という内容になります。これまで Azure Cosmos DB でドキュメントの更新を行う処理については、実際は Put 的な処理が行われる仕様となっており、更新というよりは全置換という処理内容となっていました。
今回の Partial Update が登場したことによって、Patch によるリソースの部分更新 (置換) が可能になり、クライアント側での処理が楽になるというものです。

これまでの Azure Cosmos DB での更新処理の実態

これまでの Azure Cosmos DB では、オプティミスティック同時実行制御 (OCC: Optimistic Concurrency Control) という仕組み (いわゆる 楽観ロック) を活用し、以下のような流れで更新処理を行っていました。

  1. 更新対象となるドキュメントの明示的な読み取り
  2. オプティミスティック同時実行制御 (OCC) のチェック
  3. クライアント側からのドキュメント置換操作

この最後のクライアント側からのドキュメント置換操作という部分で実際にアイテム (ドキュメント) の更新が行われる訳ですが、この処理の内容は アイテム全体を置換する ものとなってしまっており、いわゆる Put 処理となっているわけです。

オプティミスティック同時実行制御

オプティミスティック同時実行制御 とは、データの競合は「原則として起こらない」ことを前提とし、データ取得時にはロックは行わず、更新時にほかのユーザーによる同時更新を検出する同時実行制御の考え方のことです。
データベースの世界では、データの整合性を保つために 排他制御 を行っておりますが、その実現方式の代表例としてあげられるものに、楽観的排他制御 (楽観ロック) と 悲観的排他制御 (悲観ロック) があります。
Azure Cosmos DB の更新処理では、オプティミスティック同時実行制御、つまり楽観ロックを採用しています。つまり、先述の更新処理の流れに合わせると、

  1. 更新対象となるドキュメントの明示的な読み取り
    => 更新対象のドキュメントに対してロックは行わず、ドキュメントの状態 (バージョン) を取得
  2. オプティミスティック同時実行制御 (OCC) のチェック
    => 更新処理実行前に、対象のドキュメントが読み取り時の状態 (バージョン) と同一か確認
  3. クライアント側からのドキュメント置換操作
    => 読み取り時の状態 (バージョン) と同一で問題なければドキュメントを置換

という処理が行われていることになります。ちなみに、Azure Cosmos DB で更新 (PUT) 処理が読み取り処理 (Fetch) 時の 2 倍の要求ユニットが消費される所以はここにあります。

https://docs.microsoft.com/ja-jp/azure/cosmos-db/request-units

オプティミスティック同時実行制御の詳細については、公式ドキュメント を参照ください。

https://docs.microsoft.com/ja-jp/azure/cosmos-db/database-transactions-optimistic-concurrency

Partial Update について

前述の通り、これまでの Azure Cosmos DB では、オプティミスティック同時実行制御を採用しているため、アイテム (ドキュメント) の更新処理には、更新対象アイテムの全読み取りアイテム全体の置換 が実行されていました。これを実行しなくても、単一のアイテム (ドキュメント) 内の特定のフィールド/プロパティに対して、パスレベルの更新が実行できるようになるという、部分的なドキュメント更新機能が Partial Update です。
これによって、クライアント側で 明示的なドキュメントの読み取りOCC チェックドキュメントの置換操作 を実行する必要がなくなります。そのため、例えば 1 アイテムにおいて異なるプロパティの値を更新する処理がほぼ同時に実行される可能性がある場合において、クライアント側では楽観ロックによる値の更新ミスを避けるために独自に排他制御を入れるような実装を行う場合がありますが、Partial Update が実装されることによってこの部分を行わなくて済むようになります。

実際に試してみる

Azure Cosmos DB 環境

2021/08/09 現在、Azure Cosmos DB の Partial Update 機能については プライベートプレビュー での提供となっています。そのため、作成済みの Azure Cosmos アカウントで Partial Update を利用したい場合は、専用フォームから申請を行ってください。

https://forms.office.com/Pages/ResponsePage.aspx?id=v4j5cvGGr0GRqy180BHbR5VsJDEls71JudfaPwmEjSFUQlA0VDNNQkFOMDZGOENCQU0yV1VCTzg1My4u

また、Partial Update については、プライベートプレビューが適用された Azure Cosmos アカウント環境以外に、Azure Cosmos DB エミュレーター上で利用することも可能です。

Windows 環境の場合

Windows 用の Azure Cosmos DB エミュレーターを利用してください。(Windows OS 上での Linux エミュレーター利用は、Docker Desktop WSL2 の利用も含め、開発チームが利用を推奨していません)

It is not recommended using the Linux emulator on Windows. For Windows environments use the Windows emulator. You can learn more and get the docker container for that here. https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator-on-docker-windows?tabs=cli

https://devblogs.microsoft.com/cosmosdb/build-2021-apps-at-any-scale/

Windows で Azure Cosmos DB エミュレーターを利用する方法は、ホストに直接インストール または Windows Docker コンテナー上に構築 のどちらかになります。
ホスト OS に直接インストールする場合は、こちらの公式ドキュメントを参照してください。

https://docs.microsoft.com/ja-jp/azure/cosmos-db/local-emulator?tabs=ssl-netstd21

Windows Docker コンテナー上への構築については、こちらの公式ドキュメントを参照してください。

https://docs.microsoft.com/ja-jp/azure/cosmos-db/local-emulator-on-docker-windows?tabs=cli

Partial Update を適用する場合は、エミュレーター起動時に/EnablePreview のオプションをつけるようにすれば OK です。Docker の場合は、コンテナー起動時にこのオプションが設定されるようにしてください。

https://github.com/AzureCosmosDB/PatchPrivatePreviewFeedbackGroup#frequently-asked-questions-faqs

macOS / Linux OS 環境の場合

macOS または Linux OS 環境で Azure Cosmos DB エミュレーターを利用する方法としては、Docker Desktop for Mac/Linux 上の Linux コンテナー上に構築 する形になります。2021/05 に新しく登場した Linux エミュレーター を使用して環境を作成してください。
公式ドキュメントのほか、こちらで解説記事も出しているので参照してください。

https://docs.microsoft.com/ja-jp/azure/cosmos-db/linux-emulator?tabs=ssl-netstd21

https://zenn.dev/ymasaoka/articles/get-started-with-cosmosdb-linux-emulator

Partial Update を適用する場合は、コンテナー起動時の環境変数 (environment) に overrides='enableJsonPatch:true' のオプションをつけるようにすれば OK です。

メソッド解説

Partial Update は、Microsoft.Azure.Cosmos.ContainerPatchItemAsync メソッドを利用します。こちらのメソッドは、通常の Azure Cosmos SDK では利用できず、Microsoft.Azure.Cosmos 3.18.0-preview 以上のプレビュー版 SDK でのみ利用可能のメソッドになります。
最新の SDK バージョンについては、NuGet リポジトリの情報を確認してください。

https://www.nuget.org/packages/Microsoft.Azure.Cosmos

使い方としては、これまで利用していた Container.ReplaceItemAsyncContainer.UpsertItemAsync の代わりに利用する感じになります。メソッドの引数として以下の値を指定することになります。

  • アイテムの ID
  • パーティションキー値
  • PatchOperations (Patch 処理内容を定義したもの)

既にお分かりの方もいるように、今回の Partial Update 機能については、アイテムの id 値を指定 することが必要になっているようです。

ItemResponse<item> response = await container.PatchItemAsync<item>(
                id: 5, // Patch 対象のアイテムの ID
                partitionKey: new PartitionKey(“pkey”), // パーティションキー値
                patchOperations: new[] { PatchOperation.Replace("/TotalDue", 0) } // Patch で更新するプロパティとその値
);

PatchOperation については、以下の内容が指定できます。

  • Add (追加)
  • Remove (削除)
  • Replace (置換)
  • Set (設定)
  • Increment (増加/減少の双方)

上記のように、単一フィールドの Patch を行うことも可能ですが、複数フィールドに対して Patch 処理を行うことも、条件に合致した場合に Patch 処理を行わせることも可能になっています。

// 複数フィールドに対する Patch 処理の例
List<PatchOperation> patchOperations = new List<PatchOperation>();
patchOperations.Add(PatchOperation.Add("/nonExistentParent/Child", "bar")); // nonExistentParent/Child フィールドに bar を Upsert
patchOperations.Add(PatchOperation.Remove("/cost")); // cost フィールドを削除
patchOperations.Add(PatchOperation.Increment("/taskNum", 6)); // taskNum フィールドの値を +6
patchOperations.Add(patchOperation.Set("/existingPath/newproperty", value)); // existingPath/newproperty フィールドに value 変数の値を設定

await container.PatchItemAsync<ToDoItems>(
                id: 5,
                partitionKey: new PartitionKey(“pkey”),
                patchOperations: patchOperations);

// 条件に合致 (フィルター) したアイテムに対する Patch 処理の例
PatchItemRequestOptions patchItemRequestOptions = new PatchItemRequestOptions
{
	FilterPredicate = "from c where (c.TotalDue = 0 OR NOT IS_DEFINED(c.TotalDue))"
};
await container.PatchItemAsync<SalesOrder>(
              id: 5,
              partitionKey: new PartitionKey(“pkey”),
	          patchOperations: new[] { PatchOperation.Replace("/ShippedDate",  DateTime.UtcNow) },
              patchItemRequestOptions);

PatchItemAsync メソッドの詳細や、その他 Partial Update に関する SDK の解説などについては、こちらの GitHub リポジトリを参照してください。

https://github.com/AzureCosmosDB/PatchPrivatePreviewFeedbackGroup

サンプルコード

こちらの GitHub リポジトリに、Partial Update を簡単に試してみるためのコードを用意しています。興味ある方は、実際に試してみてください。
コンソールアプリケーションになっており、dotnet run 実行時の引数に --red--green または --blue を指定することで、指定した RGB の値の部分を更新するものになっています。

https://github.com/ymasaoka/Presentation-materials/tree/main/CosmosDB/2021/8/9

まとめ

これまでの Azure Cosmos DB では、コンテナー内にあるアイテムのデータを更新する際、更新するプロパティ云々に関係なく、アイテム全体を置換する形で更新を行うという処理が行われていました。また、更新処理ではオプティミスティック同時実行制御 (楽観ロック) が採用されていたこともあり、アプリケーションによっては、クライアント側で排他制御を実装しなければならず、開発者にとって負荷となる状況がありました。
Partial Update が登場したことによって、この部分の負が解消され、より開発者に易しい形で Azure Cosmos DB が利用できるようになっていくと嬉しいところです。

ちなみに、Partial Update によって消費する要求ユニットが減るかというと、それは場合によりけりといったところになります。Partial Update に加えて、こういった部分も改善されていって欲しいところです。

参考情報・関連リンク

Discussion