📌

dbtのtag利用をelementaryを使って統制する

2024/02/09に公開

やりたいこと

dbtの tags はjobの制御として非常に強力だが、tagの種類自体は増えすぎないようにしたい。任意の文字列を設定できるので実装そのものは非常に簡単な一方、乱用されると似たようなtagが複数存在するようになったり、改修時のtagの修正漏れなどトラブルの元になってしまう。

特にdbtの経験が浅いジュニアメンバーはconfigに書かれているtagsが分かりやすく目立つので、そこから学習して新しいtagを書いてしまうこともあるが、複数のディレクトリ・ドメインにまたがる長大なデータパイプラインでない限りはディレクトリの指定やグラフオペレーターで十分jobを動かせることが大半だ。

そのため、設定するtagはなるべく最小限に抑えて、それぞれのtagの役割が利用者全員にすぐに伝わるような、統制が取れた状態を保ちたい。

とはいえ、管理台帳を作っても、そのうちメンテナンスされなくなることは目に見えており、都度Pull Requestで指摘するという方法もレビュアーが疲れるので、仕組みで制御したい。

どうやるか

コンセプトは以前書いたdbt project evaluatorの生成物を流用する方法と同じで、dbtのpackageがメタデータを収集する過程で生成するテーブルを使う。今回使うパッケージはelementaryというデータパイプラインの監視を目的としたもので、tagsの情報もテーブル化される。

https://zenn.dev/analytics_eng/articles/7c46c815398b5f

elementaryの本来の監視機能を活用しようとすると、専用CLIなどのインストールも必要であるが、メタデータのテーブル化だけならdbt packageのインストールだけで良いので、dbt cloudユーザーでも導入できる。

https://hub.getdbt.com/elementary-data/elementary/latest/

実装

dbt elementary が取得したデータをテーブル化する

一番多いと思われる、モデルにタグを付けるケースを想定。サンプルとしてチュートリアルのモデルにタグを付けてみる。

my_first_dbt_model.sql

{{ config(
    materialized='table',
    tags = ['tag1', 'tag2'],
    ) }}

with source_data as (

以下略

my_second_dbt_model

{{ config(
    materialized='table',
    tags = ['tag3'],
    ) }}

select *
以下略

次に dbt run --select elementary でelementaryが取得するデータをテーブル化すると、画像のような形でSTRING形式のtagsが取得できる。

tag一覧を取得するviewを作成する

生成されるデータはmodel単位なので、tag単位の一覧を作成する。同時に、それぞれのtagがどういう役割なのかを説明するdescriptionを書いたseedファイルも作っておくと良い。

タグの一覧を作成するsql

WITH
  tag_list AS (
    SELECT
        tag,
        STRING_AGG(alias
        ORDER BY
        unique_id
        LIMIT
        5) AS related_model_sample
    FROM
        {{ ref("dbt_models") }},
        UNNEST(JSON_VALUE_ARRAY(tags)) AS tag
    GROUP BY
        1 
    ),

  output AS (
    SELECT
        *
    FROM
        tag_list
    LEFT JOIN
        {{ ref("seed_tag_description") }}
    USING
        (tag) 
)

SELECT
  *
FROM
  output

出力結果

想定外のtagが追加された場合に失敗するテストを用意する

上に記載したsqlが作成するviewに対して、ymlにテストを記載する。

accepted_values と、seedファイルの description に新しいタグに関する情報を記載していないとテストに失敗するようになっている。


version: 2

models:

  - name: metrics_dbt_metadata__dbt_tag_list
    description: "List of dbt tags"
    columns:
      - name: tag
        description: "dbt tag"
        tests:
          - unique
          - not_null
          - accepted_values:
              values: ['tag1', 'tag2', 'tag3']
      
      - name: description
        description: "description of dbt tag"
        tests:
          - not_null

あとは dbt project evaluator と同様に、CIテストの中に組み込んでおくと、PR作成時に自動でチェックされるようになるので、意味もなくtagが追加されることがなくなり、増やす場合でもその必要性をPR上で議論しやすくなる。

他の任意の設定値が書けるmetaフィールドなども、同様の方法で管理できる。逆に、書いておいて欲しいメタデータがないことを検知して、テストで落とすといったことも工夫すればできそうである。

dbtはスキル要件が低く、ジュニアデータアナリストといったエンジニアスキルが少ない人でも容易に扱うことができる反面、利用者を広げていく場合は統制の取りにくさがどうしても発生してしまうので、こうしたレビュアーに負担を掛けない、仕組みを使った管理を増やしていくと、トラブルを減らせるだろう。

参考資料

dbt elementaryそのものの詳細な解説

https://www.yasuhisay.info/entry/2024/01/31/084039

Discussion