Skip to content

Commit d2b2c41

Browse files
ahrzbclaude
andcommitted
fix(bench): refuse debug native builds before timing
tests/_native_guard.py rebuilds the cwd-local _interpreter.pyd via plain `maturin develop` (debug) whenever src/*.rs is newer; the cwd package shadows the venv release wheel, so a bench run after a Rust edit + pytest silently timed a DEBUG engine -- engine rows inflated ~5x while python/duckdb rows stayed sane (measured 2026-07-26). The extension now exposes BUILD_PROFILE from cfg!(debug_assertions); bench_serving exits with a rebuild hint unless it reads release, and the native guard's rebuild message names the profile it builds. Verified: debug build -> clear refusal (exit 1); release build -> normal run; mise run gate-specializer green (129 cargo + 753 pytest). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 1856658 commit d2b2c41

3 files changed

Lines changed: 34 additions & 2 deletions

File tree

benchmarks/bench_serving.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
Run: uv run python -m benchmarks.bench_serving [--json out.json]
2525
IMPORTANT: rebuild the wheel first (uv run --reinstall-package sql-transform
2626
python -c pass) — a stale wheel inflates ONLY the engine rows and once
27-
produced a phantom 7x regression (caught by bisection, 2026-07-26).
27+
produced a phantom 7x regression (caught by bisection, 2026-07-26). Debug
28+
builds (the native test guard's `maturin develop` shadowing the release
29+
wheel) are refused automatically before any timing.
2830
"""
2931

3032
from __future__ import annotations
@@ -140,6 +142,24 @@ def engine_run(engines):
140142
print(json.dumps(results))
141143

142144

145+
def refuse_debug_build():
146+
"""Abort unless the imported native extension is a release build.
147+
148+
tests/_native_guard.py rebuilds the cwd-local .pyd via plain `maturin
149+
develop` (debug) whenever src/*.rs is newer; that shadows the venv's
150+
release wheel and silently inflates engine rows ~5x (measured 2026-07-26).
151+
"""
152+
from sql_transform import _interpreter
153+
154+
profile = getattr(_interpreter, "BUILD_PROFILE", None)
155+
if profile != "release":
156+
sys.exit(
157+
f"bench_serving: refusing to time a non-release native build "
158+
f"(BUILD_PROFILE={profile!r}, loaded from {_interpreter.__file__}).\n"
159+
f"Rebuild with: uv run maturin develop --release"
160+
)
161+
162+
143163
def orchestrate():
144164
print("parity gate:", flush=True)
145165
for mod in map(sc.load, scenario_names()):
@@ -192,6 +212,7 @@ def orchestrate():
192212

193213

194214
if __name__ == "__main__":
215+
refuse_debug_build()
195216
if "--engine-run" in sys.argv:
196217
engine_run(os.environ["BENCH_ENGINES"].split(","))
197218
else:

src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,15 @@ mod value;
2525
fn _interpreter(m: &Bound<'_, PyModule>) -> PyResult<()> {
2626
m.add_class::<datafusion::InferFn>()?;
2727
m.add_class::<duckdb::DuckDBInferFn>()?;
28+
// Lets benchmarks refuse an unoptimized build (a `maturin develop` debug
29+
// .pyd shadowing the release wheel once inflated engine rows ~5x).
30+
m.add(
31+
"BUILD_PROFILE",
32+
if cfg!(debug_assertions) {
33+
"debug"
34+
} else {
35+
"release"
36+
},
37+
)?;
2838
Ok(())
2939
}

tests/_native_guard.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ def ensure_native_built() -> None:
7373
return
7474
print(
7575
"native guard: src/*.rs newer than _interpreter -- rebuilding "
76-
"(maturin develop)...",
76+
"(maturin develop, DEBUG profile; benchmarks will refuse this build "
77+
"-- rerun with --release before timing)...",
7778
file=sys.stderr,
7879
)
7980
# noqa justification: maturin is the venv's own executable and _REPO derives

0 commit comments

Comments
 (0)