BEACHSIDE BLOG

MicrosoftとかC#を好むレンジャーの個人メモ

Azure Cosmos DB の一意キー制約(Unique Key Constraints)

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)が返ってきました。これでモヤモヤは晴れました。

f:id:beachside:20190220194959p:plain

ここでは、UniqueKey に PartitionKey を含まず検証した例ですが、含んだ場合も同様の結果であることは検証しました(そりゃそうだw)。


一意キー制約 の実装

今回はSDKAzure/azure-cosmos-dotnet-v3 を使っています。

検証は、ConsoleApp で GenericHost で実行して、愚直にコード書いただけとなります。

https://github.com/beachside-project/AzureCosmosContext/tree/master/samples/ConsoleAppSample

プロジェクトの構成をざっくり説明すると、

  • AzureCosmosContext: SDK をラップしたコアのライブラリーになります。
  • ConsoleAppSample: 検証で使った ConsoleApp です。
  • 他のプロジェクトはこの記事と無関係です。


一意キー制約の実装をざっくり説明します。

  • ConsoleAppSample プロジェクト > appsettings.jsoncosmosContainerOptions セクションで Container の設定やパーティションキー・一意制約キーの定義をしています。

  • ConsoleAppSample プロジェクトの起動時に appsettings.json を読み込みますが、具体的な処理は、私が絶賛温め中の AzureCosmosContext プロジェクト > Options > CosmosContainerOptions.cs の中でやってます。ToCosmosContainerSettings() メソッドは、validationしてないので手抜き感があったり拡張メソッドに移そうかと思ってたりですが、まぁそんな感じの手抜き具合いです(笑)。


検証自体のコードは、ConsoleAppSample > UniqueKeyConstraintsInvesigation.cs にありますが、テストプロジェクトでやればよかったと思ったり...。


終わりに

実装上で注意しないといけないのは、SqlQuery でデータを取ろうとする際、パーティションキーを条件に入れ忘れると意図せず複数取得できてしまうケースが想定されるところですね。意図せずパーティションをまたいで複数件取ってきてバグるのが目に見えてるので、この観点のテストコードは必須と心に留めています。

これは一意キーだけではなく id も同様なので、今更改めて忘れるようなことではないですかね...と思ったり。