😽

RecceとGithub Actionsを統合したCIパイプラインの構築

2024/06/28に公開

Recceとは

Recce(レーキー)とは、dbtプロジェクト専用のデータ変更検証ツールです。
Recceはデータモデリング検証として環境間の差分を実行するための便利なプラットフォームを提供します。
プルリクエスト (PR) コメントでこれらの検証を使用して、モデリング変更の正しさを証明し、PRレビューのQAプロセスを高速化します。

環境構築

基本的に公式のチュートリアルに沿って、実施しました。

Recce環境構築

チュートリアル用のdbtプロジェクトをクローンします

git clone https://github.com/dbt-labs/jaffle_shop_duckdb.git
python3 -m venv venv

Python仮想環境の作成と必要なパッケージのインストール

source venv/bin/activate
sudo apt install python3-pip
cd jaffle_shop_duckdb/
pip install -r requirements.txt
pip install recce

packages.ymlの設定

packages.yml
packages:
  - package: dbt-labs/audit_helper
    version: 0.11.0
  - package: data-mie/dbt_profiler
    version: 0.8.1

profiles.ymlの設定

Recceは2つの環境が必要なため、Dev(開発)とProd(本番)を用意します。

profiles.yml
jaffle_shop:

  target: dev
  outputs:
    dev:
      type: duckdb
      path: 'jaffle_shop.duckdb'
      threads: 24
    prod:
      type: duckdb
      path: 'jaffle_shop.duckdb'
      schema: prod
      threads: 24

比較対象のベースとするprod(本番環境)に対して、モデルのリリースとdocsの作成

dbt docs generate実行時は、--target-pathに./target-baseを指定します。

dbt deps
dbt seed --target prod
dbt run --target prod
dbt docs generate --target prod --target-path ./target-base

モデルの修正を行った風で、計算ロジックを修正

models/staging/stg_payments.sql
with source as (

    {#-
    Normally we would select from the table here, but we are using seeds to load
    our data in this project
    #}
    select * from {{ ref('raw_payments') }}

),

renamed as (

    select
        id as payment_id,
        order_id,
        payment_method,

        -- `amount` is currently stored in cents, so we convert it to dollars
        -- amount / 100 as amount
	amount

    from source

)

select * from renamed

Dev環境へモデルのリリースとdbt docsの作成

dbt seed
dbt run
dbt docs generate

Recceサーバの起動

recce server

Recceサーバの機能

Recceサーバには大きく3つの機能があり、どのような機能なのか纏めてみました。
詳細が気になる方は、公式ドキュメントのリンクを貼っておくので、ご参照ください。

Lineage

https://datarecce.io/docs/features/lineage/
リネージから次のことを確認することが出来ます。※一部のみ紹介

  • モデル変更に伴う影響範囲
  • モデルにどのような変更が起きたかの概要(added/removed/modified/row count changed/schema changedなど)
  • SQLコード差分
  • 基本統計(カウント、個別カウント、最小値、最大値、平均)の比較
  • ヒストグラム差分/Top-K差分

Query

https://datarecce.io/docs/features/query/
任意のクエリを実行したり、2 つの環境間でクエリ結果を比較したりするためのアドホッククエリインターフェイスが用意されています。
dbtユーザーの場合は、プロジェクトにインストールされている任意のdbtマクロを使用できます。

Checklist

https://datarecce.io/docs/features/checklist/
データモデルを検証する際に、忘れがちになる検証観点などをチェックリストとして、保存することができる。

コマンドラインインタフェース

RecceにはCLIの機能があり、この機能とCIワークフローを組み合わせることが適切とされています。

コマンド 概要
recce run Web画面でのファイル比較やチェックリストの実行結果をJSON形式で保存するコマンド
recce summary <stateファイル> recce runコマンド結果を纏めて、人間レビューしやすい形式に出力してくれるコマンド

recce run

recce runを実行することで、検証対象のdbtのアーティファクトのタイムスタンプ情報や、プリセットチェックリストの内容を出力し、recce_state.jsonとしてファイル保存してくれます。

$ recce run
──────────────────────────────────────────── DBT Artifacts ─────────────────────────────────────────────
Base:
    Manifest: 2024-06-23 04:49:26.321108+00:00
    Catalog:  2024-06-23 04:49:29.218109+00:00
Current:
    Manifest: 2024-06-23 05:18:46.690883+00:00
    Catalog:  2024-06-23 04:50:34.942057+00:00
─────────────────────────────────────────── Default queries ────────────────────────────────────────────
Querying row count diff for the modified table models. [0 node(s)]
Skipped
──────────────────────────────────────────── Preset checks ─────────────────────────────────────────────
                                  Recce Preset Checks                                   
 ──────────────────────────────────────────────────────────────────────────────────────
  Status      Name                         Type         Execution Time   Failed Reason
 ──────────────────────────────────────────────────────────────────────────────────────
  [Success]   Query diff of customers      Query Diff   0.30 seconds     N/A
  [Success]   Value diff of stg_payments   Value Diff   0.32 seconds     N/A
 ──────────────────────────────────────────────────────────────────────────────────────
───────────────────────────────────────────── Export state ─────────────────────────────────────────────
Repository ******/dbt-recce not found. Please provide '$GITHUB_TOKEN' environment variable.
The state file is stored at 'recce_state.json'

recce summary

recce summaryコマンドを実行すると、recce_state.jsonを基に様理を出力してくれます。

$ recce summary recce_state.json
# Recce Summary

## Lineage Graph
` ` `mermaid
graph LR
model.jaffle_shop.stg_payments["stg_payments

[What's Changed]
Code, Schema, Value Diff"]
style model.jaffle_shop.stg_payments stroke:#ffa502
model.jaffle_shop.stg_payments---->model.jaffle_shop.customers
model.jaffle_shop.stg_payments---->model.jaffle_shop.orders
model.jaffle_shop.customers["customers"]
model.jaffle_shop.orders["orders"]

` ` `

## Checks Summary
|Checks Run|Data Mismatch Detected|
|----------|----------------------|
|     2    |           2          |


### Checks of Data Mismatch Detected
|           Name           |   Type   |Related Models|
|--------------------------|----------|--------------|
|  Query diff of customers |Query Diff|      N/A     |
|Value diff of stg_payments|Value Diff| stg_payments |

この結果をGithubのプルリクエストコメントに記載してあげると、次のように見えます。

一目で、どこのモデルが変更され、変更点もわかります。(What’s Changed Code, Schema, Value Diff)
また、プリセットで登録しておいたチェック一覧の結果も分かるようになっています。

プリセットチェック

Recceサーバー上で毎回毎回チェックリストをポチポチして、作るのが面倒な場合は、プリセットチェックがおすすめです。
その名の通り、事前にチェックリストを作成しておくことが出来ます。
作成方法は、recce.ymlと呼ばれる構成ファイルを作成しておくだけです。

recce.yml
# recce.yml
checks:
  - name: Query diff of customers
    description: |
      This is the demo preset check.

      Please run the query and paste the screenshot to the PR comment.
    type: query_diff
    params:
      sql_template: select * from {{ ref("customers") }}
    view_options:
      primary_keys:
        - customer_id

構成ファイルに記載する項目は以下の5つがあります。

Field Description Type
name チェックリストのタイトル名 string
description チェックリストの説明 string
type チェックリストのタイプ string
params チェックリストで実行するパラメータ object
view_options 実行結果の表示オプション object

正直何をどう設定すれば良いか分からないと思いますが、安心してください。
Recceサーバ上でプリセットのテンプレートを作成できます。
試しにQueryのDiffをチェックする構成ファイルのテンプレートを作成してみます。

まず、手動でチェックリストを用意します。
QueryのDiff結果を取得し、画面中央右端の「Changed Only」の右横にある「+」をクリックし、チェックリストに追加します。

チェックリストに追加すると、チェックリスト画面に遷移し、画面右上にある縦「・・・」をクリックし、Get Preset Check Templateをクリックします。

すると、以下の通りrecce.ymlに設定する構成ファイルのテンプレートが表示されます。
このまま使うもよし、アレンジを加えても良しです。

Stateファイルを使ったPRレビュー

開発者がローカルで開発し、Recceサーバ上で確認した内容や結果をレビュアーに見てもらうこともできます。
レビュアー側が次のコマンドを実行して、Recceサーバをレビューモードとしてレビューすることが出来ます。
recce server --review <stateファイル>
これにより、レビュー担当者が結果を監査してマージを続行できるかどうかを簡単に判断できるようになります。
さらに、stateファイルには、比較の実行結果などが保持されているので、レビュアーが開発環境にアクセスすることなく、レビューモードのRecceサーバ上でクエリを実行することが出来ます。

stateファイルは、recce runコマンド実行で生成されたファイルもしくは、開発者がRecceサーバ上で「Export」からstateファイルを取得してレビュアーに共有することが出来ます。

レビューモードでRecceサーバを起動すると、画面左上にREVIEW MODEの文字が入ります。

実践的な開発サイクル

次のようなシナリオを考えた時、Recceが支援できることを紹介します。

  1. コードを書き、変更を検証し、コードをコミットする
  2. コミットをリモートにプッシュする
  3. 変更の影響を確認する

リネージの確認

Lineage diffによって影響範囲の初期評価を実施できます。
これにより、潜在的な意図しない影響を特定できる場合があります。

モデルのメタデータの検証

系統差分を使用すると、変更されたモデルから開始して影響を確認できます。
安価な方法は、影響を受けるモデルのメタデータの影響範囲を調べることです。
各モデルのスキーマに変更が検出されているかどうかを確認します。場合によっては、 IntegerからTextへの変更、またはDecimalからNumericへの変更が、下流のモデルに微妙な影響を与えることがあります。
さらには、スキーマ変更のあるモデルに追加された列のみがあるかどうかも注目に値します。
これは、下流のプロセスに大きな影響を与えない可能性があるためです。
ただし、列が削除された場合は、それが期待される結果であることを確認するために特別な注意を払うことが重要です。
次に、影響を受けるモデルの行数の差分を調べることができます。
通常、行数はウェアハウスのメタデータに保存されるため、行数の情報をそれほどコストをかけずに取得できます。
これにより、行数が同じかどうか、または大幅な変更があるかどうかをすばやく判断できます。
よくある問題は、誤った結合によって予期しないデータ量や誤った結果が生じることです。行数の差分は、同様のエラーをすばやく特定する方法を提供します。
各ノードの概要を確認すると、スキーマと行数の変更をすばやく確認できます。
系統差分グラフを使用して、スキーマと行数の基本的なチェックを実行することで、開発プロセス中に行われた変更について基本的なレベルの信頼性を得ることができます。

列の差分検証

Recceは、データ レベルの変更を比較するための 4つの強力なdiffツールを提供します。

No diffツール名 説明
1 Value Diff 値の差分を使用して、各列の一致率を観察できます。
2 Profile Diff プロファイル差分を使用すると、カウント、個別カウント、最小、最大、平均など、各列の基本的な統計値を比較できます。
3 Histogram Diff ヒストグラム差分を使用して、数値列の分布の変化を調べることができます。
4 Top-K Diff Top-K 差分を使用して、カテゴリ列の分布の変化を分析できます。

アドホッククエリによる検証

最も柔軟な方法を選択する場合は、クエリ diffが最適です。
個々のレコードを比較したり、where、group by、order byなどの複雑な操作を実行したりできます。
また、結合を使用して複数のモデルをクエリすることもできます。

アドホッククエリはdbtマクロの使用もサポートしており、検証の柔軟性を最高レベルに高めます。
ただし、欠点はクエリを自分で記述する必要があることです。

チェックリストによる開発

チェックリストを使ってデータが要件を満たす条件となっているかを漏れなく確認することもできます。

継続的インテグレーション(CI)

Recce CLIとGithubやGitlabを連携させることで、継続的インテグレーションを実現できます。
実現方法については、公式ドキュメントにも記載があるので、気になる方はご参照ください。
https://datarecce.io/docs/guides/scenario-ci/

Recce CI と GitHub Action の統合

公式ドキュメント通りのRecce CLIとGHAを統合した継続的インテグレーションを試してみます。
イメージとしては新しいプルリクエストが作成されたときに 2つの環境間でデータモデルを比較検証を行います。
以下の画像は基本的なアーキテクチャを示しています。

Base EnvのDBT ArtifactとPR EnvのDBT Artifactの結果を比較検証し、その結果をstate fileとして、レビュアーにローカル環境にてレビューしてもらうストーリー。
より細かな、開発フローのイメージしてみる。

  1. 公式のGHAの動作から、mainブランチへpushし、指定のGithubStorageのディレクトリにDBT Artifactが存在していることが前提
  2. 開発用ブランチを切り、データモデルの修正を行う
  3. (お好みで)開発者のローカル環境で一度Recce Serverを使って、比較検証を行い問題ないことを確認する
  4. プルリクエストをあげる(GHAがトリガーされ、Github Storageにrecce-state-fileがアップロードされ、recce summaryの結果がPRコメントに追記される)
  5. レビュアーがローカルでレビューモードで結果を確認する
  6. 問題なければマージする
  7. マージ処理がGHAのトリガーとなり、最新のPRのGithub StorageにDBT Artifactがアップロードされる
  8. 6でアップロードしたArtifactは次のデータモデル開発時の比較対象基となる

実際に動かしてみた

基本的に、公式ドキュメントに記載のGithub Actionsの設定ファイルを使って試してます。

プルリクエストを起票すると、GHAが実行され、以下スクショのように処理が流れます。
詳細は公式ドキュメントに記載のコンフィグをご確認ください。

マージ処理を行うとGHAが実行され、最新のDBT ArtifactがGithub Storageにアップロードされます。

プルリクエストのrecce summaryコメント例

おまけ

業務でSnowflakeを使っているので、少しだけSnowflakeの挙動もみてみると・・・
比較対象のデータモデルがSnowflakeのアカウントレベルで異なる場合は比較検証ができません。
Recceは使えないと思います。

実際にSnowflakeのアカウントを2つ用意し、動作検証を行いました。
例えば、アカウントAとBがあり、それぞれの環境で異なるDB名(RPOD_DBとDEV_DB)を作成したところRecceサーバ上のクエリ実行すると、PROD_DBに連なるテーブルは存在しない!と怒られます。
そんな筈はなく、実際のアカウントAの環境を確認しても存在していました。
次に実際にRecceサーバが実行したクエリをQuery Historyで確認してみることにしました。
すると、アカウントAにはその実行クエリは存在せず、アカウントB側に存在していました。
接続対象となる環境は一つだけ??

そう言えば、Recceのドキュメントにも別々のスキーマを2つ用意すると記載がりましたので、同一環境上のデータモデルに対してのみサポートしているということなのでしょうか?
現時点では、異なる環境間でのRecceの動作はできていないです。
もしできる方法がありましたら、ぜひコメントください。

Discussion