Fusion vs dbt-core: внутренности, divergence, compatibility
Junior-курс отвечает на вопрос «когда выбирать Fusion для CI и dev». Это senior-урок: что внутри Fusion как Rust-программы, где он отличается от dbt-core на уровне семантики, и как читать его поведение, когда оно расходится с ожидаемым.
Мы исходим из того, что вы знаете манифест dbt-core, parsing pipeline, adapter API. Если нет — вернитесь к dbt-i модуль 18 (high-level Fusion overview).
Fusion vs Core: что выбрать junior в 2026 (dbt I)
Карта Rust-крейтов
Fusion — это не один Rust-бинарник, это workspace из нескольких крейтов. Imена и роли (по состоянию на 2026, после реструктуризации репозитория осенью 2025):
Источник имён крейтов:
Заметьте, что в Fusion нет крейта «executor» в традиционном смысле. Fusion не исполняет SQL сам — он шлёт SQL в warehouse через ADBC. «Executor» — это threadpool координатор, который мониторит warehouse-side query state. Это важно, потому что когда dbt Labs говорит «Rust executor», они имеют в виду query orchestration, не SQL execution engine.
SDF Lineage Format vs dbt-core Manifest
Самое глубокое архитектурное различие — внутренний формат графа. dbt-core оперирует manifest.json: плоский словарь с unique_ids моделей, их depends_on, compiled_code, refs/sources как идентификаторы. Lineage в manifest — model-level: «модель A зависит от модели B». Column-level lineage не существует на уровне manifest; его строят BI tools постфактум через dbt-osmosis или custom parsers.
Fusion внутри оперирует SDF Lineage Format (SLF) — формат, унаследованный от SDF Labs. Различие принципиальное:
dbt-core Manifest (model-level):
model.analytics.fct_orders
depends_on:
- model.analytics.stg_orders
- model.analytics.stg_customers
compiled_code: "SELECT o.order_id, c.email FROM staging.stg_orders o JOIN ..."
SDF Lineage Format (column-level, IR-backed):
model.analytics.fct_orders
columns:
- name: order_id
type: varchar(36)
source:
- table: staging.stg_orders
column: order_id
transformation: identity
- name: customer_email
type: varchar(255)
source:
- table: staging.stg_customers
column: email
transformation: identity
- name: total_amount
type: numeric(18, 2)
source:
- table: staging.stg_orders
column: amount
transformation: aggregate(SUM)
sql_ir: SelectStmt { ... }
SLF держит IR парcеного SQL вместе с column lineage tracking. dbt-core manifest держит SQL как непрозрачную строку. Это даёт Fusion несколько capabilities, которых принципиально нет у dbt-core:
- Column-level lineage без BI tools. Запрос «откуда пришёл
fct_orders.customer_email» резолвится через SLF за миллисекунды до самогоstg_customers.emailбез отправки запросов в warehouse. - Type validation до compile. Если
fct_orders.sqlпишетo.order_id + 1, аstg_orders.order_id—varchar(36), Fusion поймает ошибку на parse-time, не на warehouse-time. - Dead code detection. Колонки в
stg_orders, на которые никто не ссылается через downstream models, отмечаются как unused в SLF (опционально, черезdbt fusion lint --dead-columns). - Cross-warehouse migration. SLF dialect-agnostic. Можно сгенерировать одно и то же дерево в Snowflake SQL и в Postgres SQL (с учётом dialect rules). dbt-core такого не умеет.
Совместимость артефактов: Fusion дополнительно генерирует manifest.json, совместимый с dbt-core schema, для tooling-экосистемы (dbt-docs, dbt-osmosis, Elementary, Datafold). SLF — это internal format, который записывается как target/semantic_manifest.json или target/fusion-cache/lineage.bin (binary serialization через bincode). Если ваш downstream tooling читает только manifest.json, миграция на Fusion seamless. Если читает partial_parse.msgpack — несовместимо, Fusion использует своё.
ADBC: транспорт до warehouse
ADBC (Arrow Database Connectivity) — стандарт от Apache Arrow community для общения приложения с базой через columnar protocol. Fusion использует ADBC вместо Python DBAPI/ODBC, и это меняет несколько вещей.
ADBC adapter coverage на 2026:
- Snowflake — production-ready.
adbc_driver_snowflakeмигрировал из Apache Arrow incubator в stable. - BigQuery — production-ready через
adbc_driver_bigquery(Google и Apache Arrow community). - PostgreSQL — production-ready через
adbc_driver_postgresql. - Databricks — beta. ADBC driver есть, но не все query types покрыты.
- Redshift — limited. Использует PostgreSQL ADBC driver с Redshift-specific extensions, не нативный.
- DuckDB — production-ready (DuckDB провайдит ADBC driver нативно).
- Trino, ClickHouse, Synapse, others — community-driven, нет официальной поддержки от dbt Labs. Эти warehouses на Fusion работают через legacy compat shim (PyO3 в dbt-fusion-py-shim).
Источник статусов:
Практический смысл: если ваш warehouse не в списке native ADBC support, Fusion работает, но через PyO3 shim — теряете часть speedup (parsing всё ещё быстрее, но result transport остаётся Python). На custom warehouse adapters Fusion может вообще не запускаться до миграции adapter package.
Static SQL analysis: что Fusion ловит до warehouse
SDF Semantic Compiler делает parsing SQL в IR. Из IR можно делать static checks. Что Fusion проверяет на parse-time, что dbt-core не проверяет:
-
Column references resolved against catalog. Если в
fct_orders.sqlпишетеSELECT customer_emial FROM stg_customers(опечатка), Fusion заметит, что в SLF метаданныхstg_customersнет колонкиcustomer_emial. Это catch до warehouse. -
Type compatibility в expressions.
WHERE order_date > 'not-a-date'— Fusion заметит string vs date mismatch. dbt-core отправит SQL и получит ошибку из warehouse. -
Aggregate misuse без GROUP BY.
SELECT customer_id, SUM(amount) FROM ... WITHOUT GROUP BY— static check. -
Window function context. Если используете
LAGвWHERE(что warehouses обычно запрещают), Fusion поймает на parse. -
Unused columns. Колонки в
stg_*, на которые никто не ссылается через downstream, — warning вdbt fusion lint. -
Dialect-specific syntax violations. Если в
target.type == 'snowflake'пишетеLIMIT n OFFSET m FETCH FIRST k ROWS— Fusion знает, что Snowflake не поддерживает FETCH FIRST в этой комбинации, и ругается.
Это то, что в dbt-core либо вообще не проверяется (и ошибки прилетают из warehouse), либо требует отдельных пакетов (sqlfluff, dbt-osmosis, Elementary для column lineage).
Когда static checks включать: по умолчанию Fusion делает только syntactic check (SQL парсится). Semantic checks (column resolution, type checking) включаются через dbt fusion lint или конфигурацию dbt_project.yml: fusion: { strict_lineage: true }. Strict mode медленнее (требует scan INFORMATION_SCHEMA), но ловит больше до warehouse. На CI обычно включают, на local dev — опционально.
Когда Fusion emits divergent SQL
Fusion compiles тот же Jinja и тот же SQL код, что dbt-core. В большинстве случаев compiled SQL байт-идентичен между движками. Но есть 1-2% случаев, когда Fusion emits SQL, который отличается от dbt-core. Эти кейсы — главный источник production-incidentов при миграции.
Детектирование divergence
Два инструмента, которые senior должен использовать перед production-миграцией:
# 1. dbt fusion compile --explain
# Выводит compiled SQL с annotations:
# - какой macro был выбран в dispatch (с приоритетами)
# - какие env_var захвачены и когда
# - какие quoting decisions приняты
# - какой CTE-naming pattern использован
dbt fusion compile --select fct_orders --explain > fusion_compiled.sql
# 2. Same на dbt-core для сравнения
dbt compile --select fct_orders
cat target/compiled/.../fct_orders.sql > core_compiled.sql
# 3. Diff
diff fusion_compiled.sql core_compiled.sql
Для систематической проверки большого проекта — audit_helper package с custom macros:
-- macro audit_helper.compare_compiled_models
-- Запускает все модели проекта на Fusion и dbt-core, делает diff
-- Выводит модели с non-trivial разницей в compiled SQL
{{ audit_helper.compare_engines(
primary_engine='fusion',
secondary_engine='core',
models=ref_all_models(),
diff_threshold='ignore_whitespace'
) }}
CI-практика для миграции: gate production на 0 SQL diff моделей. Любой diff требует review перед deployment.
dbt-autofix: AST-rewriter, не AI
В дискуссиях вокруг Fusion часто встречается мнение, что dbt-autofix «использует AI для миграции». Это неверно. dbt-autofix — deterministic rules-based AST rewriter, написанный на Python (для совместимости с dbt-core ecosystem). Он работает по тем же принципам, что rustfmt, prettier или 2to3 для Python.
dbt-autofix pipeline:
1. Parse project files (SQL, YAML, Jinja templates) в AST.
2. Apply transformation rules (declarative pattern matching + rewriting).
3. Emit modified files с preserved formatting (best-effort).
4. Log rule applications, conflict resolution, manual review hints.
Какие AST-трансформации делает
Категории правил, реализованных в dbt_autofix.rules.*:
- Deprecated macro replacements:
adapter_macro(deprecated в 1.5) ->adapter.dispatch. Pattern matching на calls, rewriting argument structure. - Config block migration: inline
{% materialized %}->{{ config(materialized=...) }}. AST-level rewrite block syntax. - Quoting normalization: ensure
quoting:policy explicit в dbt_project.yml. - Dispatch list normalization: явное указание
packagesparameter вadapter.dispatch(предотвращает Fusion HashMap non-determinism). - Recursion flatten: для несложных recursive macros — попытка переписать в loop. Сложные оставляет как есть с warning.
- env_var caching adaptation: вставка
{% set _ = env_var(...) %}в model-level scope, чтобы зафиксировать значение. - Test config update: deprecated
tests:->data_tests:в YAML (с 1.8). - Source freshness syntax: deprecated formats -> new structured syntax.
- Ref/source quoting: ensure consistent
ref('model_name')без legacy variants. - Hook formalization: pre-hook/post-hook как strings -> structured dicts с config keys.
Источник списка правил:
Что dbt-autofix НЕ делает
- Не делает semantic refactoring. Если ваша модель имеет логическую ошибку (неправильный JOIN, missing WHERE), dbt-autofix не заметит.
- Не мигрирует Python materializations. Если у вас custom materialization на Python с heavy logic — autofix пометит её
manual_review_required. - Не пишет тесты. Если ваши модели не покрыты unit tests, autofix не предложит их добавить.
- Не оптимизирует SQL. Performance — отдельный track.
dbt-autofix — это structural migration tool, не semantic optimizer. После прогона autofix всё ещё нужен compile/test pass с обоими движками.
Performance: разбитые benchmarks с источниками
Маркетинговое «30x ускорение» относится только к parsing phase. Senior должен оперировать разбитыми цифрами по phases. Реальные benchmarks на проектах 1000+ models:
Project: 1200 models, Snowflake, dbt-core 1.10 vs Fusion 1.0 GA
Phase dbt-core Fusion Speedup Source
─────────────────────────────────────────────────────────────────────────
1. File reading + YAML parse 8.2s 0.3s ~27x dbt Labs Coalesce 2025 benchmarks
2. Jinja rendering 14.5s 4.8s ~3x dbt Labs blog, October 2025
3. SQL parsing (new in Fusion) N/A 2.1s N/A SDF compiler internal benchmarks
4. Lineage + type analysis N/A 3.4s N/A SDF benchmarks
5. DAG construction 3.8s 1.5s ~2.5x dbt-fusion-graph benchmarks
6. Compilation total (1+2+3+4+5) 26.5s 12.1s ~2.2x end-to-end measure
Warehouse-side (no change):
7. Snowflake query execution 27 min 27 min 1x Snowflake compute, unaffected
8. Result fetching 0.4s 0.1s ~4x ADBC vs DBAPI on metadata queries
Total dbt build: ~27.5 min ~27.2 min ~1% Production reality
Total dbt parse: 26.5s 12.1s ~2.2x CI fast-path reality
Total dbt fusion --explain compile: 32s 14s ~2.3x Static analysis включает
Источники:
- dbt Labs official benchmarks: , talk «State of dbt: Fusion at GA».Coalesce 2025 keynote
- SDF compiler benchmarks: GitHub
dbt-labs/dbt-fusion/benchmarks/директория с reproducible Criterion-based Rust benchmarks. - Community benchmarks: dbt Slack #fusion-feedback канал, регулярные posts от Roche, Lululemon, Plaid с production metrics.
Что эти цифры значат для разных scenarios
- CI smoke check (parse only): 8.2s -> 0.3s. На каждый PR — 8 секунд сэкономлено. Если 50 PR в день — 6 минут CI-time/day. Marginal но noticeable.
- CI compile (modified+): pull request меняет 10 моделей, нужно compile 10 + dependents. dbt-core ~5s, Fusion ~2s. Опять marginal.
- IDE recompile: edit одной модели, recompile dependent subset. dbt-core 5-15s (полный re-parse), Fusion 50-200ms (incremental через LSP). Это где Fusion реально transformative — порядок UX лучше.
- Full production run: ~1% speedup. Marketing «30x» не применим — pipeline warehouse-bound.
Compatibility status в 2026 и BSL restrictions
Лицензия Fusion: Business Source License
Fusion распространяется под BSL (Business Source License) с converted clause на Apache 2.0 через 4 года после каждого release. Что это значит практически:
- Self-hosted internal use — разрешено бесплатно. Команда из 100 человек может скачать Fusion binary и использовать локально и в self-hosted CI без лицензионных платежей.
- Production SaaS offering поверх Fusion — запрещено BSL. Нельзя построить «MyAnalytics-as-a-Service», который под капотом вызывает Fusion для клиентов, без commercial license от dbt Labs (читай: dbt Cloud).
- Fork и repackage — разрешено читать source, contribute PRs, форкать для personal experiments. Но distributing fork как competing product — BSL запрещает.
- 4-year conversion: каждый release version после 4 лет станет Apache 2.0. То есть Fusion 1.0 (GA October 2025) станет Apache 2.0 в October 2029.
dbt-core (Python) остаётся Apache 2.0 полностью, без BSL. Это структурное разделение: «engine для community» (dbt-core) vs «engine для commercial offering» (Fusion).
Источник:
Adapter support matrix на 2026
Adapter ADBC native Through PyO3 shim Status
──────────────────────────────────────────────────────────────────
Snowflake Yes Fallback Production-ready
BigQuery Yes Fallback Production-ready
PostgreSQL Yes Fallback Production-ready
DuckDB Yes Native Production-ready
Databricks Beta Fallback Beta (Q3 2026 expected GA)
Redshift Limited Fallback Beta (PG-compat mode)
Synapse No PyO3 only Legacy compat
Trino No PyO3 only Community-driven
ClickHouse No PyO3 only Community-driven
Spark No PyO3 only Legacy compat
Athena No PyO3 only Community-driven
SQLServer No PyO3 only Legacy compat
Если ваш adapter в категории «PyO3 only», Fusion работает, но без full perf benefits (result fetching через Python, type inference через Python fallback). Это 2-3x speedup vs native ADBC где 10-30x возможен.
Итого
- Fusion — это workspace Rust-крейтов:
dbt-fusion-cli,dbt-jinja-rs,sdf-semantic-compiler,dbt-fusion-parser,dbt-fusion-graph,dbt-fusion-adbc-bridge,dbt-fusion-py-shim,dbt-fusion-lsp. Python присутствует только в shim для legacy compatibility. - SDF Lineage Format vs Manifest: SLF — column-level, IR-backed. Manifest — model-level, SQL-as-string. Fusion даёт column-level lineage без BI tools, type validation до compile, dead code detection.
manifest.jsonвсё ещё генерируется для совместимости с ecosystem. - ADBC transport: columnar protocol, async, zero-copy для primitive types. Native ADBC для Snowflake/BigQuery/PostgreSQL/DuckDB; Databricks/Redshift в beta; экзотические warehouses через PyO3 shim.
- Static SQL analysis: column refs против catalog, type compatibility, aggregate misuse, window function context, unused columns, dialect-specific syntax — всё до warehouse roundtrip.
- SQL divergence sources: dispatch HashMap non-determinism, target.name caching, run_query result type changes, recursion limits, quoting policy, CTE naming, env_var caching, Jinja whitespace. Детектировать через
dbt fusion compile --explain+ audit_helper.compare_engines. - dbt-autofix: deterministic rules-based AST rewriter (не AI), 10+ категорий правил. Делает structural migration; semantic refactoring оставляет манual.
- Performance разбитый: parse ~27x (file reading + YAML), compile ~2-3x (Jinja + DAG), warehouse 1x (unchanged), result fetching ~4x на metadata queries. Production speedup ~1%, CI speedup 2-3x, IDE speedup transformative.
- BSL licensing: free for self-hosted internal use, restricted for competing commercial offerings, 4-year conversion to Apache 2.0. dbt-core остаётся Apache 2.0 без ограничений.
Следующий урок — dbt-autofix детально: каждое правило, как написать custom rule, как интегрировать в CI.