Cosmos DB の一意キー制約(Unique Key Constraints)について、実装方法やエラー時の挙動を調べたメモです。
いきなり余談ですが、
一昔前に Cosmos DB で Collection と呼ばれていたものは、Azure の Portal だとまだ "Collection" と表現されていますが、ドキュメントや最新の SDK (Azure/azure-cosmos-dotnet-v3 では、Container と呼ばれるようになりましたね。ということでこのブログでも Container と書きます。
一意キー制約(Unique Key Constraints) とは
id
自体は一意の制約がありますね。
それとは別に、特定のプロパティ(またはプロパティの組み合わせ)で一意の制約を持たせることがきる機能です。
id
と同様、Container の論理パーティションの中で一意とするものです。
具体的な例は、公式ドキュメントを見た方が良いですね。
Azure Cosmos DB で一意なキーを使用する | Microsoft Docs
一意キー制約 の検証
検証として使うDocumentのスキーマ( Cosmos の Container のスキーマ)は以下にしました。
{ "id" : string, "division": string, "companyId": string, "employeeId": string }
検証に使った Cosmos DB の Container の設定は以下の通り。
- Partition Key は "/division"
- Unique Key Constraints は、 "/companyId", "/employeeId"
「エラー発生時にどんなエラーがでるんだろ」の興味が事の発端だったのですが、いろんなパターンで試してみたいと思いだし、結局以下パターンを試しました。
No | 説明(既存データとの比較) | idの値 | divisionIdの値 | companyId の値 | employeeIdの値 | データは入る? |
---|---|---|---|---|---|---|
- | これが既存データ | 100 | Div0 | Company0 | Employee0 | - |
1 | idは異なる Partition Key同じ Unique Keyは異なる |
101 | Div0 | Company1 | Employee1 | 成功 |
2 | idは異なる Partition Key同じ Unique Keyは同じ |
102 | Div0 | Company0 | Employee0 | エラー |
3 | idは異なる Partition Key異なる Unique Keyは異なる |
103 | Div3 | Company3 | Employee3 | 成功 |
4 | idは異なる Partition Key異なる Unique Keyは同じ |
104 | Div4 | Company0 | Employee0 | 成功 |
5 | idは同じ Partition Key同じ Unique Keyは異なる |
100 | Div0 | Company5 | Employee5 | エラー |
6 | idは同じ Partition Key同じ Unique Keyは同じ |
100 | Div0 | Company0 | Employee0 | エラー |
7 | idは同じ Partition Key異なる Unique Keyは異なる |
100 | Div7 | Company7 | Employee7 | 成功 |
8 | idは同じ Partition Key異なる Unique Keyは同じ |
100 | Div8 | Company0 | Employee0 | 成功 |
成功/エラーは予想通り違和感のない結果でしたが、No.7 と No.8 は大丈夫かなーと気にしてました。
ちなみに Azure Portal で Data Explorer からデータを入れた場合、No.7 と No.8のような Partition Key が異なる同一の id を挿入することはできませんが、API 経由だとできます。
また、最も気にしてたエラー時の挙動ですは、CosmosException 型でエラーをキャッチされ、StatusCodeプロパティで "Conflict" (409)が返ってきました。これでモヤモヤは晴れました。
ここでは、UniqueKey に PartitionKey を含まず検証した例ですが、含んだ場合も同様の結果であることは検証しました(そりゃそうだw)。
一意キー制約 の実装
今回はSDKに Azure/azure-cosmos-dotnet-v3 を使っています。
検証は、ConsoleApp で GenericHost で実行して、愚直にコード書いただけとなります。
https://github.com/beachside-project/AzureCosmosContext/tree/master/samples/ConsoleAppSample
プロジェクトの構成をざっくり説明すると、
- AzureCosmosContext: SDK をラップしたコアのライブラリーになります。
- ConsoleAppSample: 検証で使った ConsoleApp です。
- 他のプロジェクトはこの記事と無関係です。
一意キー制約の実装をざっくり説明します。
ConsoleAppSample プロジェクト > appsettings.json の
cosmosContainerOptions
セクションで Container の設定やパーティションキー・一意制約キーの定義をしています。ConsoleAppSample プロジェクトの起動時に appsettings.json を読み込みますが、具体的な処理は、私が絶賛温め中の AzureCosmosContext プロジェクト > Options > CosmosContainerOptions.cs の中でやってます。
ToCosmosContainerSettings()
メソッドは、validationしてないので手抜き感があったり拡張メソッドに移そうかと思ってたりですが、まぁそんな感じの手抜き具合いです(笑)。
検証自体のコードは、ConsoleAppSample > UniqueKeyConstraintsInvesigation.cs にありますが、テストプロジェクトでやればよかったと思ったり...。
終わりに
実装上で注意しないといけないのは、SqlQuery でデータを取ろうとする際、パーティションキーを条件に入れ忘れると意図せず複数取得できてしまうケースが想定されるところですね。意図せずパーティションをまたいで複数件取ってきてバグるのが目に見えてるので、この観点のテストコードは必須と心に留めています。
これは一意キーだけではなく id
も同様なので、今更改めて忘れるようなことではないですかね...と思ったり。