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
Ключевое отличие — после 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 делает:
- Jinja rendering — рендерит Jinja2 синтаксис в SQL.
- Macro resolution — резолвит
{% macro ... %}через dispatch. - Static SQL parsing — парсит результат в AST через ANTLR grammar.
- Type inference — выводит column types из catalog (для warehouses, которые поддерживают INFORMATION_SCHEMA быстро).
- Lineage analysis — column-level lineage (опционально).
- Diagnostics — собирает warnings и errors с локацией в исходнике.
Это то, что SDF Labs строили два года до acquisition. Не «парсер SQL» (их тысячи), а полноценный compiler с типизацией и lineage.
Почему компилятор, а не просто 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 даёт:
- Lineage — IR явно говорит «эта модель читает из
analytics.staging.stg_ordersи колонкуamount». Без парсинга строк регэкспами. - Column propagation — можно проследить откуда взялся каждый column в outputе. Column-level lineage.
- Dialect translation — IR абстрактный, не привязан к Snowflake/BigQuery. Можно сгенерировать SQL обратно под другой dialect.
- 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 это особенно полезно, потому что:
ANTLR-based подход означает, что dbt Labs может добавлять поддержку нового warehouse, написав grammar overrides — не переписывая весь парсер. Это structurally extensible, что важно для long-term maintenance.
Компонент 4: ADBC drivers
ADBC (
Сравнение с 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:
- Arrow native — Fusion может работать с результатами как с Arrow record batches без копирования.
- Columnar throughput — для больших результатов columnar transfer быстрее построчного.
- Async-first — драйверы поддерживают async API, что хорошо ложится на Rust tokio.
- Унифицированный driver model — один интерфейс для всех warehouses.
Реальность 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.jsonextension с точными 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 не поможет.
Итого
- Semantic Compiler — сердце Fusion. Делает Jinja rendering + SQL semantic analysis. Это новый layer относительно dbt-core.
- SQL IR — структурированное дерево SQL, абстрактное относительно dialect. Даёт column-level lineage, static diagnostics, dialect translation.
- Multi-dialect ANTLR grammar — extensible подход к поддержке разных warehouses. Common grammar + dialect-specific layers.
- ADBC drivers — Arrow-native communication с warehouses. Замена ODBC/DBAPI. Не для всех warehouses готово на 2026.
- Artifacts — совместимы с dbt-core schema (
manifest.json,run_results.json,catalog.json). Internal cache формат другой (fusion-cache/). - 30x parsing — реалистично, но относится к phase 1-2. Total pipeline speedup зависит от warehouse-bound time. 2x compilation — phase 3.
- Не заменяет warehouse. Если 95% времени — Snowflake compute, Fusion даст 3-5% ускорение total.
Следующий урок — практический: когда Fusion vs dbt-core, как выбирать.