追記
以下、色々書いていますが、dbt-osmosisの作者に課題感を共有した上でそれを解決したPull Requestを取り込んでもらい、よりシンプルに解決できるようになりました。
meta.osmosis_keep_description
を付与した上で--force-inheritance
を使えば意図通りに運用できます。
背景: dbt-osmosisを運用に乗せたい
- 少し前にdbt-osmosisを紹介するエントリを書いた
- データリネージを考慮しながら、メタデータの伝播をしてくれる便利なツール
- しかし、運用に乗せようと思うと、これだけだと足りない点があり、まだ運用に乗せ切れていない...
- 「親のメタデータが更新されたときに子どものメタデータをどう更新するか」問題
- データカタログを強化していきたい機運が高まっており、そうするとカラムのメタデータの役割は非常に大きい
- dbt-osmosisを運用に乗せるために必要な残りのパーツを埋めていきたい
運用時に考慮するべき課題
dbt-osmosisの運用時に考えないといけない課題は一言でいうと「親のメタデータが更新されたときに子どものメタデータをどう更新するか」だと思っている。
以下のようなカラムのリネージがあったとしよう。
graph LR; X --> Y; X --> Y'; Y --> Z; Y' --> Z';
dbt-osmosisを最初に動かす場合、Xにさえdescriptionを付与すれば、YやZにもdescriptionが伝播される。この場合はよいし、dbt-osmosisの便利なところである。
問題はXが更新された場合だ。カラムのdescriptionが更新されるのは、例えば
- 「5はXXXを意味する」といったようなenum的な意味の追加
- 「このカラムはdeprecatedになった。代わりにあのカラムを使って欲しい」といった注意喚起
- 「2023/07/01以降はnullableになった」などデータの仕様の変更に対する注意書き
などのケースだ。こういった情報はYやZにも伝播して欲しいし、これらが漏れるとデータマートの利用者が困ることが出てくるだろう。
dbt-osmosisはYやZにすでに値が入っていれば、ディフォルトでは特にrewriteしない。dbt-osmosisはrewriteさせるオプション--force-inheritance
が存在するので、例えば以下のようなコマンドを打つと、Xが更新されていればYやZも更新される。
% dbt-osmosis yaml refactor --force-inheritance --fqn my_model
force-inheritanceオプションで解決しない問題
--force-inheritance
で全てが解決すればよいのだが、残念ながら世の中はそんなに簡単ではない。Xが更新されていれば、基本的にはYやZも更新して欲しいが、そうでないケースがあるからだ。例えば、以下のようなケースだ。
xxx
というカラムがDWHで定義されていて、データマート側でも同じxxx
というカラムがあるが、定義が若干変わっておりその旨をデータマート側のカラムのdescriptionに反映したい- 例:
MY_FUNCTION(xxx) AS xxx
のように、ちょっと処理をして同名にrenameする
- 例:
- 日毎のユーザー数を表わす
users_count
というカラムがあるが、月毎に集約した別テーブルにusers_count
というカラムがある- 「日毎の」や「月毎の」といった意味をdescriptionに反映させたい
- この例はテーブル名から類推して欲しいのでちょっと微妙だが、集約したい場合などで意味が変わることは他の条件(フィルタの条件が変わるなど)が変わることも考えるとそれなりにあると思う
こういったケースでは--force-inheritance
を実行してしまうと、子ども側のカラムのdescriptionの情報が必要以上に上書きされてしまうので、大変困る。
解決方法: dbtのmetaを使って上書きさせない情報をフィールドに追加する
上記の課題を考慮すると、以下のような解決方法が考えられるだろう。
- 解決方法1:
--force_inheritance
しつつ、カラムによっては無視できるオプションをdbt-osmosis側に実装する- 将来的にはうれしそう
- すぐに取り込んでもらえるかは分からない
- 解決方法2:
--force_inheritance
は使わず、親側が更新されたら追従して欲しいカラムのdescriptionを事前に削除しておく- 削除後に
dbt-osmosis yaml refactor
を動かせば削除された部分を再度親の値で埋めてくれる - dbt-osmosis側にprを取り込んでもらう必要はない
- 手動で削除して回るのは現実的ではないが、yqなどスクリプトを組み合わせれば可能
- 削除後に
今回はアイディアの筋がよいかをさっと試したり、フィードバックをもらいたいのが目的のため、解決方法2でやってみることにする。
解決方法2を実現するためには「このカラムは特殊な処理が入っているので、親側が更新されても更新しない」という意図を示す情報をyamlファイルに落とし込む必要がある。dbtで独自のフィールドを追加する方法として、meta
というものがある。
例えば、カラムのmeta
にosmosis_keep_description
というフィールドを生やし「このカラムは保持してくれ」という情報を追加する。
version: 2 models: - name: my_model columns: - name: my_column description: このモデル専用のロジックがこのカラムには含まれているよ!! meta: osmosis_keep_description: true
ちなみに、このmetaの情報はdbt docsにも反映されるので「親のdescriptionを引き継いだものか、このmodel特有のものか」を判断することができる。残念ながらBigQueryに反映されるわけではない(タグテンプレートでタグとしてDataplexに送り込むなど、やりようはあるとは思う)。
osmosis_keep_description
が付いていないカラムのdescriptionを一括で削除したい場合、yqコマンド*1を使うと簡単にyamlファイルを書き換えてくれる。
yq --inplace '(.models[].columns[] | select(.meta.osmosis_keep_description != true)) |= del(.description)' models/my_model.yml
このコマンドを実行した上で、dbt-osmosisを--force-inheritance
なしで実行すれば「保持したいカラムのdescriptionはそのままで、そうでないカラムは親のdescriptionを引き継ぐ」ということが実現できる。
% dbt-osmosis yaml refactor --fqn my_model
まとめ
dbt-osmosisは便利だが、運用上「親のメタデータが更新されたときに子どものメタデータをどう更新するか」問題をどうするか考える必要がある。今回はdbtのmetaを使ってこの問題を回避する方法を提示してみた。
この方法でしばらく運用してみて筋がよさそうであれば本家のdbt-osmosisにPull Requestを送ってみようと思うし「こっちの方法がいいんじゃない?」とか「うちではこう対処しているよ」などがあれば是非コメントなどで教えて欲しい。
*1:yq、いつの間にかpythonからgoに変わっていた