DocumentDBのChange Feed機能を使う方法

アナウンスされてから気になっていた、DocumentDBのChange Feedについて、ようやくじっくり調べられたので、理解したことをメモしておきます。前回に引き続きのDocumentDBネタですが、今回はXamarinは登場しませんっ。

DocumentDB Change Feedの位置付け

「Change Feed」というくらいなので、変更履歴をどこかにいい感じに通知してくれるようなワンストップソリューション(笑)を期待したのですが、もう少し汎用的な機能でした。

要するに、DocumentDBの変更履歴は、DocumentDB本体のコレクションに問い合わせにいくのではなく、「Change Feed」と呼ばれるDB本体とは別の入口(DBの変更ログが蓄積されたデータ)を用意しましたのでそっちから取得してくださいという感じだと思います。

しかも、Change Feedは自分で都度変更データを取得しにいく必要があるので、公式ブログやAzure Docsでアピールされているような「リアルタイムの変更追跡ソリューション」的なものを実現するには、Azure FunctionsあたりでChange Feedを継続的にモニターして、変更があれば何らかのアクションを起こすような仕掛けを独自に作る必要があります。

changefeed
(引用元: https://docs.microsoft.com/ja-jp/azure/documentdb/documentdb-change-feed)

Change Feedのデータ

Change Feedで取得できるデータはだいたい以下のようなイメージです(詳細な仕様はAzure Docsを参照)。

  • 取得したデータは変更日時順にあらかじめソートされている(パーティション単位)
  • 取得開始の位置をetagで指定できる
  • 物理削除は対象外(論理削除を利用することが推奨されている)
  • パーティション毎に並列で取得可能

気をつけなければならないのは、大規模なコレクションでパーティションを分割している場合、パーティション毎にChange Feedが別々になる点です。逆にいうと、変更順序を厳密に守ってイベント処理したいようなシナリオでは、順序づけしたいデータを同一パーティションに含まれるように設計しなければなりません。

Change Feedの取得方法

Azure Docsの例では複数パーティションも考慮したコードになっているので、ちょっと取っつきにくかったです(バグもあったし・・)。なので、ここでは単一パーティションを前提としたシンプルなコードを掲載しておきます。

まず、ChangeFeedOptionsでどのようにChange Feedを取得するかを設定します(9行目)。設定で最も重要なポイントは、RequestContinuationに入れる「etag」の値で、どこからの変更を取得したいかを指定する点です(12行目)。

変更データが存在すれば、(7行目の)CreateDocumentChangeFeedQueryの結果として返ってくるFeedResponse<>に変更データが入ってきます(21行目)。あとは、変更データを使って通知イベントなどを発行すれば良いと思います(上記例ではコンソールでIDを出力しているだけ)。

データ取得後は、ResponseContinuationに新しいetagが入ってくるので(30行目)、次回のクエリ取得に使います(これを忘れないように)。

Change Feed取得処理の配置

Change Feedの取得自体は能動的に行う必要があるので、Azure Functions等で一定時間毎に継続実行させておき、変更データをキャッチしたらAzure Storageキュー(順番保証したい場合はServiceBusを使う)に出力するのが良いかと。

そして、変更イベントを処理(通知など)するFunctionは別に用意して、キュートリガーで変更からのイベント処理を行うような配置がスケーラビリティを確保できそうです。

ちなみに、今回の検証中に公式ドキュメントのサンプルコードにバグを見つけてしまったので、一か八かPR出したらマージされました(笑)。

Azure/azure-documentdb-dotnet: change RunDemoAsync #209