Azure Data Factory(ADF)のCDC(Change Data Capture/Preview)でAzure SQL Databaseの増分コピーをしていたら、突然「cdc.fn_cdc_get_net_changes_* の引数不足」エラーで失敗することがあります。原因の大半は“本当に引数が足りない”のではなく、capture_instanceやLSN(ウォーターマーク)の前提崩れです。最短で切り分けて復旧する手順をまとめます。
発生しやすい状況とエラーメッセージ
本記事で扱うのは、次のようなケースです。
- ADFのコピー(またはデータフロー)で、SQL Database → SQL DatabaseへCDC(Preview)を使って増分連携している
- これまで問題なく動いていたのに、ある日から急に失敗するようになった
- 失敗ログにはSpark/JDBC経由のSQL実行エラーが出ており、メッセージに次の文言が含まれる
An insufficient number of arguments were supplied for the procedure or function cdc.fn_cdc_get_net_changes_<capture_instance>
このメッセージだけを見ると「関数呼び出しの引数が足りない=ADFが不正なSQLを投げた?」と考えがちですが、実務では“引数不足に見える別要因”で発生していることが多いです。特に、CDCの読み取り位置(LSN)や参照しているcapture_instanceがズレたときに、結果としてこのエラーに落ちることがよくあります。
ADFのCDC(Preview)で内部的に起きていること
トラブルシューティングを短時間で終わらせるには、ADFとSQL Database側で「何を前提に増分を取っているのか」を押さえるのが近道です。CDC(Change Data Capture)は、トランザクションログから変更を取り出す仕組みで、CDCの関数(TVF)を呼ぶと指定した範囲の変更行が返ります。
| 用語 | 意味 | 現場で起きる事故 |
|---|---|---|
| capture_instance | CDCが対象テーブルに対して作る“監視の単位”。通常は schema_table のような名前になる | 再有効化や複数定義でインスタンスが増え、ADFが意図しない方を参照する |
| LSN | ログ上の位置を表すID(binary(10))。CDCはLSN範囲(from/to)で変更を取得する | 停止・遅延で、読み取り開始LSNが保持期間より古くなり参照不能になる |
| min_lsn | そのcapture_instanceで“現在残っている最古のLSN” | ウォーターマークがmin_lsnより古いと復旧できず、想定外のエラーに見える |
| max_lsn | 現在の“最新のLSN”。変更が進めば進む | max_lsnが進まない場合、CDCのキャプチャが止まっている可能性がある |
| ウォーターマーク(チェックポイント) | 前回どこまで読んだか(from_lsn)を示す保存値 | 壊れる/ズレる/古すぎると、次回実行が失敗して止まる |
| 保持期間(retention) | CDCが変更データを保持しておく期間。期限を過ぎるとクリーンアップされる | パイプライン停止が保持期間を超えると、過去分が失われる |
ADFのCDC(Preview)は、最終的にはSQL側のCDC関数(例:cdc.fn_cdc_get_net_changes_<capture_instance>)を、from_lsn/to_lsnなどのパラメーター付きで呼び出して増分行を取り出します。つまり、下記のどれかが崩れると失敗しやすくなります。
- 参照するcapture_instanceが正しくない(別インスタンスを見ている)
- from_lsn/to_lsnの範囲が不正(古すぎる、存在しない、整合しない)
- CDCが進んでいない/内部ジョブが止まっている(max_lsnが更新されない)
- Previewコネクタの仕様変更で、生成SQLやパラメーターバインドが噛み合わなくなった
「引数不足」エラーが出る主要因と、見分け方
実務で遭遇頻度が高い順に、原因・典型サイン・復旧の方向性を整理します。
| 原因パターン | 典型サイン | まずやること |
|---|---|---|
| LSN範囲(ウォーターマーク)が保持範囲外 | しばらく停止していた/遅延が続いた後に失敗。min_lsnより古いfrom_lsnを参照している | min_lsn/max_lsnと、ログに出るfrom_lsnを突き合わせる |
| capture_instanceの取り違え | CDCを再有効化した、または複数のcapture_instanceが存在。テーブル側は正常でもADFが別名を見ている | sp_cdc_help_change_data_captureでcapture_instanceを洗い出す |
| Previewコネクタの更新・仕様変更 | 環境側の変更がないのに突然失敗。直前までの実行は成功している | ADF側のCDC設定を作り直し(状態リセット)で挙動が変わるか確認 |
| CDC自体が進んでいない | max_lsnが進まない/変更が取れない。DMVでログスキャンが止まっている | CDC関連の稼働状態(セッション/エラー)を確認して復旧 |
| 権限・接続ユーザーの問題 | 最近ユーザー/権限を変えた。別のエラーと混在することもある | cdc_readerロール付与、対象DB/スキーマへの権限を確認 |
最短で切り分けるチェック
「どこから手を付ければいいか分からない」状態でも、次の2点を押さえると、原因がほぼ絞れます。
- 参照しているcapture_instanceが想定通りか
- ADFのウォーターマーク(from_lsn)が、SQL側のmin_lsn以上になっているか
capture_instanceが想定通りか確認する
まずは、CDCが有効で、どのcapture_instanceが存在するかを列挙します。
EXEC sys.sp_cdc_help_change_data_capture;
結果に複数の行が並ぶ場合、同じ元テーブルに対して複数のcapture_instanceが存在している可能性があります。特に次のような状況で増えやすいです。
- 同じテーブルでCDCを無効→再度有効にした(suffixが付いた新インスタンスが作られる)
- 移行・リストア・IaCデプロイなどでCDC設定が再適用された
- テーブル名変更・スキーマ変更の過程で再設定された(自覚がなくても運用のどこかで起きる)
次に、対象のCDC関数(TVF)が“どんな引数を要求するか”を確認します。実際に必要な引数数と型が分かれば、ログに出ているSQLが本当に引数不足なのか、それとも別要因なのかを判断しやすくなります。
-- 関数定義(ソースコード)を確認
SELECT OBJECT_DEFINITION(OBJECT_ID('cdc.fn_cdc_get_net_changes_<capture_instance>')) AS definition;
一般的にfn_cdc_get_net_changesは、from_lsn/to_lsn/row_filter_option の3引数を取ります。にもかかわらず「引数不足」になるときは、次のどちらかが多いです。
- ADFが参照している関数が“想定していたcapture_instanceの関数ではない”(別名の関数を呼び、結果的に解釈が崩れる)
- LSN範囲が不正で、内部的に関数呼び出しが成立せず“引数不足に見える”エラーとして返っている
min_lsn / max_lsn とウォーターマークの整合性を確認する
次に、CDCで取得可能なLSN範囲を確認します。
-- 例:capture_instance = 'dbo_Table' の場合
SELECT
sys.fn_cdc_get_min_lsn('dbo_Table') AS min_lsn,
sys.fn_cdc_get_max_lsn() AS max_lsn;
ここで重要なのは、ADFが次回実行で使おうとしているfrom_lsn(ウォーターマーク)が、min_lsn以上であることです。もしfrom_lsnがmin_lsnより古い場合、CDC保持期間の外に落ちているため、その範囲の変更はすでにクリーンアップされており、正常に増分取得できません。
ADFのCDC(Preview)が保存しているウォーターマークは、環境や構成によって見え方が異なります。まずは次を試してください。
- ADFのモニター画面で、失敗したアクティビティの出力(Error details)を開き、SQLやLSN(0x…)が含まれていないか確認する
- 診断ログ(Log Analytics)を有効にしている場合は、実行時に生成されたクエリやパラメーターが出力されていないか検索する
- どうしても取れない場合は、ADF側の状態をリセットして「どのLSNから読み始めるか」を再設定できる形に切り替える
原因候補A:capture_instanceのズレを直す
capture_instanceのズレは、症状が分かりにくいのが厄介です。テーブルは同じでも、CDCは“インスタンス単位”でstart_lsnや保持データを持つため、ADFが別インスタンスを参照すると、LSN整合性が崩れて読み取れなくなります。
よくあるズレのパターン
- 本来:
dbo_Tableを参照したいのに、ADFがdbo_Table_1(または古い方)を参照している - 本来:スキーマが変わっていないつもりでも、運用・デプロイでCDC再設定が走り、別インスタンスが生まれている
- 参照先のDBが切り替わっている(本番/検証/DRなど)
確認SQL(複数インスタンスの有無)
SELECT
ct.capture_instance,
OBJECT_SCHEMA_NAME(ct.source_object_id) AS source_schema,
OBJECT_NAME(ct.source_object_id) AS source_table,
ct.start_lsn,
ct.create_date
FROM cdc.change_tables AS ct
ORDER BY ct.create_date DESC;
同じsource_tableに対して複数行がある場合は、ADFがどれを見ているかを合わせる必要があります。最短復旧を優先するなら、ADF側のCDCソース設定を作り直し、正しいcapture_instanceに合わせ直すのが手堅いです(Preview機能は内部状態がブラックボックスになりやすいため、状態の“作り直し”が効きます)。
CDC設定を整理する(不要なインスタンスを減らす)
運用上不要なcapture_instanceが残っていると、将来も同様の取り違えが起きやすくなります。環境の要件に合わせて、CDCの再設定・整理を検討してください。
- 不要なcapture_instanceを無効化する(実行前に影響範囲を確認)
- CDCを再有効化する場合は、ADF側も同時に作り直して整合を取る
-- 注意:実行には権限が必要です。影響を理解した上で実施してください。
-- 対象テーブルのCDCを無効化(capture_instanceを指定して無効化する例)
EXEC sys.sp_cdc_disable_table
@source_schema = N'dbo',
@source_name = N'Table',
@capture_instance = N'dbo_Table';
無効化・再有効化は、取得できる変更履歴の連続性に影響します。連携データの整合性が重要な場合は、無効化の前に「欠落期間はフルコピーで埋める」「差分照合を取る」など、復旧計画をセットで用意しておくと安全です。
原因候補B:LSN範囲(ウォーターマーク)が不正で起きている
現場で最も多いのがこのパターンです。CDCは保持期間を過ぎると古い変更を削除します。つまり、パイプラインが停止・遅延し、最後に読んだLSNからの再開が保持期間を超えてしまうと、もうその範囲の変更は取得できません。
確認のポイント
min_lsnは“今ある最古”。これより前はもう取り出せない- from_lsn(ウォーターマーク)がmin_lsnより古いなら、どこかで“繰り上げ”が必要
- 繰り上げると、欠落期間の変更はCDCからは取れない(別手段で埋める必要がある)
復旧の選択肢
| 復旧方針 | いつ選ぶか | メリット | 注意点 |
|---|---|---|---|
| from_lsnをmin_lsnまで繰り上げて再開 | なるべく取りこぼしを減らしたい/欠落期間が短い | 保持されている範囲の“最古”から拾える | min_lsnより前は埋められない。欠落期間がある前提で整合性確認が必要 |
| from_lsnをmax_lsn(現在)まで繰り上げて再開 | 過去分は諦めて早く復旧したい/欠落期間が長い | すぐに“今から”の増分が取れる | 欠落期間が大きくなる。フルコピーや再同期の計画が必須 |
| 欠落期間をフルコピーで埋めてから再開 | データ整合性が最優先/欠落が許されない | 欠落を実データで補完できる | 負荷・時間がかかる。フルコピーの範囲設計が必要 |
min_lsn/max_lsnを具体的に取得するSQL
DECLARE @capture_instance sysname = N'dbo_Table';
SELECT
sys.fn_cdc_get_min_lsn(@capture_instance) AS min_lsn,
sys.fn_cdc_get_max_lsn() AS max_lsn;
-- 参考:LSNと時刻の対応(存在する範囲の“端”を掴むのに便利)
SELECT TOP (20)
ltm.start_lsn,
ltm.tran_begin_time
FROM cdc.lsn_time_mapping AS ltm
ORDER BY ltm.tran_begin_time DESC;
ADFの内部ウォーターマークを直接編集できない構成の場合、最短復旧としては「CDCソース定義を作り直して状態を初期化し、開始点を再設定できるようにする」ことが現実的です。欠落期間が問題になる場合は、次の章の“外部ウォーターマーク管理”の形に切り替えると、復旧が圧倒的に楽になります。
ADF側の状態リセットで最短復旧する
Preview機能は、内部で保持するチェックポイントやパラメーター生成がブラックボックス化しがちです。SQL側のCDC設定が正しく見えても、ADF側の状態が古い前提のままだと、失敗がループすることがあります。そういうときは、ADF側のCDC設定を“新しい前提で作り直す”のが最短のことが多いです。
具体的には、次のようなリセットが効きます。
- CDCソースのLinked service/Datasetを作り直す(同名ではなく新規にするのが確実)
- CDC(Preview)を使っているCopy Activityやデータフローを作り直す
- パイプラインを新規に切り出し、既存のものは退避して比較できるようにする
このとき、復旧判断として重要なのは「どこから再開するか」です。CDC保持期間を超えている疑いがある場合は、最初から“欠落がある前提”で、次のどちらかに寄せます。
- 整合性優先:欠落期間をフルコピーで埋める計画を立ててから増分再開
- 可用性優先:最大LSN付近(現在)から再開し、過去は別バッチで補う
外部ウォーターマーク管理に切り替えると復旧が楽になる
「Previewコネクタに依存すると、急に壊れた時に打てる手が少ない」という課題があるなら、ウォーターマーク(from_lsn)を自前テーブルで管理する方式が有効です。ADFは“今のウォーターマークを読み、CDC関数で差分を取り、成功したらウォーターマークを更新する”というシンプルなオーケストレーションに徹します。
ウォーターマークテーブル例
CREATE TABLE dbo.CdcWatermark (
capture_instance sysname NOT NULL PRIMARY KEY,
last_lsn binary(10) NOT NULL,
updated_at_utc datetime2(3) NOT NULL DEFAULT (SYSUTCDATETIME())
);
取得クエリ例(net changes)
capture_instance名は動的SQLが必要になるため、運用ではストアドプロシージャ化してADFから呼ぶのが安全です(SQLインジェクション対策にもなります)。ここではイメージだけ示します。
-- 例:@from_lsn, @to_lsn を渡して差分を返す(概念例)
SELECT *
FROM cdc.fn_cdc_get_net_changes_<capture_instance>(@from_lsn, @to_lsn, N'all');
この方式のメリットは、異常時に「last_lsnをmin_lsnへ補正して再開する」「max_lsnまで繰り上げて今から再開する」といった判断を、明示的にコントロールできる点です。Preview機能の内部状態に依存しないため、トラブルシューティングの自由度が上がります。
CDCが進んでいない(max_lsnが更新されない)場合の確認
LSNが古い/ズレている以前に、CDCがログから変更を取り込めていないと、何度実行しても期待通りに増分が出ません。Azure SQL DatabaseとSQL Serverで見え方は違いますが、参考として“動いているか”の確認観点を載せます。
- 直近のログスキャンセッションが継続しているか
- CDC関連のエラーが溜まっていないか
- 対象テーブルで実際に変更が発生しているか(テスト更新して反映を見る)
-- 直近のログスキャン状況(参照可能な環境で)
SELECT TOP (20)
session_id,
start_time,
end_time,
error_count,
scan_phase,
duration
FROM sys.dm_cdc_log_scan_sessions
ORDER BY start_time DESC;
-- CDCエラー(参照可能な環境で)
SELECT TOP (50)
*
FROM sys.dm_cdc_errors
ORDER BY entry_time DESC;
ここでエラーが出ている場合は、まずCDC側を正常化しない限りADF側の対処だけでは改善しません。ストレージ逼迫、ログの問題、権限、サービス側の一時障害など、原因は幅広いので、DB側の監視・メトリクスも併せて確認してください。
権限まわりの最低限チェック
メッセージが「引数不足」でも、実際には権限が絡んで別の症状に見えることがあります。特に、ADFの接続ユーザーを変更した、資格情報をローテーションした、Managed Identityへ移行した、といったタイミングがあるなら要チェックです。
一般的には、CDCの読み取りにはcdc_readerロールが使われます(環境によっては追加権限が必要な場合があります)。
-- 例:ユーザーをcdc_readerに追加
ALTER ROLE cdc_reader ADD MEMBER [your_user];
加えて、参照先DBが意図したものか(接続文字列/サーバー名/DB名)も確認してください。DR切替や接続先差し替えが起きた場合、「CDCが有効化されていない別DBに繋いでいた」という単純な原因でハマることがあります。
再発防止の運用ポイント
復旧できても、同じ構造のままだと再発しやすいので、運用面の“刺さる”ポイントをまとめます。
保持期間と停止許容時間を合わせる
CDCには保持期間があり、停止・遅延が保持期間を超えると、ウォーターマークが保持範囲外になって復旧が難しくなります。運用上「週末は止める」「障害で数日止まる可能性がある」なら、保持期間をそれ以上に設定する、あるいは止まったら即アラートが飛ぶようにするなど、前提を揃えてください。
“止まったまま”を検知する監視を入れる
- ADFパイプラインの失敗を即通知(メール/Teams/アラート)
- 連携先テーブルの更新時刻が一定時間進まない場合に検知
- min_lsnが進む速度と、ウォーターマークの進みを監視して乖離を検知
欠落期間が出たときのランブックを用意する
LSN範囲外に落ちた場合、CDCから欠落分を取り戻せないことがあります。そのときに慌てないよう、次の意思決定を事前に運用手順として決めておくと復旧が早いです。
- 欠落が発生した場合は、対象期間をフルコピーで埋めるのか
- 欠落を許容するのか(許容する場合、どのテーブル/期間までか)
- 整合性確認(件数突合、ハッシュ比較、差分抽出)をどう取るのか
トラブルシューティング用チェックリスト
最後に、現場でそのまま使えるチェックリストを置いておきます。エラーが出たら上から順に潰していくと、調査が迷子になりにくいです。
| チェック項目 | 確認方法 | 判断基準 | 次のアクション |
|---|---|---|---|
| 接続先DBが正しい | ADFのLinked service、接続文字列、DB名を確認 | 想定の本番DB/サーバーに繋がっている | 誤接続なら修正して再実行 |
| CDCがDBで有効 | SELECT is_cdc_enabled FROM sys.databases WHERE name = DB_NAME(); | 1なら有効 | 0なら有効化(権限/影響を確認) |
| capture_instanceが想定通り | EXEC sys.sp_cdc_help_change_data_capture; | 対象テーブルに対して意図したcapture_instanceが存在 | 複数あるなら整理、ADF参照先を合わせる |
| min_lsn/max_lsnの範囲 | sys.fn_cdc_get_min_lsn / sys.fn_cdc_get_max_lsn | from_lsnがmin_lsn以上 | 範囲外ならウォーターマーク繰り上げ+欠落補完 |
| CDCが進んでいる | DMV(可能なら)や変更テストで確認 | max_lsnが進む、変更が反映される | 進まないならCDC側を復旧(DB負荷/ログ/エラー確認) |
| ADF側の状態 | CDCソースを作り直し、再実行で改善するか | 作り直しで改善する場合、内部状態が原因 | 恒久対応として外部ウォーターマーク管理を検討 |
「capture_instanceの整合」と「LSN範囲の整合」を押さえるだけで、ほとんどの“突然壊れたCDC”は原因に辿り着けます。Preview機能ゆえに一発で直らない場合でも、SQL側の事実(min/max LSN、capture_instance)を基点にすれば、最短距離で復旧方針を決められます。

コメント