[新機能]Snowflake の Pipeline Builder で SQL + Python パイプラインを GUI で組んで Deploy してみた

[新機能]Snowflake の Pipeline Builder で SQL + Python パイプラインを GUI で組んで Deploy してみた

Snowflakeの新機能Pipeline Builderを使ってデータパイプラインをGUIで構築してみました。SQLファイルやPythonを組み合わせたtask graphの作成からDEV/PROD環境への展開まで、実際の運用シーンを想定した検証結果を記載します。
2026.10.27

かわばたです。

2026年10月22日に Pipeline Builder が Public Preview になりました。Snowsight の Workspaces で task graph を GUI で組み立てる機能です。これまで Notebook や SQL を順番に動かすには CREATE TASK ... AFTER ... を手で書き、変更のたびに DDL の順序や差分も自分で管理していました。

本記事では、dbt のサンプルで知られる jaffle shop のデータを使います。公開 S3 からのロード、SQL による staging / marts の作成、Python による件数チェックと分岐を扱います。これらを Pipeline Builder のキャンバスで 1 つの task graph に組みます。そのうえで DCM project として Deploy し、実行と監視、DEV / PROD の 2 ターゲットへの展開までを確認します。

https://docs.snowflake.com/en/release-notes/2026/other/2026-10-22-pipeline-builder-preview

https://docs.snowflake.com/en/user-guide/pipeline-builder/pipeline-builder-overview

Pipeline Builder の概要

Pipeline Builder は Workspaces の中で動くビジュアルデザイナーです。Notebook、Python ファイル、ML Job、SQL をキャンバスにカードとして置き、カード同士をつなぐと実行順が決まります。裏側では次の 4 種類のオブジェクトを Pipeline Builder が作って管理します。

オブジェクト 役割
Task キャンバスのカード 1 枚が Task 1 つ。Notebook / Python / ML Job / SQL / .sql ファイルを実行する
Code Bundle .py や .ipynb をキャンバスに置くと、プロジェクトのコードを束ねた Code Bundle を自動で作る。ファイルを自分でデプロイしなくてよい
Task graph 2 つ以上の Task をつなぐと作られる。スケジュール、実行ポリシー、Task の既定値をまとめて持つ
DCM project パイプライン 1 つが DCM project 1 つ。Deploy 前に変更内容(作成 / 変更 / 削除)を一覧で確認でき、同じ定義を別ターゲットにも展開できる

キャンバスに置ける Task の種類は 4 つです。

Task の種類 実行するもの 補足
Notebook / Python Code Bundle 内の .ipynb または .py Notebook は compute pool 必須。Python は warehouse でも動く
ML Job Python ファイルを ML Job として実行 bundle type が ML の Code Bundle を使う
SQL Task に直接書いた SQL ストアドプロシージャの呼び出しなどにも使う
SQL file .sql ファイル Workspace 内のファイル、ステージ上のファイル、ステージ URI から選べる

CoCo による下書き生成(Create ダイアログの Generate with Cortex)と /pipeline-builder スキルは「coming soon」です。本記事の時点では使えません。

前提条件

  • Workspaces にアクセスできること。パイプラインは所有ロールで作った共有 Workspace に置く(理由は「試してみた」の冒頭)
  • パイプラインのオブジェクトを置く DB とスキーマ。既存でも、同じ DCM project で定義してもよい
  • SQL Task 用の warehouse。Notebook や Container Runtime の Python には compute pool も必要
  • 実行するファイルはプロジェクトフォルダの中に置く(sources/ と out/ の中は不可)
  • Deploy するロールに必要な権限は次のとおり
権限 対象 用途
CREATE DCM PROJECT スキーマ 初回 Deploy で DCM project オブジェクトを作る
CREATE TASK スキーマ Task と task graph の作成
CREATE CODE BUNDLE スキーマ Notebook / Python Task がある場合
EXECUTE TASK アカウント Task と task graph の実行
EXECUTE MANAGED TASK アカウント serverless の Task を使う場合のみ
USAGE DB、スキーマ、warehouse、compute pool オブジェクトの置き場所と compute
USAGE notification integration 通知を設定する場合のみ
OWNERSHIP Task graph 内の Task はすべて graph と同じロールが所有する
CREATE WORKSPACE スキーマ 共有 Workspace を作る場合。Task の所有ロールで作れば、ファイルを読む追加権限は不要(ドキュメントの表には無い)

検証するパイプライン

データは dbt Labs が公開している jaffle shop の 6 年分(s3://dbt-tutorial-public/long_term_dataset/)です。次の 6 つの Task を 1 つの task graph にします。

LOAD_RAW (SQL file) → STAGING (SQL file) → MARTS (SQL file) → CHECK_MARTS (Python)
                                                                  ├─ 成功時 → PUBLISH_SUMMARY (SQL)
                                                                  └─ 失敗時 → LOG_FAILURE (SQL)
Task 種類 内容
LOAD_RAW SQL file 外部ステージから 6 つの raw テーブルへ COPY INTO
STAGING SQL file dbt の stg_* モデルに相当する 6 つの view を作る
MARTS SQL file orders / order_items / customers など 6 つのテーブルを作る
CHECK_MARTS Python orders の件数をチェックし、しきい値未満なら例外で失敗する
PUBLISH_SUMMARY SQL CHECK_MARTS が成功したときだけ日次サマリーを作る
LOG_FAILURE SQL CHECK_MARTS が失敗したときだけログテーブルに記録する

データ量は orders が約 209 万行、order_items が約 302 万行です(2016年9月〜2022年8月)。環境は PB_DEMO.DEV と PB_DEMO.PROD の 2 スキーマを用意し、同じパイプラインを 2 つのターゲットに Deploy します。

事前準備

ACCOUNTADMIN で、専用ロール、warehouse、DB、外部ステージを作ります。公開バケットなので認証情報は不要です。権限は OWNERSHIP でまとめず、ドキュメントの一覧どおり個別に付与して過不足を確かめます。

事前準備のSQL
USE ROLE ACCOUNTADMIN;

CREATE ROLE PB_ENGINEER;
GRANT ROLE PB_ENGINEER TO ROLE SYSADMIN;
GRANT ROLE PB_ENGINEER TO USER <自分のユーザー名>;

CREATE WAREHOUSE PB_WH WAREHOUSE_SIZE = XSMALL AUTO_SUSPEND = 60 AUTO_RESUME = TRUE;
GRANT USAGE ON WAREHOUSE PB_WH TO ROLE PB_ENGINEER;

CREATE DATABASE PB_DEMO;
CREATE SCHEMA PB_DEMO.DEV;
CREATE SCHEMA PB_DEMO.PROD;

-- dbt Labs 公開の jaffle shop データ(6 年分)
CREATE STAGE PB_DEMO.PUBLIC.JAFFLE_S3
  URL = 's3://dbt-tutorial-public/long_term_dataset/'
  FILE_FORMAT = (TYPE = CSV SKIP_HEADER = 1 FIELD_OPTIONALLY_ENCLOSED_BY = '"' NULL_IF = ('', 'NULL'));

-- ドキュメントの Access control requirements に沿って個別に付与
GRANT USAGE ON DATABASE PB_DEMO TO ROLE PB_ENGINEER;
GRANT USAGE ON SCHEMA PB_DEMO.PUBLIC TO ROLE PB_ENGINEER;
GRANT USAGE ON STAGE PB_DEMO.PUBLIC.JAFFLE_S3 TO ROLE PB_ENGINEER;
GRANT USAGE, CREATE TABLE, CREATE VIEW, CREATE TASK, CREATE CODE BUNDLE, CREATE DCM PROJECT
  ON SCHEMA PB_DEMO.DEV TO ROLE PB_ENGINEER;
GRANT USAGE, CREATE TABLE, CREATE VIEW, CREATE TASK, CREATE CODE BUNDLE, CREATE DCM PROJECT
  ON SCHEMA PB_DEMO.PROD TO ROLE PB_ENGINEER;
GRANT EXECUTE TASK ON ACCOUNT TO ROLE PB_ENGINEER;

-- パイプラインを置く共有 Workspace を PB_ENGINEER が作れるようにする
GRANT CREATE WORKSPACE ON SCHEMA PB_DEMO.PUBLIC TO ROLE PB_ENGINEER;

-- 失敗時 Task が書き込むログテーブル(環境ごと)
USE ROLE PB_ENGINEER;
CREATE TABLE PB_DEMO.DEV.PIPELINE_RUN_LOG (logged_at TIMESTAMP_LTZ, graph_name VARCHAR, message VARCHAR);
CREATE TABLE PB_DEMO.PROD.PIPELINE_RUN_LOG (logged_at TIMESTAMP_LTZ, graph_name VARCHAR, message VARCHAR);

LIST @PB_DEMO.PUBLIC.JAFFLE_S3; で 6 ファイルが見えれば準備完了です。raw_orders.csv が約 298 MB、raw_order_items.csv が約 248 MB あります。serverless の Task は使わないので、EXECUTE MANAGED TASK は付けていません。

試してみた

共有 Workspace とパイプラインの作成

Workspace は共有 Workspace にします。SQL file タスクは実行のたびに Workspace 上のファイルを読みに行き、Task は所有ロールのセッションで動くためです。今回の検証では、個人 Workspace のファイルは Task から参照できませんでした(「制限事項・注意点」参照)。Projects » Workspaces の Create で Shared workspace を選び、名前 PB_SHARED、Owner role PB_ENGINEER、Database PB_DEMO、Schema PUBLIC で作ります。

Pipeline Builder を選ぶと Create Pipeline Builder ダイアログが開きます。Name と、DCM project オブジェクトを作る Database and schema を指定します。

作成すると Workspace にプロジェクトフォルダができ、空のキャンバスが開きます。フォルダには manifest.yml と sources/ があり、定義ファイル pipeline.yml は sources/definitions/ の下にできます。キャンバスでの操作はすべて pipeline.yml に書き込まれ、ファイルを直接編集した内容もキャンバスに反映されます。

実行するファイルをプロジェクトに置く

Task が実行するファイルは、プロジェクトフォルダの中(sources/ と out/ 以外)に置きます。今回は .sql ファイルを 3 つと .py ファイルを 1 つアップロードします。

01_load_raw.sql は raw テーブルの作成と COPY INTO です。テーブル名を DB やスキーマで修飾していない点がポイントです。Task は自身が属する DB とスキーマのコンテキストで実行されるため、同じファイルを DEV と PROD で使えます。

01_load_raw.sql
CREATE TABLE IF NOT EXISTS raw_customers (
    id VARCHAR,
    name VARCHAR
);
CREATE TABLE IF NOT EXISTS raw_orders (
    id VARCHAR,
    customer VARCHAR,
    ordered_at TIMESTAMP_NTZ,
    store_id VARCHAR,
    subtotal NUMBER,
    tax_paid NUMBER,
    order_total NUMBER
);
CREATE TABLE IF NOT EXISTS raw_order_items (
    id VARCHAR,
    order_id VARCHAR,
    sku VARCHAR
);
CREATE TABLE IF NOT EXISTS raw_products (
    sku VARCHAR,
    name VARCHAR,
    type VARCHAR,
    price NUMBER,
    description VARCHAR
);
CREATE TABLE IF NOT EXISTS raw_supplies (
    id VARCHAR,
    name VARCHAR,
    cost NUMBER,
    perishable BOOLEAN,
    sku VARCHAR
);
CREATE TABLE IF NOT EXISTS raw_stores (
    id VARCHAR,
    name VARCHAR,
    opened_at TIMESTAMP_NTZ,
    tax_rate FLOAT
);

COPY INTO raw_customers   FROM @PB_DEMO.PUBLIC.JAFFLE_S3/raw_customers.csv;
COPY INTO raw_orders      FROM @PB_DEMO.PUBLIC.JAFFLE_S3/raw_orders.csv;
COPY INTO raw_order_items FROM @PB_DEMO.PUBLIC.JAFFLE_S3/raw_order_items.csv;
COPY INTO raw_products    FROM @PB_DEMO.PUBLIC.JAFFLE_S3/raw_products.csv;
COPY INTO raw_supplies    FROM @PB_DEMO.PUBLIC.JAFFLE_S3/raw_supplies.csv;
COPY INTO raw_stores      FROM @PB_DEMO.PUBLIC.JAFFLE_S3/raw_stores.csv;

COPY INTO はロード履歴(64 日間)にあるファイルをスキップします。そのため同じファイルに対する 2 回目以降の実行は、初回より短い時間で終わります(今回は初回 35 秒、2 回目以降 4〜10 秒)。

02_staging.sql は dbt の jaffle shop にある stg_* モデルを view にしたものです。6 つの view を作ります。

02_staging.sql
CREATE OR REPLACE VIEW stg_customers AS
SELECT
    id   AS customer_id,
    name AS customer_name
FROM raw_customers;

CREATE OR REPLACE VIEW stg_locations AS
SELECT
    id   AS location_id,
    name AS location_name,
    tax_rate,
    DATE_TRUNC('day', opened_at) AS opened_date
FROM raw_stores;

CREATE OR REPLACE VIEW stg_order_items AS
SELECT
    id       AS order_item_id,
    order_id,
    sku      AS product_id
FROM raw_order_items;

CREATE OR REPLACE VIEW stg_orders AS
SELECT
    id          AS order_id,
    store_id    AS location_id,
    customer    AS customer_id,
    subtotal    AS subtotal_cents,
    tax_paid    AS tax_paid_cents,
    order_total AS order_total_cents,
    (subtotal / 100)::NUMBER(16, 2)    AS subtotal,
    (tax_paid / 100)::NUMBER(16, 2)    AS tax_paid,
    (order_total / 100)::NUMBER(16, 2) AS order_total,
    DATE_TRUNC('day', ordered_at)      AS ordered_at
FROM raw_orders;

CREATE OR REPLACE VIEW stg_products AS
SELECT
    sku         AS product_id,
    name        AS product_name,
    type        AS product_type,
    description AS product_description,
    (price / 100)::NUMBER(16, 2)        AS product_price,
    COALESCE(type = 'jaffle', FALSE)    AS is_food_item,
    COALESCE(type = 'beverage', FALSE)  AS is_drink_item
FROM raw_products;

CREATE OR REPLACE VIEW stg_supplies AS
SELECT
    MD5(COALESCE(id, '') || '-' || COALESCE(sku, '')) AS supply_uuid,
    id         AS supply_id,
    sku        AS product_id,
    name       AS supply_name,
    (cost / 100)::NUMBER(16, 2) AS supply_cost,
    perishable AS is_perishable_supply
FROM raw_supplies;

03_marts.sql は marts モデルをテーブルにしたものです。order_items、orders、customers の順に並べ、依存する側を後に作ります。

03_marts.sql
CREATE OR REPLACE TABLE order_items AS
WITH order_supplies_summary AS (
    SELECT
        product_id,
        SUM(supply_cost) AS supply_cost
    FROM stg_supplies
    GROUP BY 1
)
SELECT
    oi.*,
    o.ordered_at,
    p.product_name,
    p.product_price,
    p.is_food_item,
    p.is_drink_item,
    s.supply_cost
FROM stg_order_items AS oi
LEFT JOIN stg_orders   AS o ON oi.order_id = o.order_id
LEFT JOIN stg_products AS p ON oi.product_id = p.product_id
LEFT JOIN order_supplies_summary AS s ON oi.product_id = s.product_id;

CREATE OR REPLACE TABLE orders AS
WITH order_items_summary AS (
    SELECT
        order_id,
        SUM(supply_cost)     AS order_cost,
        SUM(product_price)   AS order_items_subtotal,
        COUNT(order_item_id) AS count_order_items,
        SUM(CASE WHEN is_food_item  THEN 1 ELSE 0 END) AS count_food_items,
        SUM(CASE WHEN is_drink_item THEN 1 ELSE 0 END) AS count_drink_items
    FROM order_items
    GROUP BY 1
)
SELECT
    o.*,
    s.order_cost,
    s.order_items_subtotal,
    s.count_food_items,
    s.count_drink_items,
    s.count_order_items,
    s.count_food_items  > 0 AS is_food_order,
    s.count_drink_items > 0 AS is_drink_order,
    ROW_NUMBER() OVER (PARTITION BY o.customer_id ORDER BY o.ordered_at ASC) AS customer_order_number
FROM stg_orders AS o
LEFT JOIN order_items_summary AS s ON o.order_id = s.order_id;

CREATE OR REPLACE TABLE customers AS
WITH customer_orders_summary AS (
    SELECT
        customer_id,
        COUNT(DISTINCT order_id)     AS count_lifetime_orders,
        COUNT(DISTINCT order_id) > 1 AS is_repeat_buyer,
        MIN(ordered_at)  AS first_ordered_at,
        MAX(ordered_at)  AS last_ordered_at,
        SUM(subtotal)    AS lifetime_spend_pretax,
        SUM(tax_paid)    AS lifetime_tax_paid,
        SUM(order_total) AS lifetime_spend
    FROM orders
    GROUP BY 1
)
SELECT
    c.*,
    s.count_lifetime_orders,
    s.first_ordered_at,
    s.last_ordered_at,
    s.lifetime_spend_pretax,
    s.lifetime_tax_paid,
    s.lifetime_spend,
    CASE WHEN s.is_repeat_buyer THEN 'returning' ELSE 'new' END AS customer_type
FROM stg_customers AS c
LEFT JOIN customer_orders_summary AS s ON c.customer_id = s.customer_id;

CREATE OR REPLACE TABLE products  AS SELECT * FROM stg_products;
CREATE OR REPLACE TABLE locations AS SELECT * FROM stg_locations;
CREATE OR REPLACE TABLE supplies  AS SELECT * FROM stg_supplies;

check_marts.py は marts の件数チェックです。--min-orders 未満なら例外を送出して Task を失敗させ、成功時は PIPELINE_CHECK_LOG に件数を記録します。Code Bundle で動く Python には Snowpark のセッションが自動で注入されるので、接続情報は書きません。

check_marts.py
"""marts の件数チェック (Python タスク)。

ORDERS の件数が --min-orders 未満なら例外を送出し、タスクを失敗させる。
成功時は PIPELINE_CHECK_LOG に件数を記録する。
"""
import argparse

from snowflake.snowpark.context import get_active_session

def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--min-orders", type=int, default=1)
    parser.add_argument("--schema", default=None, help="database.schema (default: current schema of the session)")
    args = parser.parse_args()

    session = get_active_session()
    schema = args.schema or f"{session.get_current_database()}.{session.get_current_schema()}"

    counts = {t: session.table(f"{schema}.{t}").count() for t in ("ORDERS", "ORDER_ITEMS", "CUSTOMERS")}
    print(f"schema={schema} counts={counts} min_orders={args.min_orders}")

    if counts["ORDERS"] < args.min_orders:
        raise RuntimeError(f"ORDERS has {counts['ORDERS']} rows, less than --min-orders {args.min_orders}")

    session.sql(
        f"CREATE TABLE IF NOT EXISTS {schema}.PIPELINE_CHECK_LOG "
        "(checked_at TIMESTAMP_LTZ, orders NUMBER, order_items NUMBER, customers NUMBER)"
    ).collect()
    session.sql(
        f"INSERT INTO {schema}.PIPELINE_CHECK_LOG VALUES "
        f"(CURRENT_TIMESTAMP(), {counts['ORDERS']}, {counts['ORDER_ITEMS']}, {counts['CUSTOMERS']})"
    ).collect()
    print("check passed")

if __name__ == "__main__":
    main()

SQL file タスクを並べて task graph を作る

ファイルエクスプローラーから 01_load_raw.sql をキャンバスにドラッグすると、SQL file タスクのカードができて設定ドロワーが開きます。General タブで Name(完全修飾名)と Warehouse を設定します。

次に 02_staging.sql を LOAD_RAW カードの右側へドラッグし、Run After のヒントが出たところでドロップします。Task が 1 つ増えて 2 つがつながり、同時に task graph が作られます。確認ダイアログは出ず、先頭の Task が root になります。03_marts.sql も同じ要領で STAGING の後ろに置きます。

つなげない組み合わせは、理由がキャンバスに表示されます。ドキュメントには「スキーマが違う」「root が 2 つになる」「この project 以外の Task」「別の graph に属している」「ループになる」の 5 つが挙がっています。

Python タスクと Code Bundle

check_marts.py を MARTS の後ろにドロップすると、Notebook / Python タスクができます。同時に、プロジェクトのコードを束ねる Code Bundle が自動で作られ、ランタイムと compute の既定値が設定されます。今回は compute pool を使わず warehouse で動かすので、Specification タブを変更します。

Code Bundle の名前は Pipeline Builder が付けたものをそのまま使います。Python ファイルを増やすときは、カードの + から同じ bundle を使う Task を追加し、Entrypoint だけ変えます。

条件付きタスクで成功時と失敗時を分ける

Task は既定では「すべての先行 Task が成功したとき」に動きます。ドキュメントでは、Task タブの Dependencies にある Predecessors condition を変えると、失敗時だけ動く Task を作れるとあります。CHECK_MARTS の後ろに SQL タスクを 2 つ置きます。

PUBLISH_SUMMARY は既定の条件のまま、日次サマリーを作ります。stg_orders の ordered_at は日付に切り詰めた TIMESTAMP_NTZ なので、TO_DATE で日付型にします。

CREATE OR REPLACE TABLE daily_summary AS
SELECT
    TO_DATE(ordered_at) AS order_date,
    COUNT(*)            AS order_count,
    SUM(order_total)    AS revenue
FROM orders
GROUP BY 1;

LOG_FAILURE は先行 Task が失敗したときだけ動くようにして、ログテーブルに書き込みます。

INSERT INTO pipeline_run_log (logged_at, graph_name, message)
SELECT
    CURRENT_TIMESTAMP(),
    SYSTEM$TASK_RUNTIME_INFO('CURRENT_TASK_GRAPH_NAME'),
    'CHECK_MARTS failed';

検証時の画面にはプルダウンが無く、Predecessors と Additional conditions だけでした。Additional conditions は Task の WHEN 句に相当するので、ここに SYSTEM$PREDECESSORS_STATE('AT_LEAST_ONE_FAILED') を書いて同じ条件にします。

SYSTEM$PREDECESSORS_STATE に指定できる条件は次の 9 種類です。左がドキュメントの Predecessors condition の表記、右が関数に渡す値です。

Predecessors condition 引数の値 動く条件
All succeeded(既定) ALL_SUCCEEDED すべての先行 Task が成功
All done ALL_DONE 成功 / 失敗 / skip / キャンセルを問わず、すべて終了
At least one failed AT_LEAST_ONE_FAILED 1 つ以上が失敗
All failed ALL_FAILED すべて失敗
At least one succeeded AT_LEAST_ONE_SUCCEEDED 1 つ以上が成功
None failed NONE_FAILED 失敗がない(成功か skip)
None failed, at least one succeeded NONE_FAILED_MIN_ONE_SUCCEEDED 失敗がなく、1 つ以上が成功
All skipped ALL_SKIPPED すべて skip
None skipped NONE_SKIPPED skip がない

Predecessors condition と Additional conditions の両方を指定した場合は、双方を満たしたときだけ Task が動きます。Additional conditions には他の SQL の真偽式も書けます。ドキュメントには SYSTEM$STREAM_HAS_DATA(...) でデータがあるときだけ動かす例があります。SYSTEM$SET_RETURN_VALUE と SYSTEM$GET_PREDECESSOR_RETURN_VALUE で分岐する例もあります。root と finalizer には Predecessors condition を設定できません。

task graph のスケジュールと既定値

Objects » Task graphs から graph を開き、名前、スケジュール、Deploy 後の状態、Task の既定値を設定します。今回は Cron を設定しつつ Desired state は Suspended にして、手動実行で確認します。

今回は必要最低限の項目だけ設定しています。設定項目はほかにも多数あります。

graph を作った時点で、スケジュールなど graph 全体の設定は root Task から graph へ移ります。Task が共通で持つ値(warehouse など)は Task defaults へ移り、Task 個別に指定しなければ既定値が使われます。Overlap policy、Auto retry attempts、Suspend after # of failures、通知用の integration も graph 側の設定です。

生成された YAML

ドロワーの YAML トグルで 1 オブジェクト分、sources/definitions/pipeline.yml を開けば全体の定義を見られます。

生成された YAML
definitions:
  - resource: task
    name: PB_DEMO.DEV.load_raw_task
    definition:
      type: sql_file
      location:
        type: stage_uri
        uri: >-
          snow://workspace/PB_DEMO.PUBLIC.PB_SHARED/versions/last/JAFFLE_PIPELINE/01_load_raw.sql
  - resource: task
    name: PB_DEMO.DEV.staging_task
    definition:
      type: sql_file
      location:
        type: stage_uri
        uri: >-
          snow://workspace/PB_DEMO.PUBLIC.PB_SHARED/versions/last/JAFFLE_PIPELINE/02_staging.sql
    predecessors:
      - PB_DEMO.DEV.LOAD_RAW_TASK
  - resource: task_graph_object
    name: PB_DEMO.DEV.JAFFLE_PIPELINE
    roots:
      - PB_DEMO.DEV.load_raw_task
    schedule:
      type: cron
      value: 0 9 * * * UTC
    state: suspended
    task_defaults:
      warehouse: PB_WH
  - resource: task
    name: PB_DEMO.DEV.marts_task
    definition:
      type: sql_file
      location:
        type: stage_uri
        uri: >-
          snow://workspace/PB_DEMO.PUBLIC.PB_SHARED/versions/last/JAFFLE_PIPELINE/03_marts.sql
    predecessors:
      - PB_DEMO.DEV.STAGING_TASK
  - resource: code_bundle
    name: PB_DEMO.DEV.jaffle_pipeline_waqvc
    from:
      type: asset
      asset: project
  - resource: task
    name: PB_DEMO.DEV.check_marts_task
    definition:
      type: code_bundle
      name: PB_DEMO.DEV.jaffle_pipeline_waqvc
      entrypoint: check_marts.py
      specification:
        bundle:
          type: custom
          compute_type: warehouse
          language: python
          compute_options:
            runtime_version: '3.11'
          properties:
            requirements_file: ''
      arguments:
        - '--min-orders'
        - '1'
    predecessors:
      - PB_DEMO.DEV.MARTS_TASK
    warehouse: PB_WH
  - resource: task
    name: PB_DEMO.DEV.PUBLISH_SUMMARY
    definition:
      type: sql
      value: |-
        CREATE OR REPLACE TABLE daily_summary AS
        SELECT
            TO_DATE(ordered_at) AS order_date,
            COUNT(*)            AS order_count,
            SUM(order_total)    AS revenue
        FROM orders
        GROUP BY 1;
    predecessors:
      - PB_DEMO.DEV.CHECK_MARTS_TASK
    warehouse: PB_WH
  - resource: task
    name: PB_DEMO.DEV.LOG_FAILURE
    definition:
      type: sql
      value: |-
        INSERT INTO pipeline_run_log (logged_at, graph_name, message)
        SELECT
            CURRENT_TIMESTAMP(),
            SYSTEM$TASK_RUNTIME_INFO('CURRENT_TASK_GRAPH_NAME'),
            'CHECK_MARTS failed';
    predecessors:
      - PB_DEMO.DEV.CHECK_MARTS_TASK
    warehouse: PB_WH
    condition: SYSTEM$PREDECESSORS_STATE('AT_LEAST_ONE_FAILED')

Code Bundle は from: asset で、manifest.yml の assets: project: path: '**/*' が指すプロジェクト全体を Deploy 時に束ねます。SQL file タスクは location.uri に Workspace 上のファイルの絶対 URI を持ち、実行のたびにそこを読みます。

生成直後の URI は versions/live でした。これは編集中の自分のビューを指し、Task のセッションからは見えません。Task が読めるのは公開済みの最新版 versions/last です。Deploy の前に、3 つの SQL file タスクの URI を versions/last に書き換えて Publish します。上の YAML は書き換え後のものです。

注意: versions/live のまま Deploy すると、実行時に「Content of the provided location can not be read: version live is not found.」で失敗します。

Deploy する

キャンバス右上の Deploy を押すと、DCM project の plan が実行されます。DCM project オブジェクトがまだ無いので、作ってよいか確認するダイアログが出ます。続く Review planned changes で、作成されるオブジェクトの一覧を確認して Deploy します。

Deploy が完了したら、作成されたオブジェクトを確認します。

Deploy はプロジェクトファイルの内容にアカウントの状態を合わせる操作です。Task を追加すれば作成、設定を変えれば ALTER、キャンバスから消せば次の Deploy で DROP されます。Code Bundle に含まれるファイルを変えた場合も、Deploy で新しいバージョンに更新されます。

実行して監視する

Deployed Project を押すと、Snowsight の DCM project 詳細画面が Objects タブで開きます。task graph を選び、Graph タブの Run Task Graph アイコンで実行します。Suspended のままでも手動実行はできます。

SQL からは EXECUTE TASK GRAPH で実行し、TASK_HISTORY で結果を追えます。task graph に属する Task は単独では実行できません。

EXECUTE TASK GRAPH PB_DEMO.DEV.JAFFLE_PIPELINE;

SELECT name, state, error_message, scheduled_time, completed_time
FROM TABLE(PB_DEMO.INFORMATION_SCHEMA.TASK_HISTORY(
    SCHEDULED_TIME_RANGE_START => DATEADD(hour, -1, CURRENT_TIMESTAMP())))
WHERE schema_name = 'DEV'
ORDER BY scheduled_time;

graph 全体は 30 秒で終わりました(X-Small)。raw はその前の実行でロード済みだったため、LOAD_RAW_TASK は短時間です。

SELECT graph_name, state, first_error_task_name, scheduled_time, completed_time
FROM TABLE(PB_DEMO.INFORMATION_SCHEMA.COMPLETE_TASK_GRAPHS(RESULT_LIMIT => 10))
ORDER BY scheduled_time DESC;

失敗したときだけ動く Task を確かめる

CHECK_MARTS の引数を変えて、わざと失敗させます。--min-orders を 999999999 にすると、209 万行では足りないので例外になります。

CHECK_MARTS_TASK が失敗すると、後続のうち PUBLISH_SUMMARY は SKIPPED になり、LOG_FAILURE だけが実行されます。その結果、PIPELINE_RUN_LOG に 1 行入ります(graph_name は完全修飾名)。COMPLETE_TASK_GRAPHS では state が FAILED、first_error_task_name が CHECK_MARTS_TASK でした。

SELECT * FROM PB_DEMO.DEV.PIPELINE_RUN_LOG;

DEV と PROD の 2 ターゲットに展開する

パイプラインは DCM project なので、manifest.yml にターゲットを増やせば同じ定義を別の DCM project オブジェクトへ Deploy できます。今回は同じアカウントの PB_DEMO.PROD を PROD ターゲットにします。

スキーマ名を変数にするため、manifest.yml に PROD ターゲットと templating の env を足します。生成された assets の指定はそのまま残します。

manifest_version: 2
type: DCM_PROJECT
default_target: DEV

targets:
  DEV:
    account_identifier: <ORG>-<ACCOUNT>
    project_name: PB_DEMO.DEV.JAFFLE_PIPELINE
    project_owner: PB_ENGINEER
    templating_config: DEV
  PROD:
    account_identifier: <ORG>-<ACCOUNT>
    project_name: PB_DEMO.PROD.JAFFLE_PIPELINE
    project_owner: PB_ENGINEER
    templating_config: PROD

templating:
  configurations:
    DEV:
      env: DEV
    PROD:
      env: PROD

assets:
  project:
    path: '**/*'

pipeline.yml では、オブジェクト名と参照名に含まれる PB_DEMO.DEV. をすべて PB_DEMO.{{ env }}. に置き換えます。Task、task graph、Code Bundle の名前だけでなく、roots、predecessors、Code Bundle の参照先も対象です。SQL file タスクの URI は共有 Workspace を指しているので、そのままにします。

  - resource: task
    name: PB_DEMO.{{ env }}.staging_task
    predecessors:
      - PB_DEMO.{{ env }}.LOAD_RAW_TASK
    definition:
      type: sql_file
      location:
        type: stage_uri
        uri: >-
          snow://workspace/PB_DEMO.PUBLIC.PB_SHARED/versions/last/JAFFLE_PIPELINE/02_staging.sql

PROD 側の DCM project オブジェクトは先に作っておきます。キャンバスの Deploy は無ければ作るか確認してきますが、下部ペインからの Deploy は「object does not exist, or the project_owner role does not have access」で止まりました。

USE ROLE PB_ENGINEER;
CREATE DCM PROJECT PB_DEMO.PROD.JAFFLE_PIPELINE;

Deploy は Workspace 下部ペインの DCM project コントロールから行います。target メニューを PROD に切り替え、操作メニューの Deploy を実行します。

SQL ファイルの中はテーブル名を修飾していないので、ファイル側は何も変えずに PROD でも動きます。

制限事項・注意点

Public Preview の時点(2026年10月27日)で確認した内容です。

制限事項・注意点
  • キャンバスで作れるのは Task、task graph、Code Bundle、DCM project だけです
  • Dynamic Tables や view は、同じ DCM project の定義ファイルに DEFINE で書きます
  • 1 つの task graph の Task は同じスキーマ、同じ DCM project、同じ所有ロールに限られます
  • Task 名とオブジェクト名は完全修飾で書きます
  • Code Bundle のソースはプロジェクト内のファイルだけで、ステージは選べません
  • プロジェクト外で作った既存の Code Bundle を Task から呼ぶことはできます
  • Notebook タスクは compute pool が必須です
  • Python ファイルは Specification タブで Compute type を Warehouse にすれば warehouse で動きます
  • warehouse での Code Bundle 実行は Public Preview です
  • Task の SQL は Task 自身の DB とスキーマをコンテキストに実行されます(CURRENT_SCHEMA() で確認)
  • SQL file タスクは実行のたびに Workspace 上のファイルを読みます。Deploy 時に版が固定される Code Bundle とは違います
  • Task は SYSTEM ユーザーと所有ロールのセッションで動きます。個人 Workspace のファイルは GRANT READ ON WORKSPACE を付けても Task から読めませんでした
  • 共有 Workspace でも Task が読めるのは公開済みの versions/last だけです。生成される versions/live は書き換えが要ります
  • SQL file タスクの URI はファイルを置いた Workspace の絶対パスです。プロジェクトを別の Workspace へ移しても自動では変わりません
  • root の Task が失敗すると後続はすべて skip になり、At least one failed の Task も動きません(skip は失敗ではない)
  • 定義ファイルの実体は sources/definitions/pipeline.yml です(ドキュメントはプロジェクト直下の pipeline.yml と説明)
  • 下部ペインから別ターゲットへ Deploy するときは、DCM project オブジェクトを先に作っておきます
  • 分岐の後ろで合流する Task は、既定の All succeeded のままだと動きません(片方が常に skip のため)
  • 合流する Task の条件は None failed, at least one succeeded にします
  • Code Bundle を管理する DCM project は DROP DCM PROJECT だけでは消せません
  • EXECUTE DCM PROJECT <name> PURGE で管理対象を削除してから DROP します
  • CoCo によるパイプライン生成と /pipeline-builder スキルは「coming soon」です
  • task graph は最大 1,000 Task、1 Task あたりの親と子はそれぞれ最大 100 です

最後に

Pipeline Builder では CREATE TASK を書かずに SQL と Python の task graph を組め、plan で差分を見てから Deploy できました。
Task のセッションが Task 自身のスキーマで動く性質と DCM のターゲットを組み合わせると、同じファイルで DEV / PROD を分けられます。
Task 間の依存関係を SQL ではなく画面で設計できる点は、大きな利点だと感じました!

この記事が何かの参考になれば幸いです!


Snowflakeの導入支援はクラスメソッドに!

クラスメソッドでは Snowflake の導入を支援しております。
製品の詳細や支援の内容についてお気軽にお問い合わせください。

Snowflakeの詳細を見る

この記事をシェアする

関連記事