コードリーディングで理解する dbt seed の仕組み
はじめに
こんにちは!ナウキャストのデータエンジニアのけびんです。
皆さん dbt の seed は使っていますか?ナウキャストでも使っている機能なのですが、利用する中で dbt seed の仕組みについて理解できていなかったポイントがあり、バッチジョブがエラーになってしまったことがありました。
本ブログでは dbt-adapter のコードも紹介しつつ dbt seed の仕組みについて解説します。
dbt seed とは?
dbt の Seed は CSV ファイルを dbt project で管理し、DWHにロードする機能です。変更頻度はそこまで高くないが、バージョン管理したりレビュー体制を用意しておきたいような小さめのデータの管理に適した機能です。
具体的には小さいディメンションテーブルであったり、絞り込み条件などに利用したいカラムの値のリストの管理などによく使われるかなと思います。
以下のドキュメントのサンプルを引用して仕組みをおさらいしておきましょう。
まず以下のように CSV ファイルを dbt project 内で用意しておきます。
country_code,country_name
US,United States
CA,Canada
GB,United Kingdom
...
あとは dbt seed
コマンドを実行するだけで、 seed_country_codes
という名前のテーブルが出来上がるというわけです。
$ dbt seed
Found 2 models, 3 tests, 0 archives, 0 analyses, 53 macros, 0 operations, 1 seed file
14:46:15 | Concurrency: 1 threads (target='dev')
14:46:15 |
14:46:15 | 1 of 1 START seed file analytics.seed_country_codes........................... [RUN]
14:46:15 | 1 of 1 OK loaded seed file analytics.seed_country_codes....................... [INSERT 3 in 0.01s]
14:46:16 |
14:46:16 | Finished running 1 seed in 0.14s.
Completed successfully
Done. PASS=1 ERROR=0 SKIP=0 TOTAL=1
普通のモデルのように ref
で参照することができます。
select * from {{ ref('seed_country_codes') }}
seed に関しては prefix として seed_
をつけるようにしておくとぱっと見で seed を使っていることがわかるのでおすすめです。
また dbt build
コマンドには seed も含まれるので、 build を使えば依存関係を踏まえ適切に seed の実行も可能です。
dbt seed で実行されるクエリ
dbt seed のコマンドを実行した際に、具体的にどのようなクエリが発行されるかご存知でしょうか?
普通の dbt model のように、毎回 CREATE OR REPLACE TABLE
の CTAS クエリを実行しているのかなと自分は思っていたのですが、実は違います。実際には実行タイミング・オプションによって異なるのですが、
- 初回実行時・
--full-refresh
指定時- (table が存在すれば)
DROP TABLE IF EXISTS {{ relation }} CASCADE
CREATE TABLE
INSERT
- (table が存在すれば)
- 2回目以降の実行時
TRUNCATE
INSERT
という挙動になります。
この挙動のため、seedに関して「カラムの追加」や「カラム名の変更」などがあったりすると dbt seed の実行時にエラーになります。このことは seed の FAQ にある "The columns of my seed changed, and now I get an error when running the seed
command, what should I do?" でも触れられています。
この内容を引用すると、
$ dbt seed
Running with dbt=1.6.0-rc2
Found 0 models, 0 tests, 0 snapshots, 0 analyses, 130 macros, 0 operations, 1 seed file, 0 sources
12:12:27 | Concurrency: 8 threads (target='dev_snowflake')
12:12:27 |
12:12:27 | 1 of 1 START seed file dbt_claire.seed_country_codes...................... [RUN]
12:12:30 | 1 of 1 ERROR loading seed file dbt_claire.seed_country_codes.............. [ERROR in 2.78s]
12:12:31 |
12:12:31 | Finished running 1 seed in 10.05s.
Completed with 1 error and 0 warnings:
Database Error in seed country_codes (seeds/seed_country_codes.csv)
000904 (42000): SQL compilation error: error line 1 at position 62
invalid identifier 'COUNTRY_NAME'
Done. PASS=0 WARN=0 ERROR=1 SKIP=0 TOTAL=1
このようなスキーマの変更で drop cascade
を実行すると下流のモデルに影響がでる可能性があるため、エラーになるようにすることで安全な実装にしているようです。
スキーマが変更される際には下流のモデルへの影響に注意して dbt seed --full-refresh
でフルリフレッシュしましょう。
このように dbt は便利ですが、その裏では様々なクエリが実行されています。dbtが 行なっていることを推測するのではなく、実際のクエリに向き合うことが非常に重要です。
dbt seed の YAML ファイルの書き方について
seed に関する小ネタです。
dbt seed でもメタデータ管理のために普通の dbt model と同様 YAML ファイルを書くと思いますが、実はカラムのデータ型の書き方が普通の dbt model とは少し異なります。通常のモデルだと columns の中に諸々書くと思いますが、以下の通り config → column_types の中に書く必要があるので注意しましょう。
version: 2
seeds:
- name: country_codes
config:
column_types:
country_code: varchar(2)
country_name: varchar(32)
dbt-adapters のコードリーディング
上記の挙動の確認は Snowflake であれば Query History を眺めておくのが手っ取り早いです。しかし細かい部分を把握したければ dbt-adapters のコードを読むのが良いです。
seed のクエリ
seed の実際のマクロは以下のあたりになります。
テーブルが存在していたら reset_csv_table
というマクロを呼び出し、そうでなければ create_csv_table
を呼ぶと分岐していることが確認できます。
実際の reset_csv_table
のマクロはこちらに記載があります。 --full-refresh
モードであれば一度 drop relation してから create_csv_table
のマクロを実行する一方、そうでなければ truncate しておく、という分岐の実装が確認できます。
insert のクエリは load_csv_rows
のマクロで作られていますがこちらについては dbt-snowflake の中で詳細を確認することができます。
このように、 dbt-adapters の多くのマクロは jinja テンプレートで書かれているだけなので、意外と読みやすいです。
詳細な実装を把握するのにはコードリーディングが一番なのでぜひ呼んでみてください。
seed の config
以下のコードを読むと config → column_types を元に CREATE TABLE
のクエリが構成されていることがわかります。
まとめ
dbt seed に関する仕組みついて紹介しました。
パフォーマンス改善の文脈で「推測するな、計測せよ」と言われたりしますが、dbtを利用する際にも dbt が実行していることを推測するのではなく、実際に実行されるクエリと向き合うことが大事になります。また dbt-adapter のコードを読んだりしてみると多くの学びがありました。
皆さんも dbt seed に関する知見があれば教えてください!
Discussion