CreateMultiple(バルク作成 API)を使ったのにジョブが大量に積まれる。あるいは処理速度が単発と変わらない。原因の多くは受け手にある。
送る側がバルクになっても、受け取るプラグインが「1件ずつ処理」のままなら、Dataverse は内部でバルク要求を件数分の単発イベントに分解し、既存プラグインを N 回個別に発火させる。この挙動が「縮小発火」(公式呼称:message pipelines merged の逆方向動作)だ。
結論を先に言う。受け手のプラグインを CreateMultiple イベントに登録し直し、EntityCollection をまとめて処理するロジックに書き替えて初めて、バルク化の効果が出る。 天井は消えない——ただし、正直に言えば「移る」だけだ。それも含めて説明する。
なぜ受け手が「1件ずつ」だと遅いのか
Microsoft Learn 公式ドキュメント(2026年時点)には次の一文がある:
“When you use a bulk operation message, the respective Create and Update event occurs for each Entity instance in the Targets parameter.”
N 件を CreateMultiple で送ると、Create に登録されたプラグインは N 回個別に呼び出される。バルク API を叩いた送り側からは 1 リクエストに見えていても、受け手の世界では N 回のプラグイン実行サイクルが回る。
これが「縮小発火」(message pipelines merged の逆方向)と呼ぶ挙動の正体だ。公式ドキュメントで明示されており、仕様通りの動作——つまり、バグではない。
この構造で問題になるのは、同期プラグインの実行時間だ。公式は「同期プラグインが 2 秒を超えると深刻なパフォーマンス低下につながる」と明記している。100 件のバルク要求で縮小発火が起きていれば、同期プラグインは合計で最大 200 秒分のサイクルを費やすことになる。送る側だけをバルクにしても、ここが律速だ。
受け手の直し方——CreateMultiple への移行手順
プラグインを CreateMultiple イベントに対応させる方向は任意だが、公式ドキュメントは「パフォーマンス向上はバルク版に書き換えたプラグインのみが享受できる」と明示している。実質、書き替えが必要になる。
移行の手順は次の流れで踏む。
ステップ 1:既存の単発プラグインを無効化(または削除)してから移行する
これを省くと二重実行のリスクがある。公式は次のように明記している:
“Duplicate logic can potentially be applied on both single and bulk message of an event, and Dataverse will not try to prevent this.”
単発プラグインを残したままバルクプラグインも登録すると、同じロジックが 2 回走る。Dataverse はこれを自動的に防がない。移行は必ず「無効化してから追加」の順序で行う。
ステップ 2:Targets パラメータ(EntityCollection)を一括処理するロジックに書き替える
CreateMultiple 向けプラグインは IPluginExecutionContext の InputParameters["Targets"](EntityCollection 型)を受け取り、コレクション内の各 Entity を一括でまとめて処理する。1 件ずつループして個別処理する構造では、縮小発火と本質が変わらないことに注意する。
ステップ 3:entity images のサイズを確認する
バルク操作プラグインでは entity images のデータが Targets 件数分増加する。Dataverse のサンドボックス環境には 116.85 MB のメッセージサイズ制限があり、大量件数のバッチでは制限に当たる可能性がある。1 バッチあたりの件数を調整する設計を最初から組み込む。
アップスケール——単発も拾われる、だから登録は一本化できる
CreateMultiple プラグインに移行した後、もう一つ確認すべき挙動がある。これを知っておくと、単発向けと複数向けで二系統を管理する必要がなくなる。
公式ドキュメントはこう説明している:
“When you use a single operation message, the respective bulk operation event occurs with an EntityCollection containing a single Entity instance passed in the Targets parameter.”
単発の Create メッセージが送られた場合でも、CreateMultiple に登録されたプラグインが**EntityCollection(1 件)**として自動的に発火する。これが「アップスケール」(公式呼称:message pipelines merged の順方向動作)だ。
この仕組みにより、CreateMultiple 一本に登録を統一すれば、単発 Create が来た場合もバルク Create が来た場合も、同じプラグインが処理を引き受ける。プラグイン登録の二重管理は不要になる。
天井は移る——次の律速を正直に示す
受け手をバルク対応に切り替えると、縮小発火は止まる。ただし、「速くなる」と「ボトルネックが消える」は別の話だ。
プラグインを非同期に設定した場合、次の律速は非同期 FIFO キュー(先入れ先出し型実行キュー)の消化速度になる。公式ドキュメントには次の記述がある:
“If a system job is postponed, all subsequent dependent system jobs continue to wait until the postponed system job executes.”
あるジョブが保留されると、後続の依存ジョブはすべてそのジョブが実行されるまで待機し続ける。バルクプラグインで 1 回の発火を大幅に圧縮しても、キューが詰まっていれば消化は遅延する。
天井は消えない——縮小発火という律速を外したあと、次の律速がキューに移るだけだ。設計時に「どこが次の律速になるか」を予測しておくことが、トラブルシュートの工数を減らす。
発火パターンの比較
| 構成 | プラグイン発火回数(N 件送信時) | 実行コンテキスト |
|---|---|---|
| 送る側だけ CreateMultiple / 受け手は Create 登録のまま | N 回(縮小発火) | 1 件ずつ / InputParameters[“Target”](単数) |
| 両端バルク化:受け手も CreateMultiple 登録 | 1 回 | EntityCollection / InputParameters[“Targets”](複数) |
| 単発 Create / 受け手は CreateMultiple 登録 | 1 回(アップスケール) | EntityCollection(1 件入り)/ InputParameters[“Targets”] |
公式確認済みの制約として:
- 同期プラグインは 2 秒超で深刻なパフォーマンス低下(公式明記)
- サンドボックスのメッセージサイズ上限は 116.85 MB(Targets 件数が多い場合は注意)
- 並列度(DOP)はサーバーが動的に推奨値をレスポンスヘッダーで返す(固定値なし)
件数あたりの処理速度の具体的な数値は一次ソースに存在しない。設計判断は上記の構造的差異と定性的制約値をもとに行う。
設計時に確認する 4 点
1. 既存 Create / Update プラグインの棚卸し
CreateMultiple への移行前に、今どのプラグインがどのメッセージに登録されているかを一覧化する。無効化の対象を洗い出してから移行を始める。
2. 同期 / 非同期の選択
同期の場合は 2 秒制限を必ず意識する。重い処理(外部 API 呼び出し・複雑なバリデーション)は非同期に切り替える。ただし非同期に切り替えた場合は FIFO キューが次の律速になる点を設計に織り込む。
3. バッチサイズの上限設計
Targets に詰め込む件数が多すぎると 116.85 MB 制限に当たる。1 バッチあたりの件数を設定し、送り側の分割ロジックと受け手の処理能力を合わせた設計にする。
4. テーブル種別の確認
Elastic Tables(Azure Cosmos DB バックエンド)は標準テーブルより高いスケール性能を持つ。大量取り込み要件で標準テーブルに縛られている設計は、テーブル種別から見直す選択肢がある(数値は非公開のため、定量判断は実測が必要)。
M365 Developer Tenant で動かしてみる
上記の発火パターンは、M365 Developer Program の無料テナントで CreateMultiple プラグインを実際に登録して確認できる。Plugin Trace Log を有効にすれば、縮小発火のログと一本化後の差異を目視で比較できる。
→ 関連記事:Dataverse への大量取り込み、ツール選択の判断軸
まとめ
CreateMultiple でバルク送信しても速くならないとき、受け手のプラグインが Create に登録されたままなら、原因はそこだ。単発プラグインを無効化してから CreateMultiple プラグインを作り、EntityCollection をまとめて処理するロジックに切り替える。アップスケールにより単発 Create も同じプラグインが拾うため、登録は一本化できる。次の律速は非同期キューへ移る——それも含めて設計する。
次の一歩:Plugin Registration Tool でプラグインのメッセージ登録先を確認し、Create と CreateMultiple の二重登録がないかを最初に棚卸しする。Microsoft Learn「Write plug-ins for CreateMultiple and UpdateMultiple」が実装の出発点になる。
関連記事: