#️⃣

SQLMeshが提供するVirtual Data Environments(データ環境の仮想化)を体験してみた

2023/05/07に公開

はじめに

開発・運用のコストを最小限にしつつDev・Staging・Prodをシームレスに切り替える設計思想としてVirtual Data Environments(データ環境の仮想化)という概念があるらしい。

SQLMeshで実装されており、簡単に試すことが出来るので、どんな感じかさわってみた。

https://sqlmesh.readthedocs.io/en/stable/

Virtual Data Environments(データ環境の仮想化)

TL;DR

Physical layerにデータのスナップショットを作成して、それをVirtual layerから参照することで独立性を担保しつつ、素早く環境を切り替えるというコンセプト。
(Virtual layerはViewのみで構成されている)


【引用元: Iaroslav Zeigerman, tobiko data Blog, 2023/4/18, Figure 6: Virtual Data Environments end-to-end】

  • Virtual layerは必ずViewになっているため、開発時にテーブルを生成するコストや準備時間が少なくスケールしやすい(個人環境も作りやすくスケールしやすい)
  • Virtual layerでスナップショットの参照先を変えるだけなので、Dev・Prod間でシームレスに切り替えが出来る
  • スナップショットでデータを管理しているので、何か問題があってもロールバックが簡単に出来る
  • Prod・Devで使うスナップショットを共通化しているので、Prod特有のエラーケースを防げる

Current Landscape(現状)

従来Staging環境は、別のデータセット・DWHにデータを準備しているが、これには以下のような問題があると主張しています。

  1. データを移植するのは大変なので、一般的に以下のようなアプローチが取られる
    • プロダクションのフルコピーを取得する(人数が増えると料金が増大する)
    • サンプリングしてプロダクションのコピーを作成する(正確性に欠ける)
    • 変更されない静的なデータセットを使う(正確性に欠ける)
    • ランダムバリューで生成されたデータセットを使う(本番のデータとズレがある)
  2. プロダクション環境でデータが再作成されるため、開発環境で観測したデータと違うものが生成される可能性がある
  3. データパイプラインの変更は即座に反映されないため、一時的に一貫性が無い状態になる


【引用元: Iaroslav Zeigerman, tobiko data Blog, 2023/4/18, Figure 1: Isolated but rigid Data Warehouse environments】

Stateful Development Environments(stateを使った開発環境)

dbtのstate + deferやFivetranのsmart runsのように、変更されていない部分を再利用するアプローチもありますが、この方法にも以下のような課題があると主張しています。

  • プロダクション環境でデータが再作成されるため、開発環境で観測したデータと違うものが生成される可能性がある
  • 本番にデプロイされると、変更前のstateが削除されるので変更前のデータセットにrevert出来ない

【引用元: Iaroslav Zeigerman, tobiko data Blog, 2023/4/18, Figure 2: Detecting changes by comparing the raw contents of source files】

Virtual Data Environments(SQLMeshが提供するデータの仮想化)

Virtual Data EnvironmentsではPhysical layerとVirtual layerを定義しています。
Physical layerではモデルはユニークなfingerprintと紐づけられたスナップショットとして管理されます。
Virtual layerのテーブルは全てViewになっており、Physical layerのスナップショットを参照しています。

このスナップショット(バージョン)を切り替えることで、以下のようなメリットが得られると思っています。

  • モデルに問題があった場合にも昔のモデルが使っていたデータにrevertすることが出来る
  • データの再作成を出来るだけ少なくすることで、環境を作るコストを最小限にする
    • 結果的に、個人環境も作りやすくなり、スケールしやすい
  • Dev・Prodで使うデータを共通化することで、Prod特有のエラーを防ぐ
  • Dev・Prodでデータを素早く切り替えることが出来る


【引用元: Iaroslav Zeigerman, tobiko data Blog, 2023/4/18, Figure 3: Environments managed through the virtual layer】


【引用元: Iaroslav Zeigerman, tobiko data Blog, 2023/4/18, Figure 6: Virtual Data Environments end-to-end】

SQLMeshを使ってデプロイした場合にBigQueryでどのように管理されているか見てみる

筆者はBigQueryしか分からないので、BigQueryでやっています。Snowflakeなど他のエンジンもサポートしているので一番得意なエンジンで試してみてください。

https://sqlmesh.readthedocs.io/en/stable/prerequisites/

環境構築・必要なライブラリのインストール

python -m venv .env
poetry init
poetry add "sqlmesh"
poetry add "sqlmesh[bigquery]"
source .env/bin/activate

実際に動かしてみる

SQLMeshプロジェクトの作成

mkdir sqlmesh-example
cd sqlmesh-example
sqlmesh init

使用するエンジンをBigQueryに変更する

sqlmesh-example/config.yaml
connections:
    bigquery_connection:
        type: bigquery
        method: oauth
        project: {your-project}
        location: {your-location}

default_connection: bigquery_connection
test_connection: bigquery_connection

本番にモデルをデプロイする

planを打つと、BigQueryにモデルに異常が無いかチェックします。
plan後にApply - Backfill Tables [y/n]:のように、applyするか聞かれるので、applyするとデプロイされます。
Backfillと言っているのは、メタデータから時系列上で抜けているデータを埋め合わせるのでBackfillと言っています。

sqlmesh plan
planで失敗したら

BigQueryはseedモデルが上手くデプロイ出来なかったので、seedモデルを事前に書き換える必要がありました

models/seed_model.sql
MODEL (
    name sqlmesh_example.seed_model,
    kind FULL,
    columns (
        id INTEGER,
        item_id INTEGER,
        ds DATE
    )
);
SELECT 
    id,
    item_id,
    ds
FROM UNNEST([
    STRUCT(1 AS id, 2 AS item_id, CAST('2020-01-01' AS DATE) AS ds),
    STRUCT(2 AS id, 1 AS item_id, CAST('2020-01-01' AS DATE) AS ds),
    STRUCT(3 AS id, 3 AS item_id, CAST('2020-01-03' AS DATE) AS ds),
    STRUCT(4 AS id, 1 AS item_id, CAST('2020-01-04' AS DATE) AS ds),
    STRUCT(5 AS id, 1 AS item_id, CAST('2020-01-05' AS DATE) AS ds),
    STRUCT(6 AS id, 1 AS item_id, CAST('2020-01-06' AS DATE) AS ds),
    STRUCT(7 AS id, 1 AS item_id, CAST('2020-01-07' AS DATE) AS ds),
])
;

モデル内で定義したデータセットはVirtual layerとして扱われるので、全てViewになっていることが確認できます。

ViewのDDLを見ると、sqlmeshという名前のデータセットにあるスナップショットを参照していることが確認できます。
sqlmeshという名前のデータセットをPhysical layerとして管理しているようです。
スナップショットのテーブル名はsqlmesh.{データセット名}__{View名}__{version}で管理されているようです。

SELECT * FROM sqlmesh.sqlmesh_example__seed_model__3707578938

sqlmeshという名前のデータセット(Physical layer)には以下のテーブルが保存されていました。

  • sqlmesh._environments
    • Physical layerとVirtual layerを紐付けるメタデータを管理している
  • sqlmesh._snapshots
    • スナップショットに関するメタデータを保存している
  • sqlmesh._versions
    • sqlmeshの管理用DB(_environmentsとか_snapshots)のメタデータを保存している
  • スナップショット

個人的な感想ですが、BigQueryエンジンを使うとBigQueryにSQLMeshが管理しているモデルのメタデータを作りますが、これは微妙な気がしています。
普通にMySQLとか外部のOLTPで管理してくれると、メタデータの更新も早くて良さそうだと感じました。

開発用の環境を作る

以下のコマンドでVirtual layerを複製出来ます。

# devはサフィックスの指定なので、自由に変えられる。
# `sqlmesh plan {user_name}_dev` とかで個人環境を作るのが良さそうです。
sqlmesh plan dev

seedモデルのviewを見ると本番と同じスナップショットを参照していることが分かる。

SELECT * FROM sqlmesh.sqlmesh_example__seed_model__3707578938

開発用の環境でモデルを変更してみる

models/seed_model.sqlに新しくカラム(new_column)を追加する。

diff -u models/seed_model_old.sql models/seed_model_new.sql
--- models/seed_model_old.sql     2023-05-07 16:34:43
+++ models/seed_model_new.sql       2023-05-07 16:31:28
@@ -4,12 +4,14 @@
     columns (
         id INTEGER,
         item_id INTEGER,
+        new_column STRING,
         ds DATE
     )
 );
 SELECT 
     id,
     item_id,
+    'z' AS new_column,
     ds
 FROM UNNEST([
     STRUCT(1 AS id, 2 AS item_id, CAST('2020-01-01' AS DATE) AS ds),
\ No newline at end of file

新しくカラムを追加した状態で、planを実行する

sqlmesh plan dev
余談: 変更から自動的に依存関係のあるモデルをリストアップしてくれる機能も使いやすい

dev planを実行してから、sqlmesh_example__dev.seed_modelを見に行くとVirtual layerのViewの参照先が新しく作成されたスナップショットに切り替わっているのを確認できる。

SELECT * FROM sqlmesh.sqlmesh_example__seed_model__1447806862

本番環境にデプロイする

以下のコマンドで本番にデプロイすると、本番のseedモデルが参照しているスナップショットのバージョンも切り替わっていることが確認できる。(開発と同じスナップショット)

sqlmesh plan

感想

SQLMeshを使うと、データ環境の仮想化が簡単に出来ます。
データ環境を仮想化することで開発スピードを上げつつ、障害時の対応を簡単にすることが出来そうです。

SQLMeshはdbtとの相互運用性を担保していくようなので、ここに期待しています。
現状だとBigQueryへのクエリ周りで、seedが上手く使えなかったり、モデルに外部変数が使えなかったりとTransformツールとしては微妙に感じました。(開発が2022年の12月なので、時間が経てばもう少し良くなるかも)
モデルはdbtで管理、Opsの部分はSQLMeshで管理していけると生産性が上がりそうです。

興味があれば

SQLMeshには、AirflowのDAGの自動生成や、変更サマリ情報の表示など他の面白い機能も沢山あるので、興味があれば触ってみてください。
https://sqlmesh.readthedocs.io/en/stable/

またSQLMeshの開発元のブログも面白かったので、ぜひご覧ください。
https://tobikodata.com/blog.html

Discussion