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 DB | DynamoDB Streams | Spanner 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)
確認すべき設定は次の通りです。
| 確認項目 | 推奨確認 |
|---|---|
StreamEnabled | trueになっているか |
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で分岐すべき部分を分けて設計することが、移行を安全に進める一番の近道です。

コメント