背景 & やりたいこと
- dbtは多くの場合、後段でBIなどのダッシュボードに接続されます
- つまり、dbtの実行失敗はダッシュボードへの影響があることを意味します
- dbtはDAG形式でmodelを実行していくため、失敗したとしても全て失敗するよりも部分的に失敗することが多いです
- その場合は影響を受けるダッシュボードも一部になります
- 実行が失敗したモデル情報を元に、影響があるダッシュボードのオーナーに素早く連絡をしたいことは多いです
前提
Tableau / Looker Studio / Connected Sheetなど、様々なダッシュボードはdbtのexposureとして登録できます。その自動化のやり方については以下を参照してください。
影響を受けているダッシュボードの一覧を出す
結論から先に。以下のコマンドで一覧をさっと出せます。uv経由でdbtを使う想定で書いていますが、そうでない人はよしなに調整してください。
# ステップ1: 失敗したモデルをの一覧を出す cat target/run_results.json | jq -r '[.results[] | select(.status != "success") | .unique_id | split(".") | .[-1]] | map(. + "+") | join(" ")' > /tmp/models_with_plus.txt # ステップ2: 影響を受けているexposureの一覧を出す uv run dbt ls --select "$(cat /tmp/models_with_plus.txt)" --resource-type exposure --output json --quiet | jq -s '.' > /tmp/affected_exposures.json # ステップ3: 連絡に必要なexposureの情報を付与する jq -s ' .[0] as $exposures | .[1].exposures as $all_exposures | [$exposures[] | .unique_id as $id | $all_exposures[$id] | {id: $id, name: .name, url: .url, label: .label, owner: .owner.name, email: .owner.email}]' /tmp/affected_exposures.json target/manifest.json
出力結果としては以下のようなJSONを得られます。jqでmarkdownに加工してもいいんですが、最近だとこういったところの欲しい加工についてはClaude CodeなどLLM Agentがよしなにやってくれると思うので、この情報が得られれば十分でしょう。
[ { "id": "exposure.my_project.3ac3db04_7879_4c08_b37f_42dfa7b0b597", "name": "3ac3db04_7879_4c08_b37f_42dfa7b0b597", "url": "https://prod-apnortheast-a.online.tableau.com/#/site/my_site/workbooks/1026318/views", "label": "全社KPIダッシュボード", "owner": "Yasuhisa Yoshida", "email": "yasuhisa.yoshida@..." }, { "id": "exposure.my_project.ece56807_e366_4c65_ad24_436270bdb29c", "name": "ece56807_e366_4c65_ad24_436270bdb29c", "url": "https://prod-apnortheast-a.online.tableau.com/#/site/my_site/workbooks/1026322/views", "label": "自分用の砂場ダッシュボード", "owner": "Yasuhisa Yoshida", "email": "yasuhisa.yoshida@..." } ]
解説
大したことはやってないですが、一応どんな感じで動いているのかを解説しておきます。
ステップ1: 失敗したモデルをの一覧を出す
このステップではモデルの一覧を出しますが
target/run_results.jsonにはunique_idしか入っていない- ステップ2で、直接参照ではないケースでも影響を受けるダッシュボードを割り出すために
my_model+の形式で出力する必要があるので、jqで適当に加工
という処理をやっています。
cat target/run_results.json | jq -r '[.results[] | select(.status != "success") | .unique_id | split(".") | .[-1]] | map(. + "+") | join(" ")' > /tmp/models_with_plus.txt
ステップ2: 影響を受けているexposureの一覧を出す
ここは単純です。ステップ1の出力を使いつつ、対象をダッシュボード(exposure)に絞り込みます。
uv run dbt ls --select "$(cat /tmp/models_with_plus.txt)" --resource-type exposure --output json --quiet | jq -s '.' > /tmp/affected_exposures.json
ステップ3: 連絡に必要なexposureの情報を付与する
ここが若干ダルいところです。dbt ls --selectした結果にはexposureのURLやオーナーといった詳細な情報が載っていません。その情報はtarget/manifest.jsonに載っているため2つのJSONファイルの内容をmergeして連絡に必要な情報を取得しています。
jq -s ' .[0] as $exposures | .[1].exposures as $all_exposures | [$exposures[] | .unique_id as $id | $all_exposures[$id] | {id: $id, name: .name, url: .url, label: .label, owner: .owner.name, email: .owner.email}]' /tmp/affected_exposures.json target/manifest.json
普段dbtを開発している人だと、jqよりもSQLのほうが伝わりやすいと思うので、↑と同じ処理をするクエリ(DuckDBで動きます)も載せておきます。
WITH affected_exposures AS ( SELECT unique_id FROM read_json('/tmp/affected_exposures.json') ), all_exposure_details AS ( SELECT key AS unique_id, value->>'$.name' AS name, value->>'$.url' AS url, value->>'$.label' AS label, value->>'$.type' AS type, value->>'$.description' AS description, value->>'$.owner.name' AS owner_name, value->>'$.owner.email' AS owner_email FROM json_each( (SELECT content FROM read_json('/tmp/manifest_exposures.json') AS t(content)) ) ) SELECT unique_id AS id, name, url, label, type, owner_name, owner_email FROM all_exposure_details WHERE unique_id IN ( SELECT unique_id FROM affected_exposures ) ORDER BY unique_id;
まとめ
障害対応中は慌しいので、ステークホルダーへの連絡が後手になることもあると思いますが、こういったスクリプトも活用しながらスムーズな障害対応を目指しましょう。