Skip to content

refactor: host remaining Rust-only shapes, event export and janitor planning natively - #1115

Merged
kvlonge merged 19 commits into
mainfrom
feat/native-group-16
Oct 15, 2026
Merged

kvlonge merged 19 commits into
mainfrom
feat/native-group-16

Conversation

@kvlonge

@kvlonge kvlonge commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Why

Most remaining movable Python was data shapes used only by Rust, plus runtime logic (event export, janitor planning, discovery, Dagster parsing, loader records) that only had to stay near user code or the executor.

Changes

  • About 213 dataclasses, enums and exceptions used only by Rust are now hosted natively; executor-, adapter-, user- and public-facing shapes stay Python with stays entries.
  • Native lifecycle event export: policy, priority queue, routing, drop accounting; user sinks run through small Python shims.
  • Native janitor planning (adapter calls stay callbacks), project module discovery around a module-loading shim, Dagster argument/log/payload parsing, dlt/ingestr loader records.
  • Fixes: no abort at interpreter exit with a slow command-output sink or running SQL test planning; command-output sinks share one thread again; plain compile no longer loads provider settings.

Verification

  • make test and make check-ci passed with all optional dependencies available.
  • Compiler differential: 371 projects, unchanged goldens. Planner differential: 11 projects matched the main baseline.
  • Compile outputs and exit codes match the reference on synthetic and private compile-only projects.
  • Old and new janitor plans, Dagster and loader captures matched; exit stress runs clean.
  • Independent review: no blocking findings.

Add the destination-neutral export policy derived from the lifecycle catalogue,
severity comparison and narrowing, exception-name sanitisation, the bounded
finite-priority queue with displacement, per-sink routing and accounting
snapshots to sqlbuild-core, with unit tests.
Move the event exporter dispatcher (priority queue, per-sink accounting, drain
deadlines, invocation timeouts, failure notifications and periodic and final
health summaries), the export policy, severity narrowing, error-name
sanitisation and the command scope with its context variable into Rust. The
dispatcher hosts its work on named Python daemon threads under a Python
condition, joins its health thread at shutdown and stops its threads at
interpreter exit. Each lifecycle sink call runs on a thread whose target is
the Python invocation shim, so user code never runs above native frames that
finalization would abort.

Python keeps the public sink decorators and readers, the shapes re-exported by
sqlbuild.sinks and the two shims that call user sink functions.
A command-output sink still running when the interpreter finalized ran above
native frames on the capture worker thread, and CPython ending that daemon
thread aborted the process. Each command-output sink call now runs on its own
daemon thread whose target is the Python invocation shim, which reports
completion back to the native export. Writes from those threads stay
uncaptured. At exit, hooks set a shared exit signal that ends native waits,
then stop and join the capture worker and the event exporter threads with a
bounded wait. Delivered records and accounting are unchanged.
When planning failed before the SQL test plan was awaited, the background
planning thread could still be running native code as the interpreter
finalized, and CPython ending that daemon thread aborted the process instead
of reporting the planning error. Execution planning now joins the planning
thread on every exit path, and an exit hook joins any still-running planning
thread with a bounded wait. Planned output is unchanged.
…cher

Each command-output sink call started its own daemon thread, so a provider
used only by output sinks that creates a thread-bound resource on first use
(such as a SQLite connection) failed on the next record. All command-output
sink calls for a dispatcher now run in order on a single long-lived thread
whose target is a Python shim waiting on a Python queue, so no native frame
is on that thread when interpreter finalization can end it. Records,
ordering, failure counting, filtering and provider teardown are unchanged.
@kvlonge
kvlonge enabled auto-merge (squash) October 15, 2026 15:57
@kvlonge
kvlonge merged commit bbd4e53 into main Oct 15, 2026
40 checks passed
@kvlonge
kvlonge deleted the feat/native-group-16 branch October 15, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant