[ver1.5新機能]dbtでモデル自体をバージョン管理し、後続のモデルへの影響を確認してから最新のモデルをリリースできる「Versions」を試してみた
さがらです。
先日、dbt-coreのver1.5がリリースされました。
dbt-core ver1.5の新機能として、「Versions」がリリースされました。この機能を試してみたので、本記事で試した内容をまとめてみます。
Versionsとは
一言でいうと、モデル自体をバージョン管理し、後続のモデルへの影響を確認してから最新のモデルをリリースできる機能です。
dbtはSELECT文の記述でモデルと言う形で簡単にデータ変換処理を定義できますが、急にあるモデルにおいてデータ型が変更になったりカラムを削除したり、ということを行うと後続のモデルに影響を及ぼしてしまいエラーを出すようになってしまう可能性もあります。
そんなときに役立つのが今回追加された「Versions」です!これを使うことで、dbtのモデルについてバージョン管理を行いモデルのプレリリース期間を設けた上で最新バージョンに移行するという流れをスムーズにできるため、モデルでデータ型変更やカラムの削除などがあっても、エラーを起こすことなく最新バージョンに移行することができます。
先日私が試したContractsと併せて使うと、より正確にモデルのデータ型を担保しつつ、後続のモデルに影響がないことを確認してから最新のモデルをリリース出来るので、おすすめです!
検証概要
検証環境
- DWH:Snowflake
- dbt Cloud
- dbt-Core:ver1.5 ※Environmentより設定
検証内容
中間モデルのintermediate_customers.sql
で、カラム名がわかりづらいと不評だったfirst_order
列をfirst_order_date
に変更することを考えたとします。このとき、急に後続のモデルやに影響がでないようにVersionを使ってみます。
Versions適用前の各ファイル
Versionsの設定をを適用させる前の各ファイルは下記のように定義されているとします。
intermediate_customers.sql
select customer_id, first_name, last_name, first_order, number_of_orders from {{ ref("int_customer_order_history_joined") }}
intermediate_customers.yml
models: - name: intermediate_customers config: contract: enforced: true columns: - name: customer_id data_type: varchar constraints: - type: not_null - type: primary_key tests: - unique - not_null - name: first_name data_type: varchar - name: last_name data_type: varchar - name: first_order data_type: date - name: number_of_orders data_type: number
mart_customers.sql
※intermediate_customers.sql
を参照する後続のモデル
select * from {{ ref('intermediate_customers') }}
- データリネージ
カラム名を変更したモデルをversion:2としてプレリリースする
まずintermediate_customers.sql
で、first_order
列をfirst_order_date
列に名称変更することを行います。ただ、後続のモデルに影響がでないようにVersionsの設定を適用し、プレリリースします。
まず、intermediate_customers_v2.sql
という新しいモデルを下記のように定義します。_v2
という接尾辞は後でVersionsを設定する際に使うバージョン値となります。
select customer_id, first_name, last_name, first_order as first_order_date, number_of_orders from {{ ref("int_customer_order_history_joined") }}
続いて、intermediate_customers.yml
を以下のように変更します。主な変更点は下記のとおりです。
latest_version: 1
として、intermediate_customers
の現行バージョンは1
であることを宣言するversions:
のところで、各バージョンの差分のみを記述するv: 1
は、既存のyamlのカラム定義をそのまま引用するためinclude: all
だけ定義する。alias: intermediate_customers
とすることで、生成されるオブジェクトの名称の末尾に_v1
がつくことを回避できるため、これはお好みでv: 2
は、include: all
とexclude: [first_order]
としてfirst_order
以外のすべての定義はそのまま使うように宣言し、first_order_date
を別途定義することで、version:2での差分を表すようにする
models: - name: intermediate_customers latest_version: 1 config: contract: enforced: true columns: - name: customer_id data_type: varchar constraints: - type: not_null - type: primary_key tests: - unique - not_null - name: first_name data_type: varchar - name: last_name data_type: varchar - name: first_order data_type: date - name: number_of_orders data_type: number versions: - v: 1 columns: - include: all config: alias: intermediate_customers - v: 2 columns: - include: all exclude: [first_order] - name: first_order_date data_type: date
この上で、新しいversion:2を参照するには、{{ ref('intermediate_customers', version=2) }}
のようにref関数を記述する必要があります。実際に、プレリリースのversion:2のモデルを参照するmart_customers_test.sql
を下記のように定義してみました。
参照先としても、_v2
で作られたオブジェクトを参照するようになっています。
select * from {{ ref('intermediate_customers', version=2) }}
また既存のversion:1のモデルを参照する際には、{{ ref('intermediate_customers') }}
と記述するだけでlatest_version:
に定義されているversionを参照する用になっています。そのため、latest_version:1
の場合にはversion:1のモデルを参照するようになっています。
実際、intermediate_customers.sql
を参照する後続のモデルmart_customers.sql
では、下記のように特に定義を変えていないですが、version:1のaliasであるintermediate_customers
を参照するようになっています。
ちなみにこの2つのバージョンを持つ状態で、dbt build --select intermediate_customers+
を実行すると、下図のようにversion:1、version:2どちらも実行されます。
このときのデータリネージは下図のように表されます。個人的には、リネージ上でもversionsがわかると嬉しいですね!
カラム名を変更したversion:2のモデルを本番リリースする
プレリリースしたカラム名を変更したversion:2のモデルの検証が上手くいったと仮定し、実際に本番リリースしてみます!
まず、version:2をリリースしたいintermediate_customers.yml
について、以下のように変更します。変更点のポイントは下記です。
latest_version: 1
から、latest_version: 2
に変更するv: 1
でversionの接尾辞を付けないようにaliasを設定していた場合は、v: 2
の方にalias: intermediate_customers
と言った形でaliasを転記する
models: - name: intermediate_customers latest_version: 2 config: contract: enforced: true columns: - name: customer_id data_type: varchar constraints: - type: not_null - type: primary_key tests: - unique - not_null - name: first_name data_type: varchar - name: last_name data_type: varchar - name: first_order data_type: date - name: number_of_orders data_type: number versions: - v: 1 columns: - include: all - v: 2 config: alias: intermediate_customers columns: - include: all exclude: [first_order] - name: first_order_date data_type: date
また、元々version管理をしていなかった場合は元々のversion:1のモデルを定義するファイル名がintermediate_customers.sql
となっていると思いますので、これをintermediate_customers_v1.sql
と名称変更することで、過去のバージョンであることを明示的にするようにします。※これを行わないと、上のyamlを変更したタイミングで「同じモデルが2つあるよ」というコンパイルエラーが出てきます…
- version:1として運用していたファイル名が
intermediate_customers.sql
の場合 ※エラーが発生する
- version:1として運用していたファイル名を
intermediate_customers_v1.sql
に変更した場合
この状態で、dbt build --select intermediate_customers+
を実行してみると、version:2として定義したカラム名を変更後のモデルが実行され、元々運用していたmart_customers
もversion:2のカラムfirst_order_date
を参照して構築されたことがわかります。これで、version:2の本番リリースは完了です!
最後に
dbtでモデル自体をバージョン管理し、後続のモデルへの影響を確認してから最新のモデルをリリースできる「Versions」を試してみました。
正直、dbt1.5の新機能の中で一番複雑な機能で、運用もちゃんと考えないと逆に混乱を招いてしまう可能性もある機能だと感じました。ただ有効に使えれば、データ型の変更やカラムの削除・名称変更時など後続のモデルに影響を及ぼす変更を行うときに、トラブルを起こさずに移行することが出来ます!