Skip to content

POC: Nuitka compilation of MetricFlow for Fusion (dbt-core v2) integration - #2069

Draft
QMalcolm wants to merge 5 commits into
mainfrom
qmalcolm--nuitka-poc
Draft

POC: Nuitka compilation of MetricFlow for Fusion (dbt-core v2) integration#2069
QMalcolm wants to merge 5 commits into
mainfrom
qmalcolm--nuitka-poc

Conversation

@QMalcolm

Copy link
Copy Markdown
Contributor

What this is

dbt-core v2 (Fusion) is written in Rust. We need a way to call Python MetricFlow from Fusion without maintaining a separate Rust implementation of the query compiler — two implementations will inevitably diverge.

This PR validates the Nuitka sidecar as the integration strategy: compile Python MetricFlow into a standalone native binary (bundling CPython + all dependencies), then have Fusion spawn it as a subprocess and communicate over JSON IPC. See `pyo3-vs-nuitka-analysis.md` for the full PyO3 vs. Nuitka comparison and rationale.

The PoC answers one binary question: does a Nuitka-compiled MetricFlow binary produce identical SQL to the Python reference? It passed on macOS arm64.

What's in this PR

  • `pyo3-vs-nuitka-analysis.md` — architectural analysis comparing PyO3 embedding vs. Nuitka sidecar; recommends Nuitka
  • `poc/nuitka_poc.py` — minimal script that loads `sg_00_minimal_manifest`, calls `MetricFlowEngine.explain()` with a stub SQL client (no real DB connection), and prints the compiled SQL to stdout
  • `pyproject.toml` — new `nuitka-build` hatch environment with `compile` and `validate` scripts

How to run the PoC yourself

Prerequisites: the `nuitka-build` hatch env will be created automatically on first use. First run takes ~5 minutes as Nuitka compiles ~1500 C files (subsequent runs use ccache).

# 1. Check out the branch
git checkout qmalcolm--nuitka-poc

# 2. Compile MetricFlow to a standalone binary
#    Output: poc/nuitka_poc.dist/nuitka_poc.bin  (macOS)
#            poc/nuitka_poc.dist/nuitka_poc       (Linux)
hatch run nuitka-build:compile

# 3. Validate — runs Python reference, runs binary, diffs the SQL output
#    No output = clean diff = pass
hatch run nuitka-build:validate

# Or run the steps manually:
MANIFEST=metricflow_semantics/test_helpers/semantic_manifest_yamls/sg_00_minimal_manifest
hatch run nuitka-build:python poc/nuitka_poc.py --manifest-dir $MANIFEST
poc/nuitka_poc.dist/nuitka_poc.bin --manifest-dir $MANIFEST   # macOS

What this is NOT

  • Not a production artifact
  • Not an IPC server (that's `mf_entry.py`, the next step)
  • Not integrated with Fusion in any way

Next steps (not in this PR)

  1. Design `mf_entry.py` — stdin/stdout NDJSON IPC entry point wrapping `MetricFlowEngine`, owned by this repo
  2. Build `MetricFlowSidecarClient` in Fusion using the existing `SidecarClient` trait, initially using regular Python subprocess (no Nuitka required)
  3. Validate end-to-end with Fusion, then swap in the Nuitka binary for hermetic distribution

QMalcolm added 5 commits June 15, 2026 15:20
Fusion (dbt-core v2) is Rust-based. The existing crates/dbt-metricflow
crate is explicitly non-viable long-term — two independent query compiler
implementations will inevitably diverge. Python MetricFlow must remain
authoritative.

Two embedding strategies were evaluated: PyO3 embedding (link libpython
into Fusion, call MetricFlow in-process) and Nuitka sidecar (compile
MetricFlow to a standalone native binary, invoke via subprocess with JSON
IPC). Full analysis in pyo3-vs-nuitka-analysis.md.

Nuitka sidecar is recommended. Decisive factors against PyO3:
- CPython must init on Fusion's main thread before Tokio starts — UB if
  called from a worker thread, requiring Fusion startup changes.
- pydantic-core ABI conflict risk: two PyO3 runtimes in one process.
- GIL serializes all concurrent MetricFlow compilations.
- A MetricFlow crash kills the entire Fusion process.

Nuitka sidecar avoids all of these: crash isolation via subprocess EOF
detection, true concurrency via a process pool, hermetic CPython bundle,
and it extends Fusion's existing SidecarClient infrastructure rather than
introducing a new pattern.

poc/nuitka_poc.py is the empirical validation step. It loads
sg_00_minimal_manifest, calls explain() via a _StubSqlClient (DuckDB
renderer only, no real DB connection), and prints compiled SQL to stdout.
Validation: diff Python output against the Nuitka binary output — a
non-empty diff indicates silent miscompilation in pydantic v2 validators
or fast_frozen_dataclass dynamic __hash__ replacement.

The PoC is intentionally minimal: no IPC, no execution, no Fusion
integration. Nuitka compilation and diff validation are the next step.
Adds a dedicated hatch env for the Nuitka compilation PoC rather than
installing Nuitka into the main dev environment. Nuitka is a build tool
(invokes a C compiler to produce a standalone binary) — it has no role
in day-to-day MetricFlow development or testing, so it doesn't belong
in dev-env alongside ruff/black/mypy.

The nuitka-build env templates off dev-env (inheriting the full
MetricFlow dependency set) and adds nuitka via extra-dependencies, so
the compiled binary sees the same package versions as the Python
reference run.

Scripts:
  hatch run nuitka-build:compile   — build the standalone binary
  hatch run nuitka-build:validate  — run reference + binary, diff output
The sync_dsi.py migration renamed all dbt_semantic_interfaces imports to
metricflow_semantic_interfaces, but missed this docstring. Stale package
name left after the MSI extraction.
Nothing in the metricflow codebase imports dbt_semantic_interfaces
directly — the MSI extraction replaced all such imports with
metricflow_semantic_interfaces. Passing --include-package for a package
that isn't installed (and isn't needed) caused Nuitka to fail hard at
startup. Removed; --follow-imports handles all reachable packages
automatically under --standalone.
Nuitka appends .bin to the output binary on macOS (nuitka_poc.bin),
not the extensionless name used on Linux. The validate script was
referencing the Linux path and failing with "No such file or directory".
@cla-bot cla-bot Bot added the cla:yes label Jun 15, 2026
@github-actions

Copy link
Copy Markdown

Thank you for your pull request! We could not find a changelog entry for this change. For details on how to document a change, see the contributing guide.

1 similar comment
@github-actions

Copy link
Copy Markdown

Thank you for your pull request! We could not find a changelog entry for this change. For details on how to document a change, see the contributing guide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant