こんにちは、MLBお兄さんこと松村です。
この記事はオルターブース アドベントカレンダー 2023の19日目の記事です。
今回は Azure Cosmos DB for NoSQL のデザインパターン解説記事の第3弾です。
前回は「データビニング (Data Binning) パターン」を解説しました。
今回は「分散カウンター (Distributed Counter) パターン」について解説をします。
このデザインパターンについては、Cosmos DB の GitHub リポジトリやブログで紹介されていますので、詳しく読みたい方はこちらもご覧ください。
分散カウンターという言葉の意味をまず整理します。
データベースにある1つの項目に対して、複数のクライアントアプリケーションから同時に更新を行おうとすると、どうしても変更の競合という問題にぶつかります。
例えば EC サイトで商品を1つ購入した際に、在庫数を1つ減らすクエリを発行する必要がありますが、そうしたクエリを複数箇所で同じ商品に発行すると、競合により整合性がとれなくなります。
Cosmos DB ではこのような同時更新に対応できるよう「オプティミスティック同時実行制御」という仕組みが用意されています。
ただしオプティミスティック同時実行制御では、同時に更新しようとすると一方の操作は成功し、もう一方の操作は失敗します。
よって、EC サイトのようなケースでは適切な方法ではないようです。
そこで、分散カウンターというデザインパターンを用いることで、この問題を回避しリアルタイム更新が実現可能になるとのこと。
文章を読むよりサンプルコードを見たほうが理解が早そうなので、サンプルコードを見てみます。
リポジトリをクローンして SETUP.md に沿って Cosmos DB リソースを準備すれば、皆さんの手元でも動かすことができます。
分散カウンターの処理で一番大事なのは、ドキュメントのカウンターのみを部分更新することです。
Cosmos DB には HTTP PATCH 要求でドキュメントの一部のみを更新することができます。
SDK でのコードを例にすると以下のなります。
PatchOperation.Increment
で指定するのが、 "countervalue"
というプロパティに対してインクリメント(デクリメント)を行う数値です。
List<PatchOperation> operations = new() { PatchOperation.Increment($"/countervalue",(value *-1)) }; await container.PatchItemAsync<DistributedCounter>( dcId, new PartitionKey(pcId), patchOperations: operations, requestOptions: new PatchItemRequestOptions { FilterPredicate = "FROM Counters c WHERE c.status = 0 and c.countervalue >=" + value });
この部分更新がよいのは、更新対象のドキュメントを取得しなくてよいことです。
指定した ID やパーティションキーに該当するドキュメントに対して、直接更新を行うため「取得している間に更新されたら」みたいな同時実行制御を気にしなくて済みます。
その他、サンプルではドキュメントのカウント値が減る様子を見るためのダッシュボードも用意されており、Blazor + Cosmos DB のリアルタイムアプリケーションの面白さを体験することができます。
サービス一覧 www.alterbooth.com cloudpointer.tech www.alterbooth.com