Microsoft developer platformのChange Feed更新まとめ|Cosmos・DynamoDB・Spanner対応で確認すべき点

Microsoft developer platformの2026年5月5日更新で確認すべきポイントは、マルチクラウドDB向けJava SDKに、Azure Cosmos DB、Amazon DynamoDB Streams、Google Cloud Spanner Change Streamsを横断して扱うポータブルなChange Feed APIが追加される点です。要するに、各データベースごとにCDC処理を書き分けるのではなく、MulticloudDbClient.readChanges()を中心に、行レベルの変更イベントを共通の形で取得できるようにする更新です。PR #74は執筆時点でOpenのため、すぐ本番適用するというより、既存設計・設定・移行計画を先に点検するタイミングと考えるのが安全です。(GitHub)

この更新で特に影響を受けるのは、Cosmos DB、DynamoDB、Spannerのいずれかで変更データキャプチャ、監査ログ、イベント連携、非同期同期処理を実装しているJavaアプリケーションです。すでに各クラウドのネイティブAPIでChange FeedやStreamsを読んでいる場合は、共通APIへ寄せられる範囲と、プロバイダー固有の制約を切り分けて確認する必要があります。

目次

Microsoft developer platformのChange Feed更新で何が変わるのか

今回のMicrosoft developer platform documentation updateでは、Multicloud DB SDK for Javaに「単一のChange Feedサーフェス」を追加する内容が整理されています。PRのSummaryでは、Azure Cosmos DB、Amazon DynamoDB Streams、Google Cloud Spanner Change Streamsからの行レベル変更イベントを、1つのAPIで消費できるようにする変更と説明されています。(GitHub)

主な変更は次の通りです。

観点変更前の考え方更新後の考え方
CDCの実装Cosmos、DynamoDB、Spannerごとに個別実装しやすいreadChanges()で共通の変更取得処理に寄せられる
イベント形式プロバイダーごとにイベント構造が異なるChangeEventでCREATE、UPDATE、DELETEを共通表現
再開処理ネイティブカーソルやシーケンス番号を個別管理opaqueなcontinuationTokenを保存して再開
スケールアウト各DBのパーティション・シャード仕様を直接扱うFeedScope.physicalPartition()で物理パーティション単位の消費を表現
非対応機能実装によって静かに劣化するリスクがあるUNSUPPORTED_CAPABILITYで早期に失敗させる設計

実務上の価値は、「マルチクラウド対応」という言葉よりも、CDC処理のアプリケーション側コードを標準化しやすくなることにあります。たとえば監査ログ基盤、検索インデックス更新、別DBへのレプリケーション、キャッシュ無効化、イベント駆動処理などで、データベース変更を起点にする設計が作りやすくなります。

対応すべきチームと、様子見でよいチーム

今回の更新は、すべてのMicrosoft developer platform利用者が即対応すべき変更ではありません。判断基準は、現在のアプリケーションが「変更イベント」を扱っているかどうかです。

対象対応優先度理由
Cosmos DB、DynamoDB、SpannerでCDCを使っているJavaアプリ高共通APIへの移行候補になり、設定不足があるとイベント欠落につながる
複数DBを切り替えるSDK設計を採用しているチーム高Capability確認と非対応機能の分岐が必要
監査ログ、レプリケーション、検索インデックス更新をChange Feedで動かしているチーム高重複処理、再開トークン、削除イベントの扱いを見直す必要がある
CRUDやクエリだけを使っているアプリ低直接の影響は小さいが、SDKバージョン更新時のAPI追加は確認したい
本番利用前のPoC・検証段階中PRがOpenかつPublic Preview扱いのため、検証環境での確認が現実的

リポジトリのREADMEでは、このSDKはPublic Previewであり、本番利用には未完成の機能や破壊的変更の可能性があると明記されています。したがって、記事執筆時点では「本番移行を即決する更新」ではなく、「設計レビューと検証項目を洗い出す更新」と捉えるのが適切です。(GitHub)

追加される主要APIと実装時の見方

中心になるのは、MulticloudDbClientに追加されるreadChanges(ChangeFeedRequest, OperationOptions)です。PR対象ブランチのコードでは、変更フィードを1ページずつ読み、結果としてChangeFeedPageを返すインターフェースになっています。listPhysicalPartitions()も追加され、プロバイダー固有の物理パーティションIDを取得して並列消費に使える設計です。(GitHub)

ChangeEventで見るべき項目

ChangeEventには、少なくとも次の観点があります。

項目実務での使い方
provider()Cosmos、DynamoDB、Spannerなどの発生元識別
eventId()重複排除のキー。少なくともproviderId + eventIdで管理する
eventType()CREATE、UPDATE、DELETEの分岐処理
key()対象レコードの識別
data()新しいアイテム状態。削除や設定次第ではnullになり得る
commitTimestamp()プロバイダーが提供できる場合のコミット時刻

重要なのは、Change Feedの配信は「少なくとも1回」、つまりat-least-once前提で設計されている点です。ChangeEventのJavadocでも、利用側はproviderIdとeventIdで重複排除すべきとされています。(GitHub)

最小構成の実装イメージ

if (!client.capabilities().isSupported(Capability.CHANGE_FEED)) {
    throw new IllegalStateException("Change Feed is not supported by this provider.");
}

ChangeFeedRequest request = ChangeFeedRequest.builder(address)
        .startPosition(
                savedToken == null
                        ? StartPosition.now()
                        : StartPosition.fromContinuationToken(savedToken)
        )
        .newItemStateMode(NewItemStateMode.INCLUDE_IF_AVAILABLE)
        .maxPageSize(100)
        .build();

ChangeFeedPage page = client.readChanges(request);

for (ChangeEvent event : page.events()) {
    String dedupeKey = event.provider().id() + ":" + event.eventId();

    if (dedupeStore.alreadyProcessed(dedupeKey)) {
        continue;
    }

    switch (event.eventType()) {
        case CREATE -> handleCreate(event);
        case UPDATE -> handleUpdate(event);
        case DELETE -> handleDelete(event);
    }

    dedupeStore.markProcessed(dedupeKey);
}

if (page.continuationToken() != null) {
    checkpointStore.save(page.continuationToken());
}

このコードで最も大切なのは、continuationTokenを処理後に保存することです。イベント処理前に保存すると、処理失敗時にイベントを取りこぼす可能性があります。逆に処理後に保存すると、再起動時に重複処理が起こる可能性があります。そのため、実運用では重複排除ストアとチェックポイント保存をセットで設計します。

プロバイダー別の対応範囲を確認する

Change Feed APIは「全プロバイダーで完全に同じことができる」わけではありません。Microsoft developer platformの今回の更新で重要なのは、共通化できる部分と、Capabilityで分岐すべき部分が明示されたことです。

機能Cosmos DBDynamoDB StreamsSpanner Change Streams実装時の判断
基本的なChange Feed対応対応対応Capability.CHANGE_FEEDで確認
時刻指定開始 StartPosition.atTime()PR上は対応扱い非対応対応DynamoDBでは分岐必須
論理パーティション単位の購読対応非対応非対応Cosmos専用機能として扱う
物理パーティション単位の購読対応対応対応並列ワーカー設計に使える
削除イベントAll Versions and Deletesが前提Streamsで取得可能Change Streamsで取得可能事前プロビジョニングが重要

DynamoDB Streamsはテーブル変更を最大24時間ログとして保持し、レコードには変更前後のイメージを設定に応じて含められます。AWS公式ドキュメントでも、Streamsの保持期間は最大24時間であり、StreamViewTypeにNEW_IMAGEやNEW_AND_OLD_IMAGESを選べることが説明されています。(AWS Documentation)

Spannerでは、Change StreamはDDLで作成するスキーマオブジェクトです。公式ドキュメントでは、作成時に保持期間やvalue_capture_typeなどを設定できると説明されています。(Google Cloud Documentation)

移行前に必ず確認したい設定チェックリスト

Cosmos DBはAll Versions and Deletes前提で考える

Cosmos DBでCREATE、UPDATE、DELETEを区別して扱うには、All Versions and Deletesモードが重要です。Azure Cosmos DB公式ドキュメントでは、Latest versionモードは作成・更新の最新状態を扱う一方、削除は変更として記録されないと説明されています。一方、All versions and deletesモードは作成・更新・削除を記録しますが、継続的バックアップが必要で、プレビュー扱いの記述もあります。(Microsoft Learn)

実務では、次の確認を行います。

確認項目見るべきポイント
コンテナーのChange FeedモードAll Versions and Deletesが有効か
継続的バックアップ必要な保持期間が業務要件を満たすか
削除イベントの扱い物理削除をイベント化するのか、ソフトデリートで代替するのか
パーティションキーカスタムパスや階層パスを使っている場合、イベントのキー抽出を検証する
公式仕様との差分PR上の対応説明と、現在のAzure公式ドキュメントの制限が一致するか確認する

特に注意したいのは、PR側ではCosmos providerがallVersionsAndDeletes()を使い、beginningやatTimeの開始位置もコード上に見えますが、Azure公式ドキュメントではAll Versions and Deletesモードについて、過去時刻やコンテナー開始時点からの読み取りに制限がある旨も記載されています。移行判断では、PRの説明だけでなく、利用するCosmos DB SDKバージョンとAzure側の最新仕様を検証環境で確認してください。(GitHub)

DynamoDB Streamsは24時間保持とStreamViewTypeが要点

DynamoDB Streamsで最も失敗しやすいのは、チェックポイントの放置です。Streamsのデータ保持は最大24時間のため、ワーカー停止や障害対応が長引くと、保存済みトークンから再開できない可能性があります。PRでも、古いチェックポイントではCHECKPOINT_EXPIREDを返す設計が示されています。(GitHub)

確認すべき設定は次の通りです。

確認項目推奨確認
StreamEnabledtrueになっているか
StreamViewType新イメージが必要ならNEW_IMAGEまたはNEW_AND_OLD_IMAGES
障害復旧時間24時間以内に再開できる運用体制か
ワーカー数同じシャードを複数プロセスで読みすぎていないか
時刻指定開始DynamoDBではStartPosition.atTime()を使わない設計にする

DynamoDBでは「テーブル作成時からすべて再生できる」と考えると危険です。StartPosition.beginning()を使う場合でも、実質的には保持期間内の最古レコードからの開始と考えるべきです。

SpannerはChange StreamのDDLとvalue_capture_typeを確認する

Spannerでは、Change Feedを読む前にChange StreamをDDLで作成しておく必要があります。PRのconfiguration更新でも、SDK自体はCDCリソースを作成せず、各プロバイダーで事前プロビジョニングが必要とされています。(GitHub)

例として、PR側のドキュメントでは次のようなDDLが示されています。

CREATE CHANGE STREAM events_changes
FOR events
OPTIONS (value_capture_type = 'NEW_ROW');

確認すべきポイントは、Change Stream名、監視対象テーブル、保持期間、value_capture_typeです。newItemStateMode = REQUIREで新しい行状態が必須の処理を組む場合、Change Stream側がその情報を提供できる設定になっていないと、SDK側でUNSUPPORTED_CAPABILITYになる可能性があります。(GitHub)

Capability確認を移行手順に組み込む

今回のChange Feed更新では、非対応機能を静かに代替するのではなく、Capabilityで明示的に判定し、対応できない組み合わせはUNSUPPORTED_CAPABILITYで早期に失敗させる方針が取られています。これは実務上、かなり重要です。静かに劣化すると、削除イベントが取れていない、時刻指定開始が効いていない、論理パーティション指定が無視されている、といった問題に気づきにくくなるためです。(GitHub)

実装では、少なくとも次の3つを確認します。

CapabilitySet caps = client.capabilities();

boolean canReadChangeFeed =
        caps.isSupported(Capability.CHANGE_FEED);

boolean canStartAtTime =
        caps.isSupported(Capability.CHANGE_FEED_POINT_IN_TIME);

boolean canReadLogicalPartition =
        caps.isSupported(Capability.CHANGE_FEED_LOGICAL_PARTITION_SCOPE);

判断基準はシンプルです。

やりたいこと必要なCapability非対応時の代替
変更イベントを読むCHANGE_FEEDネイティブCDC実装を維持する
特定時刻から読むCHANGE_FEED_POINT_IN_TIME保存済みチェックポイント、またはnow()開始に変更
論理パーティションキーで絞るCHANGE_FEED_LOGICAL_PARTITION_SCOPE物理パーティションで読んでアプリ側でフィルタ
並列消費するlistPhysicalPartitions()全体購読を単一ワーカーで開始し、後で分割

アプリケーションの初期化時にCapabilityをログ出力しておくと、環境差分の調査が楽になります。特にマルチクラウド対応アプリでは、設定ファイルを変えただけでプロバイダーが切り替わるため、起動時に「この環境で何が使えるか」を明示する運用が有効です。

既存実装から移行する手順

既存のCosmos Change Feed、DynamoDB Streams、Spanner Change Streams実装をこのAPIへ寄せる場合は、いきなり置き換えるのではなく、次の順序で進めるのが安全です。

| 手順 | 作業内容 | 失敗しやすいポイント |
| -: | —————– | —————————————————— |
| 1 | 現在のCDC用途を分類する | 監査、同期、検索更新などで必要なイベント粒度が違う |
| 2 | 必要なイベントを定義する | 削除イベント、更新前後の値、時刻指定開始の要否を曖昧にしない |
| 3 | 各プロバイダー設定を確認する | Cosmosのモード、DynamoDBのStreamViewType、SpannerのDDLを見落としやすい |
| 4 | Capability分岐を実装する | 非対応機能を前提にして本番で落ちるケースがある |
| 5 | 重複排除キーを決める | eventIdだけでなくproviderIdも含める |
| 6 | チェックポイント保存を設計する | 保存タイミングを誤ると欠落または重複が増える |
| 7 | 並列消費を検証する | 物理パーティション分割・退役時のchildPartitions処理が必要 |
| 8 | 旧実装と並走比較する | イベント件数、順序、削除検知、再開動作を比較する |

移行初期は、旧CDC処理と新しいreadChanges()処理を一時的に並走させ、イベントID、対象キー、イベント種別、タイムスタンプを比較するのがおすすめです。特に削除イベントは設定差分が出やすいため、テストデータでCREATE、UPDATE、DELETEを明示的に発生させて確認します。

運用で注意すべき失敗パターン

continuationTokenを小さな文字列カラムに保存する

Continuation tokenはopaqueな再開カーソルです。PRの設計資料では、プロバイダー、リソース識別子、プロバイダーカーソルを含むエンベロープとして扱われ、パーティション分割が続くと数KBになる可能性も示されています。(GitHub)

varchar(255)のような短いカラムに保存すると、トークンが切り詰められて再開不能になるリスクがあります。データベースに保存するなら、TEXT相当のカラムや十分な長さの文字列型を使い、HTTPヘッダーに載せて運ぶ設計は避けた方が安全です。

グローバル順序を期待する

Change Feedは、全コレクション・全テーブルで完全なグローバル順序を保証するものではありません。PRの設計資料でも、単一ページ内や単一パーティション内の順序はプロバイダーのネイティブ順序に依存し、コレクション全体のグローバル順序は保証しない考え方が示されています。(GitHub)

順序が必要な処理では、次のように設計します。

要件設計例
同一キーの更新順を守りたいキー単位で直列処理する
全体の時系列集計をしたいcommitTimestampを使い、遅延到着を許容する集計窓を設ける
外部システムへ冪等に反映したいイベントIDを外部側にも保存し、二重反映を防ぐ
削除後の更新など矛盾を避けたいキーごとの最終状態管理テーブルを持つ

UNSUPPORTED_CAPABILITYを例外処理だけで片付ける

UNSUPPORTED_CAPABILITYは、単なるエラーではなく「設計上、そのプロバイダーではできないこと」を示すシグナルです。例外を握りつぶして処理を続けると、イベント欠落や不完全な同期が起こります。

たとえば、DynamoDBでStartPosition.atTime()が使えない場合、「現在時刻から読む」に落とすのか、「保存済みチェックポイントがないなら処理を開始しない」のかを業務要件で決める必要があります。監査ログ用途なら、勝手にnow()へフォールバックするのは危険です。

今回の更新を受けて次に取るべき行動

まず、利用中のSDKリポジトリでPR #74がmainへマージ済みか、パッケージとして公開済みかを確認してください。執筆時点でPRページはOpen表示であり、docs/changelog上のChange Feed追加もUnreleasedとして扱われています。(GitHub)

次に、アプリケーションごとに次の3点を確認します。

確認すること判断基準
Change Feedを本当に使うかCRUD・Queryのみなら急ぎの対応は不要
必要なイベント粒度削除、更新前後の値、時刻指定開始が必要か
各DBの事前設定CosmosのAVAD、DynamoDB Streams、Spanner Change Stream DDLが整っているか

最後に、検証環境でCREATE、UPDATE、DELETE、ワーカー再起動、古いチェックポイント、パーティション分割相当のケースをテストします。今回のMicrosoft developer platformのChange Feed更新は、CDC処理を共通化する有力な変更ですが、プロバイダー差分が消えるわけではありません。共通APIで書ける部分と、Capabilityで分岐すべき部分を分けて設計することが、移行を安全に進める一番の近道です。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次