Learning Platform
Глоссарий Troubleshooting
Урок 13.02 · 28 мин
Продвинутый
dbt FusionSDF Semantic CompilerSQL IRANTLRADBC

Fusion components: Semantic Compiler, SQL IR, ADBC

В прошлом уроке мы посмотрели на Fusion с высоты птичьего полёта — что это, откуда взялось, чем отличается от dbt-core. Этот урок — про внутренности. Что именно делает Fusion engine, какие у него компоненты, и почему конкретный выбор технологий (ANTLR, ADBC, IR) даёт те самые цифры ускорения.

Зачем разбираться в этом? Не для того, чтобы написать конкурента Fusion. А для того, чтобы:

  • Понимать, что Fusion может и не может делать.
  • Уметь debugging Fusion-related issues (например, экзотический Jinja, который ломается).
  • Принимать осознанные решения про миграцию.
  • Не быть удивлёнными, когда что-то ведёт себя иначе чем dbt-core.

GIL + threading vs multiprocessing

Большая картина: pipeline Fusion vs dbt-core

Pipeline сравнение

Ключевое отличие — после Jinja rendering Fusion не оставляет SQL как непрозрачную строку. Он парсит SQL в Intermediate Representation, анализирует, и компилирует обратно. dbt-core такого делать не может — Python implementation было бы слишком медленным.


Компонент 1: Semantic Compiler

Semantic Compiler — это сердце Fusion. Он делает то, что dbt-core делал только частично (rendering), и добавляет новое (static analysis).

input:                       output:
  models/marts/fct.sql       Compiled Model:
  {{ ref('stg_o') }}           sql: "SELECT ... FROM target_db.staging.stg_o"
                               lineage: [stg_o]
                               column_types: {order_id: varchar(36), ...}
                               diagnostics: [warning: deprecated macro X]

Что Semantic Compiler делает:

  1. Jinja rendering — рендерит Jinja2 синтаксис в SQL.
  2. Macro resolution — резолвит {% macro ... %} через dispatch.
  3. Static SQL parsing — парсит результат в AST через ANTLR grammar.
  4. Type inference — выводит column types из catalog (для warehouses, которые поддерживают INFORMATION_SCHEMA быстро).
  5. Lineage analysis — column-level lineage (опционально).
  6. Diagnostics — собирает warnings и errors с локацией в исходнике.

Это то, что SDF Labs строили два года до acquisition. Не «парсер SQL» (их тысячи), а полноценный compiler с типизацией и lineage.

NOTE

Почему компилятор, а не просто rendering: dbt-core рендерит Jinja в SQL string и отправляет в warehouse. Если SQL невалиден — warehouse возвращает ошибку. Fusion парсит SQL сам, до отправки. Если SQL невалиден — ошибка сразу с диагностикой. Если используешь несуществующий column — ошибка сразу. Это shift-left validation.


Компонент 2: SQL IR (Intermediate Representation)

После того, как SDF Parser распарсил SQL, он строит IR — структурированное дерево, описывающее SQL логически (не как текст).

Пример SQL:

SELECT customer_id, SUM(amount) AS total
FROM analytics.staging.stg_orders
WHERE order_date >= '2025-01-01'
GROUP BY customer_id

В IR (упрощённо):

SelectStmt {
  projection: [
    Column(customer_id),
    Alias(Aggregate(SUM, Column(amount)), "total")
  ],
  from: Table(analytics.staging.stg_orders),
  where: BinOp(GE, Column(order_date), Literal("2025-01-01")),
  group_by: [Column(customer_id)]
}

Что IR даёт:

  1. Lineage — IR явно говорит «эта модель читает из analytics.staging.stg_orders и колонку amount». Без парсинга строк регэкспами.
  2. Column propagation — можно проследить откуда взялся каждый column в outputе. Column-level lineage.
  3. Dialect translation — IR абстрактный, не привязан к Snowflake/BigQuery. Можно сгенерировать SQL обратно под другой dialect.
  4. Static optimization — Fusion может (опционально) предупредить о SELECT * без проекций, неиспользуемых колонках, etc.
SQL text -> ANTLR parser -> AST -> semantic resolution -> IR
IR -> backend codegen -> SQL text (для нужного dialect)

Компонент 3: Multi-dialect ANTLR grammar

ANTLR — это parser generator. Вы пишете grammar (BNF-like описание языка), ANTLR генерирует парсер. Для SQL это особенно полезно, потому что:

Зачем multi-dialect grammar

ANTLR-based подход означает, что dbt Labs может добавлять поддержку нового warehouse, написав grammar overrides — не переписывая весь парсер. Это structurally extensible, что важно для long-term maintenance.


Компонент 4: ADBC drivers

ADBC (

Arrow Database Connectivity
) — это новый стандарт от Apache Arrow community для общения с warehouses.

Сравнение с alternatives:

DBAPI (Python):       app -> driver -> wire protocol -> DB
                           ↑ converts to Python objects
                           ↑ row-by-row in cursor

ODBC:                 app -> driver -> wire protocol -> DB
                           ↑ converts to ODBC types
                           ↑ row-by-row in result set

ADBC:                 app -> driver -> Arrow IPC -> DB
                           ↑ columnar batches
                           ↑ zero-copy from Arrow record batches

Почему ADBC для Fusion:

  1. Arrow native — Fusion может работать с результатами как с Arrow record batches без копирования.
  2. Columnar throughput — для больших результатов columnar transfer быстрее построчного.
  3. Async-first — драйверы поддерживают async API, что хорошо ложится на Rust tokio.
  4. Унифицированный driver model — один интерфейс для всех warehouses.
WARNING

Реальность 2025-2026: ADBC drivers пока не для всех warehouses production-ready. Snowflake ADBC driver — хорош. BigQuery — есть, но moudling. Postgres — стандартный, работает. Databricks, Redshift — на разной стадии готовности. Это одна из причин, почему dbt-core (с battle-tested DBAPI adapters) остаётся production-default для многих сценариев.


Компонент 5: Artifacts

dbt-core генерирует знакомые артефакты: manifest.json, run_results.json, catalog.json. Fusion — в основном генерирует те же артефакты, потому что dbt экосистема (CI tools, observability, slim CI) полагается на них.

target/
├── manifest.json         # совместим с dbt-core schema
├── run_results.json      # совместим с dbt-core schema
├── catalog.json          # совместим
├── partial_parse.msgpack # Fusion использует свою версию (.fusion-cache)
└── graph.gpickle         # Fusion использует свой DAG format (.fusion-graph)

Что Fusion добавляет или меняет:

  • semantic_manifest.json — расширен column-level lineage (если включено).
  • fusion-cache/ — Fusion-specific cache, заменяет partial_parse.msgpack. Структурно другой, не совместим с dbt-core.
  • fusion-graph.json — Rust-friendly DAG format, заменяет graph.gpickle (Python pickle не подходит для Rust).
  • Diagnostics с локациейrun_results.json extension с точными source locations (file:line:column) для ошибок.

Что это означает практически: если у вас есть tooling, который читает manifest.json и run_results.json — продолжит работать с Fusion. Если читаете partial_parse.msgpack или graph.gpickle напрямую (что редко, но бывает) — нужны изменения.


Что Rust меняет в каждой фазе

Подытожим, в каких именно фазах Rust даёт ускорение:

Phase 1: File reading
  dbt-core: Python I/O, sequential
  Fusion:   Rust parallel I/O через rayon
  Speedup:  3-5x на больших проектах

Phase 2: YAML/SQL parsing
  dbt-core: PyYAML + Jinja2 tokenizer (Python)
  Fusion:   serde_yaml + SDF parser (Rust)
  Speedup:  10-15x

Phase 3: Jinja rendering
  dbt-core: Jinja2 Python — interpretive
  Fusion:   Rust Jinja impl + compiled rendering
  Speedup:  3-5x

Phase 4: SQL semantic analysis
  dbt-core: ничего (только rendering)
  Fusion:   ANTLR grammar + IR + lineage
  Speedup:  N/A (новая функциональность)

Phase 5: DAG construction
  dbt-core: networkx (Python)
  Fusion:   petgraph (Rust)
  Speedup:  2-3x

Phase 6: Compilation total
  Cumulative effect on 1500 models:
    dbt-core: 30 sec parse + 60 sec compile = 90 sec
    Fusion:   1 sec parse + 30 sec compile = 31 sec

Откуда берётся 30x в маркетинговом материале dbt Labs: это про parsing phase specifically (phase 1-2), которая на больших проектах самая дорогая. 30x там — реалистичное число.

2x на compilation phase (phase 3) — тоже реалистично, но это меньшая часть pipeline.


Что НЕ меняется

Phase 7: Warehouse query planning  (no change)
  dbt-core/Fusion: warehouse получает SQL, делает свой планировщик.

Phase 8: Warehouse execution  (no change)
  dbt-core/Fusion: warehouse compute time. Здесь скорость зависит только от warehouse.

Phase 9: Result fetching
  dbt-core: DBAPI cursor, row-by-row
  Fusion:   ADBC batches (если warehouse поддерживает)
  Speedup:  2-5x для больших результатов (но dbt run обычно не fetch huge results)

Phase 7-8 — это где обычно «теряется время» на production pipeline. Если ваш dbt run занимает 30 минут на Snowflake, 28 из 30 минут — это Snowflake compute. Fusion не поможет.


Проверка знанийKnowledge check
Команда мигрирует с dbt-core на Fusion. После миграции один из senior разработчиков говорит: 'я смотрю в Fusion-логах, что parsing занимает 1 секунду, compile 30 секунд, потом 28 минут warehouse. На dbt-core было 30 секунд parse, 60 секунд compile, 28 минут warehouse. Total — почти тот же. Где обещанный 30x?'
ОтветAnswer
Разработчик увидел реалистичную картину и хорошо diagnosed её. Объясняем почему "30x" верно но не равно "30x speedup всего pipeline". **Разбираем числа:** dbt-core total: 30 + 60 + 28×60 = 90 сек + 1680 сек = 1770 секунд = ~29.5 минут. Fusion total: 1 + 30 + 28×60 = 31 сек + 1680 сек = 1711 секунд = ~28.5 минут. Сэкономили: 59 секунд = 3.3% от total time. Это не "10x", не "30x" в end-to-end. **Где же 30x?** 30x относится к **parsing phase only**. И это правда: - dbt-core: 30 секунд parsing. - Fusion: 1 секунда parsing. - Speedup: 30x. Это конкретное число которое dbt Labs опубликовал, и оно corrosponds к реальности. Но это **только phase 1-2**, не весь pipeline. **Почему важно знать?** 1. **VS Code feedback loop:** разработчик правит SQL, ждёт диагностики. На dbt-core: 30 секунд parse + compile. На Fusion: 1-2 секунды. Это **30x** для UX. 2. **CI fast paths:** - dbt parse в CI (валидация что проект compile-able): dbt-core 90 сек, Fusion 31 сек. Speedup ~3x. - dbt ls (просто список моделей): dbt-core 30 сек, Fusion 1 сек. Speedup 30x. - dbt compile (full compilation без run): dbt-core 90 сек, Fusion 31 сек. Speedup 3x. 3. **dbt run в production:** - 95-99% времени — warehouse execution. - Fusion economists Я 5% от 30 минут run. - Если pipeline 30 min — экономим 1.5 минуты. Незначительно. **Правильное framing для команды:** Fusion это **fast feedback engine**. Где это ценно: - Local dev (parsing, compile, lineage diagnostics). - VS Code experience (near-instant diagnostics). - CI fast paths (parse, ls, compile). Где это **не ценно**: - Production runs (warehouse-bound). - Slow warehouse queries. - I/O-bound операции (загрузка данных). **Что senior должен ответить team:** "Total runtime изменился незначительно, потому что 95% времени — Snowflake compute. Но dev experience radically улучшился: parse 30s->1s, recompile в VS Code — instant. Это где Fusion даёт value. Если хотим production speedup — нужно optimize warehouse (clustering, materialization, threads tuning), не парсер." **Главный урок:** "30x" в marketing — это **honest** про **parsing**, но не про **total pipeline**. Понимать что Fusion ускоряет, а что нет — критично для realistic adoption decisions.

Проверка знанийKnowledge check
Стажёр изучает Fusion и спрашивает: 'почему dbt не сделал просто rewrite Jinja2 на Rust? Зачем ANTLR grammar, IR, всё это? Можно было просто заменить Python Jinja на Rust Jinja и получить ускорение.'
ОтветAnswer
Хороший вопрос — он показывает разницу между **incremental optimization** и **architectural rethink**. **Если бы dbt Labs просто заменил Jinja Python на Rust Jinja:** - Parsing phase 1 (file reading): не ускорился бы (тот же I/O). - Parsing phase 2 (Jinja tokenization): ускорился бы 3-5x. - Phase 3-5: остались бы Python. - Phase 4 (SQL semantic analysis): не появилось бы. Это было бы 5-10x ускорение only Jinja phase, и остальная архитектура осталась бы той же. Это и есть путь, который dbt Labs мог пойти, но не пошёл. **Почему не пошёл — три причины:** **1. Hybrid Python+Rust сложно поддерживать.** Если Jinja на Rust, а остальное на Python — нужен FFI bridge между ними. Каждый макрос вызов Jinja -> передача всех контекстов из Python в Rust -> return value обратно в Python. Это сложно, медленно, и инжиниринг-уродливо. Дешевле либо всё на одном языке, либо ни одного. **2. Не было бы semantic analysis.** Это новая функциональность, которая дает значительную часть value Fusion: - Column-level lineage (требует SQL parsing в IR). - Static diagnostics (требует SQL parsing). - Multi-dialect support (требует IR + dialect codegen). - Near-instant recompile (требует incremental IR updates). Без IR ничего из этого нельзя сделать чисто Rust Jinja replacement. Это и есть **architectural rethink** — добавили новую layer (semantic compilation), которая принципиально невозможна без IR. **3. SDF Labs acquisition определила архитектуру.** dbt Labs не сел и не подумал "перепишем Jinja на Rust". Они купили SDF Labs, у которого уже был **готовый SQL Semantic Compiler на Rust с IR**. Architecture diktoval acquisition. SDF Labs строили SQL компилятор два года, ANTLR grammar для multiple dialects — у них уже был. dbt Labs увидел: "если мы возьмём этот compiler и обернём его в dbt-style Jinja front-end, получим Rust dbt engine". Это умное решение. **4. Long-term roadmap.** IR даёт основу для будущих фич: - Auto-optimization (Fusion может предложить более эффективный SQL). - Cross-warehouse migrations (один IR -> разные dialects). - ML-based query planning (на IR можно train модели предсказания cost). Чистый rewrite Jinja дал бы только short-term speedup parsing. IR подход даёт long-term platform для расширений. **Главный урок:** Major engineering decisions редко "делается X потому что быстрее". Это **architectural bets** на основе: - Что available (SDF acquisition). - Что нужно long-term (IR для расширений). - Trade-offs (сложность построения IR vs. value, который он даёт). Компилятор с IR — это не "более быстрый Jinja", это **другая architecture** dbt-engine, с другой surface area функций.

Итого

  1. Semantic Compiler — сердце Fusion. Делает Jinja rendering + SQL semantic analysis. Это новый layer относительно dbt-core.
  2. SQL IR — структурированное дерево SQL, абстрактное относительно dialect. Даёт column-level lineage, static diagnostics, dialect translation.
  3. Multi-dialect ANTLR grammar — extensible подход к поддержке разных warehouses. Common grammar + dialect-specific layers.
  4. ADBC drivers — Arrow-native communication с warehouses. Замена ODBC/DBAPI. Не для всех warehouses готово на 2026.
  5. Artifacts — совместимы с dbt-core schema (manifest.json, run_results.json, catalog.json). Internal cache формат другой (fusion-cache/).
  6. 30x parsing — реалистично, но относится к phase 1-2. Total pipeline speedup зависит от warehouse-bound time. 2x compilation — phase 3.
  7. Не заменяет warehouse. Если 95% времени — Snowflake compute, Fusion даст 3-5% ускорение total.

Следующий урок — практический: когда Fusion vs dbt-core, как выбирать.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 5. Какие основные компоненты составляют dbt Fusion engine, и что они делают?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4