Alternative Architecture DOJO

オルターブースのクラウドネイティブ特化型ブログです。

Azure Cosmos DBデザインパターン解説:Document versioningパターン

こんにちは、MLBお兄さんこと松村です。
ブログを書いていなかった間に、MLBのスプリングトレーニングが始まってしまいました。


今回は Azure Cosmos DB for NoSQL のデザインパターン解説記事の第5弾です。
前回は「グローバル分散ロック (Global distributed lock) パターン」を解説しました。

aadojo.alterbooth.com

今回は「ドキュメントのバージョン管理 (Document versioning) パターン」について解説をします。
このデザインパターンについては、Cosmos DB の GitHub リポジトリやブログで紹介されていますので、詳しく読みたい方はこちらもご覧ください。

github.com

devblogs.microsoft.com


「ドキュメントのバージョン管理」と聞いて、私は2つのパターンを思い浮かびました。
なお、ドキュメント=JSONデータと読み替えてください。

  1. ドキュメントの変化を記録すること
  2. ドキュメントのスキーマの変化を記録すること

今回のデザインパターンは、前者の「データ(JSON)の変化を記録すること」を実現するための方法です。

そもそも、なぜデータの変化を記録する必要があるのでしょうか?
いついかなる時も必要とは思いませんが、例えば監査証跡として使用するなど、時間の経過に伴うドキュメントへの変更の追跡できるような管理を行う必要がある場合に、今回のデザインパターンが活用できると考えます。

Document versioningパターンのドキュメントにも載っていますが、このデザインパターンにおけるメリットとデメリットは以下であると解説されています。

  • メリット
    • データの整合性が維持しやすく、破損したデータの回復にも使える
    • 複数のユーザーやシステムによるデータの更新を記録できる
    • 時間の経過に伴うデータの変化を分析できる
    • 現在のデータと履歴のデータを保管するコンテナーを分離することで、クエリ効率が向上する
    • データの状態変化をトリガーにしたビジネスロジックを実装しやすくなる
    • ユーザーがデータの履歴を比較・表示するような、ユーザーエクスペリエンスを実現できる
    • ビジネスの変化により、データの変更を管理することによるシステムの堅牢性や拡張性を確保できる
  • デメリット
    • データの管理が複雑になる
    • データが変更されるたびに、新しいバージョンのドキュメントが作成され、書き込み操作が増える
    • 過去のデータ(履歴)へのアクセスは別コンテナーへのクエリが必要となる

いくつかデメリットはあるものの、変更をすべて記録するには良いデザインパターンだと理解しました。

このデザインパターンは、Azure Functions での Cosmos DB Change Feed という機能を使って実現します。
Cosmos DB Change Feed とは、Cosmos DB のドキュメントの変更をトリガーとし、対象のデータを操作したり、他のビジネスロジックを実行するための手法です。

learn.microsoft.com

learn.microsoft.com

今回はサンプルを動かしながら確認したほうがイメージしやすいです。
サンプルはデザインパターンのリポジトリで公開されています。リポジトリをクローンして、実際に動かしてみましょう。

Cosmos DB Change Feed のコードを抜粋すると以下です。
このコードでは、Cosmos DB に書き込まれたドキュメントを履歴用のコンテナーに書き込む処理となっています。解説をコメントで記述しています。

[FunctionName("DocumentVersioningProcessor")]
public static async Task Run([CosmosDBTrigger(
    databaseName: "Orders",
    containerName: "CurrentOrderStatus",
    Connection = "CosmosDBConnection",
    LeaseContainerName = "leases", CreateLeaseContainerIfNotExists=true)]IReadOnlyList<VersionedOrder> input,
    [CosmosDB(databaseName: "Orders",
                containerName: "HistoricalOrderStatus", 
                Connection="CosmosDBConnection", CreateIfNotExists=true, PartitionKey="/CustomerId")] IAsyncCollector<VersionedOrder> historicalOrdersOut,
    ILogger log)
{
    // 変更されたドキュメントがあることを確認する
    if (input != null && input.Count > 0)
    {
        log.LogInformation("Document count: " + input.Count);
        
        // 変更されたドキュメントのデータを取得する
        foreach (VersionedOrder versionedOrder in input)
        {
            log.LogInformation($"Processing {versionedOrder.OrderId} - Status: {versionedOrder.Status}");
            
            // 履歴データのIDを採番する
            versionedOrder.id = System.Guid.NewGuid().ToString();
            
            // バインドを使って、履歴データをCosmos DBに書き込む
            await historicalOrdersOut.AddAsync(versionedOrder);
        }                
    }
}

※実データの作成・更新処理はアプリ側で行うため、解説は割愛します。サンプルコードをご覧ください。


RDB で「変更のあったデータを別のテーブルに書き込む」処理を実装する場合は、トリガーとプロシージャを使ったりするかと思いますが、Cosmos DB でも Azure Functions によって実現できるということです。

データの変化を逐一記録する必要がある場合に、有用なデザインパターンですので参考にしてください。


サービス一覧 www.alterbooth.com cloudpointer.tech www.alterbooth.com