[新機能]dbt Projects on Snowflakeで同一プロジェクトオブジェクトに対してdbtコマンドの並列実行が可能となりました
さがらです。
dbt Projects on Snowflakeの新機能として、Slim CIとdefer to productionが一般提供(GA)されました。このアップデートには複数の機能が含まれていますが、その中の1つに、同一のdbt project objectを並列実行できる機能があります。
これまでは、同一のdbt project objectに対して複数の実行を同時に走らせることができず、並列で実行したいモデル群がある場合はdbt project object自体を複製する構成になりがちでした。今回のアップデートで、1つのdbt project objectをデプロイしたまま、dbt buildなどのコマンドを複数かつ並列に実行できるようになったので、実際に試してみました。
機能概要
今回のGAで一般提供された機能は、大きく分けて以下の4つです。
- Slim CI: 最新の本番実行のdbtアーティファクト(
manifest.jsonなど)をインポートし、変更されたリソースとその下流依存関係のみを検証する - Defer to production: 未構築の上流参照を、既存の本番リレーションに解決する
- 並行実行: 同一の展開済みdbt project objectを、並列で実行できる
- 失敗時の復旧: エラー結果から、最新の失敗実行のアーティファクトを再利用して効率的に復旧する
本記事で扱うのは3番目の並行実行です。
例えば、次のようなプロジェクトを考えます。
models/
├── core/
│ └── core_model.sql
└── marketing/
└── marketing_model.sql
この場合、dbt project objectを複製せず、--selectで実行対象を分けられます。
Coreスライス:
--select path:models/core
Marketingスライス:
--select path:models/marketing
例えば、次のようなスケジュールを構成できます。
Core : 1時間ごと
Marketing : 15分ごと
従来は、実行対象や実行頻度を分けるために、dbt project object自体を複製する構成になりがちでした。今回の機能では、次のようにデプロイ単位と実行単位を分離できます。
デプロイ単位:
1つのdbt project object
実行単位:
--selectで指定するモデルスライス
スケジュール単位:
スライスごとに個別設定
ただし、並列実行時には、モデルが作成するテーブルだけでなく、dbtアーティファクトやログの書き込み先も考慮する必要があります。
Live versionとは
dbt project objectには、現在のプロジェクトファイルを保持する、単一の可変バージョンであるlive versionがあります。
プロジェクトを作成するとlive versionが作成され、その後のデプロイでは同じlive versionの内容が置き換えられます。Snowflake上のパスは、次の形式です。
snow://dbt/<database>.<schema>.<project>/versions/live
今回のプロジェクトでは、次のパスになります。
snow://dbt/ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO/versions/live
デプロイは、live version全体を単一操作で置き換えます。そのため、デプロイ途中の不完全なプロジェクトを実行が参照することはありません。
また、live versionには、プロジェクトのソースファイルだけでなく、writebackによって生成されたtargetやlogsも保存されます。ただし、後続のデプロイではlive version全体が置き換えられるため、既存のtarget・log artifactsもデプロイ内容に含まれる形で更新されます。
今回の並列実行機能では、single mutable live versionを利用するdbt project objectが前提です。2026_06 behavior change bundleを有効化するか、Snowflakeアカウントでsingle live version機能が利用可能である必要があります。
公式ドキュメントはこちらです。
Writebackとは
Writebackは、dbtの実行で生成されたtarget artifactsやログを、dbt project objectのlive versionへ書き戻す仕組みです。
dbt実行
├── target/
│ ├── manifest.json
│ └── run_results.json
└── logs/
└── dbt.log
│
└── live versionへ書き戻し
DEFAULT_WRITEBACKは、後続の実行でwritebackするかどうかのデフォルト値です。個別の実行では、WRITEBACKを指定して、このデフォルト値を上書きできます。
WRITEBACK = FALSE
EXECUTE DBT PROJECT ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO
ARGS = 'build --target prod --select path:models/core'
WRITEBACK = FALSE;
WRITEBACK = FALSEの場合、実行時に生成されたtarget・log artifactsはlive versionへ書き戻されません。
一方で、Snowflakeはwritebackの設定にかかわらず、実行ごとの結果artifactsをresultsディレクトリにquery単位で保存します。そのため、並列実行時にlive versionの共有target・log pathを使う必要がなければ、WRITEBACK = FALSEが推奨されます。
WRITEBACK = TRUE
writebackを有効にしたまま並列実行する場合は、実行ごとに--target-pathと--log-pathを分けます。
Core:
target/core
logs/core
Marketing:
target/marketing
logs/marketing
target・log pathはプロジェクト内部のディレクトリである必要があり、並列実行間で重複してはいけません。親ディレクトリと子ディレクトリの組み合わせも避けます。
避けるべき例:
Execution A: target/
Execution B: target/marketing/
今回の検証では、以下の2パターンを確認します。
パターン1:
WRITEBACK = FALSE
パターン2:
WRITEBACK = TRUE
かつ、実行ごとにtarget/log pathを分離
制限事項
- 2026年9月11日時点では、2026_06 behavior change bundleを有効化する必要があります
--target-path・--log-pathは、プロジェクト内のディレクトリを指す必要があり、並列実行間で重複してはいけません(親子関係になる組み合わせも避ける必要があります)DEFAULT_WRITEBACK = TRUEのまま--target-path・--log-pathを分けずに並列実行すると、共有パスへの書き込みが競合するリスクがありますWRITEBACKは、あくまでアーティファクト・ログの書き込み先の衝突を防ぐ設定です。同じテーブルへの同時書き込みや、同じincremental model/snapshotの同時実行といったデータ側の競合までは解決しません
参考:dbt Projects on Snowflakeの基本操作について
基本的にWorkspace上で検証を進めるため、dbt projectの基本操作については以下のブログも参考にご覧ください。
事前準備
1. Snowflakeオブジェクトの準備
検証用に、以下のオブジェクトを使用します。既存のものがあれば適宜読み替えてください。
| 項目 | 値 |
|---|---|
| ウェアハウス | DBT_CONCURRENT_DEMO_WH |
| データベース | ANALYTICS |
| dbt project object配置スキーマ | ANALYTICS.DBT_PROJECTS |
| dbt実行先スキーマ | ANALYTICS.DBT |
| dbt project object | ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO |
ACCOUNTADMINで、ウェアハウス・データベース・スキーマを作成します。
USE ROLE ACCOUNTADMIN;
CREATE OR REPLACE WAREHOUSE DBT_CONCURRENT_DEMO_WH
WAREHOUSE_SIZE = XSMALL
AUTO_SUSPEND = 60
AUTO_RESUME = TRUE;
CREATE DATABASE IF NOT EXISTS ANALYTICS;
CREATE SCHEMA IF NOT EXISTS ANALYTICS.DBT_PROJECTS;
CREATE SCHEMA IF NOT EXISTS ANALYTICS.DBT;
CREATE ROLE DBT_CONCURRENT_DEV_ROLE;
検証ロールに対して、必要な権限を付与します。
GRANT USAGE ON WAREHOUSE DBT_CONCURRENT_DEMO_WH
TO ROLE DBT_CONCURRENT_DEV_ROLE;
GRANT USAGE, CREATE SCHEMA ON DATABASE ANALYTICS
TO ROLE DBT_CONCURRENT_DEV_ROLE;
GRANT USAGE, CREATE DBT PROJECT ON SCHEMA ANALYTICS.DBT_PROJECTS
TO ROLE DBT_CONCURRENT_DEV_ROLE;
GRANT USAGE, CREATE TABLE ON SCHEMA ANALYTICS.DBT
TO ROLE DBT_CONCURRENT_DEV_ROLE;
GRANT ROLE DBT_CONCURRENT_DEV_ROLE TO ROLE SYSADMIN;
2. Snowsightで検証用dbt projectを作成する
SnowsightでProjects → Workspaces → Add new → dbt Projectを選択し、プロジェクトを作成します。
| 項目 | 値 |
|---|---|
| Project Name | concurrent_demo |
| Role | DBT_CONCURRENT_DEV_ROLE |
| Warehouse | DBT_CONCURRENT_DEMO_WH |
| Database | ANALYTICS |
| Schema | DBT |

最終的に以下の構成にします。(models内のサンプルの.sqlファイルだけ削除するようにしてください。)
concurrent_demo/
├── dbt_project.yml
├── macros/
│ └── get_custom_schema.sql
└── models/
├── core/
│ └── core_model.sql
└── marketing/
└── marketing_model.sql
`dbt_project.yml`は以下の内容にします。
```yaml
name: concurrent_demo
version: 1.0.0
config-version: 2
profile: concurrent_demo
model-paths:
- models
analysis-paths:
- analyses
test-paths:
- tests
seed-paths:
- seeds
macro-paths:
- macros
snapshot-paths:
- snapshots
models:
concurrent_demo:
core:
+schema: DBT
+materialized: table
marketing:
+schema: DBT
+materialized: table
profiles.ymlは以下の内容にします。target: prodがポイントです。
concurrent_demo:
target: prod
outputs:
prod:
type: snowflake
role: DBT_CONCURRENT_DEV_ROLE
warehouse: DBT_CONCURRENT_DEMO_WH
database: ANALYTICS
schema: DBT
threads: 8
macros/get_custom_schema.sqlは、以下の通り作成します。(参考ブログ)
{% macro generate_schema_name(custom_schema_name, node) %}
{% set default_schema = target.schema %}
{# targetが「prod」かつ対象のオブジェクトが「seed」かつcustom_schemaの定義ありの場合、seed向けの「custom_schema」に #}
{% if target.name == 'prod' and node.resource_type == 'seed' and custom_schema_name is not none %}
{{ custom_schema_name | trim }}
{# targetが「prod」でないかつ対象のオブジェクトが「seed」かつcustom_schemaの定義ありの場合、「default_schema」を先頭に付けたseed向けの「custom_schema」に #}
{% elif target.name != 'prod' and node.resource_type == 'seed' and custom_schema_name is not none %}
{{ default_schema }}_{{ custom_schema_name | trim }}
{# targetが「prod」かつcustom_schemaの定義ありの場合、「custom_schema」に #}
{% elif target.name == 'prod' and custom_schema_name is not none %}
{{ custom_schema_name | trim }}
{# custom_schemaの定義なしの場合、「default_schema」に #}
{% elif custom_schema_name is none %}
{{ default_schema }}
{# 上述の条件に合致しない(targetが「prod」でないがcustom_schemaの定義あり、など)の場合、「default_schema」に #}
{% else %}
{{ default_schema }}
{% endif %}
{% endmacro %}
models/core/core_model.sqlとmodels/marketing/marketing_model.sqlは、それぞれ動作確認用に以下の内容にします。
-- models/core/core_model.sql
select
1 as model_id,
'core' as slice_name,
current_timestamp() as executed_at
-- models/marketing/marketing_model.sql
select
1 as model_id,
'marketing' as slice_name,
current_timestamp() as executed_at
core・marketingは別々のテーブル(ANALYTICS.DBT.CORE_MODEL・ANALYTICS.DBT.MARKETING_MODEL)として作成されるため、並列実行してもデータ側の書き込み先が重複しない構成です。
この状態で、一度targetprodに対してdbt runを実行して、問題なく実行できてテーブルが作成されれば、準備完了です。

試してみた
1. Workspaceからdbt projectをデプロイする
WorkspaceのConnectを選択し、Deploy dbt projectをクリックします。
デプロイ画面では、以下を指定します。(今回は検証のためtargetをprodだけ設定していますが、本番運用時はdevなどの開発用targetを作成するようにしてください。)
| 項目 | 値 |
|---|---|
| Select location | ANALYTICS.DBT_PROJECTS |
| Select or Create dbt project | Create dbt project |
| Enter name | CONCURRENT_DEMO |
| Default target | prod |
| Default Writeback | 無効化 |

デプロイが成功すれば下図のように表示されます。

デプロイ結果は、SQLで確認します。
DESCRIBE DBT PROJECT ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO;
default_versionがLIVEになっていれば、single mutable live versionとしてデプロイされています。

2. WRITEBACK = FALSEで並列実行する
ここから本題の並列実行です。まずはwritebackを無効化したパターンから試します。
Worksheet Aで、Coreスライスを実行します。
EXECUTE DBT PROJECT ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO
ARGS = 'build --target prod --select path:models/core'
WRITEBACK = FALSE;
別のセッション(Worksheet B)から、できるだけ同じタイミングでMarketingスライスを実行します。
EXECUTE DBT PROJECT ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO
ARGS = 'build --target prod --select path:models/marketing'
WRITEBACK = FALSE;
SnowsightのTransformations → dbt Projects → CONCURRENT_DEMO → Run Historyを開くと、2つのdbtコマンドが同時に実行されていることが確認できました。

3. WRITEBACK = TRUEで並列実行する(target-path・log-pathを分離)
続いて、writebackを有効にしたまま、実行ごとに--target-path・--log-pathを分けて並列実行します。
Worksheet Aで、Coreスライスを実行します。
EXECUTE DBT PROJECT ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO
ARGS = 'build --target prod --select path:models/core --target-path target/core --log-path logs/core --log-level-file info'
WRITEBACK = TRUE;
Worksheet Bで、できるだけ同じタイミングでMarketingスライスを実行します。
EXECUTE DBT PROJECT ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO
ARGS = 'build --target prod --select path:models/marketing --target-path target/marketing --log-path logs/marketing --log-level-file info'
WRITEBACK = TRUE;
--target-path・--log-pathは、以下のように実行ごとに完全に分離しています。親子関係になる組み合わせ(例: target/とtarget/marketing/を同時に使う)は避けてください。
Core:
target/core
logs/core
Marketing:
target/marketing
logs/marketing
Run Historyは以下のように表示されればOKです。

4. live versionに書き戻されたtarget・logsを確認する
WRITEBACK = TRUEで書き戻されたアーティファクトが、実際に分離したパスへ格納されているかを、COPY FILESで確認します。
まず、確認用の内部ステージを作成します。
CREATE OR REPLACE STAGE ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO_CHECK_STAGE
ENCRYPTION = (TYPE = 'SNOWFLAKE_SSE');
live versionのtarget・logsを、スライスごとにステージへコピーします。
COPY FILES INTO @ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO_CHECK_STAGE/core/
FROM 'snow://dbt/ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO/versions/live/target/core/';
COPY FILES INTO @ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO_CHECK_STAGE/core/
FROM 'snow://dbt/ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO/versions/live/logs/core/';
COPY FILES INTO @ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO_CHECK_STAGE/marketing/
FROM 'snow://dbt/ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO/versions/live/target/marketing/';
COPY FILES INTO @ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO_CHECK_STAGE/marketing/
FROM 'snow://dbt/ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO/versions/live/logs/marketing/';
ステージ内のファイルを一覧表示します。
LIST @ANALYTICS.DBT_PROJECTS.CONCURRENT_DEMO_CHECK_STAGE;
下図のように、指定したパスに分けて各種アーティファクトのファイルが出力されていればOKです。--target-path・--log-pathを分けたことで、writeback有効時でも並列実行のアーティファクトが衝突せず、live version配下にスライスごとに書き戻されていることが確認できました。

なお参考までに、writebackとは別に、実行ごとのアーティファクトはquery単位でも保存されており、Run Historyから対象の実行を開き、Query Detailsタブのdbt OutputにあるDownload Build ArtifactsからもZIPでダウンロードできます。

最後に
dbt Projects on SnowflakeのSlim CI/defer to production GAに含まれる「並行実行」機能を使い、同一のdbt project objectに対してcore・marketingという独立したモデルのbuildを並列実行できるか試してみました。
WRITEBACK = FALSEにするか、WRITEBACK = TRUEのまま--target-path・--log-pathを実行ごとに分離するかのどちらかを選べば、共有のliveパスへの書き込み衝突を避けつつ並列実行できることが確認できました。
これまで、並列で実行したいモデル群がある場合はdbt project object自体を複製していた構成では、モデルの追加・変更のたびに複数のデプロイ先を意識する必要がありましたが、今回の機能でデプロイ単位と実行単位を分離し、1つのdbt project objectを複製せずに並列実行できるようになったのは、運用上大きな強みだと感じました。
ぜひ、ご活用ください!







