Loud fail for missing build tools; fix worker payload serialization on editable installs; mode-independent run-stats block counts - #70
Open
rhoadesScholar wants to merge 3 commits into
Open
Conversation
…lding daisy if necessary utilities are missing
The serializer fix was only covered incidentally (tests that fail only under editable installs); test_worker_serialization.py now forces the by-value failure mode deterministically with a synthetic module, and covers the daisy-module-global shape, the __main__ end-to-end path, the eager-failure guidance, and _PicklableLocal. Thread-mode block counting regained a pinned test after the run-stats tests were unpinned to the default subprocess mode. Rust side: unit tests for RunTally counting and the build_run_stats merge (including synthetic external-worker entries), bookkeeper registration lifecycle and recycled-port hygiene, and the TCP integration test now asserts external registered workers appear in run stats. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KxYd8bCjgZLoESSe3smbwF
rhoadesScholar
force-pushed
the
v2.0_patch
branch
from
July 28, 2026 17:33
dd8cb9f to
9f6bf9f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three things: a loud, actionable failure when build tooling is missing; a fix for subprocess-worker payload serialization breaking on editable/source-tree installs; and correct
blocks_processedrun stats across all worker execution modes.1. Loud fail for missing install tools (
357d040)Building daisy from source requires a C linker and the Rust toolchain, and the stock maturin error when they're missing is cryptic.
build_wrapper.pynow wraps maturin's PEP 517 hooks (build_wheel/build_editable) and checks forcc/gcc/clangandcargoup front, failing with per-platform install instructions instead.pyproject.tomlalso gainsdev,examples, anddocsextras so pip users get the same dependency groups uv users had.2. Fix: subprocess-worker payloads no longer break on editable installs (
ce3f488)Symptom: with daisy installed editable (or from a source tree),
run_blockwisefailed at startup withTypeError: cannot pickle '_thread._local' objectfor any 1-arg block function that references thedaisymodule as a global (e.g.block.status = daisy.BlockStatus.SUCCESS).Root cause: subprocess workers (the default execution mode) serialize the block function with
dill.dumps(..., recurse=True), which ships the globals the function references. dill pickles any module outsidesys.prefix/site-packages by value — its entire__dict__— so one unpicklable module-level object anywhere in the namespace fails the whole payload. daisy itself contains two (daisy.logging._active, athreading.local, anddaisy._worker_processes._LEN, astruct.Struct). Wheel installs never trip this, which is why CI stayed green; editable installs always do.Fix:
_serializenow uses adill.Picklersubclass that pickles every importable module by reference (_make_modules_by_ref_pickler). This is sound because the child replicates the parent'ssys.pathbefore deserializing (read_payload), so anything importable in the parent is importable in the child. Only__main__(and__mp_main__) keep dill's by-value path — the child's__main__is the worker shim, so script/notebook globals must ride in the payload, and they still do (recurse=Trueis load-bearing and untouched). This also fixes the same failure for user packages installed editable with unpicklable module-level state.Also:
daisy.logging._activeis now a_PicklableLocal(pickles as a fresh, empty local) as defense-in-depth for other transports.Task(worker_processes=False)) instead of a bare dill traceback.3. Fix:
blocks_processedcounted server-side, correct in every mode (ce3f488)Symptom: the Resource Utilization report showed
blocks 0per task (and empty per-worker counts) for subprocess-mode runs — the default — andtests/test_run_stats.pyhad to pin tests to thread mode. This was the documented gap inRUN_STATS.md.Root cause:
WorkerStats.blocks_processedwas incremented only inside the in-process worker thread's block loop. Subprocess shim workers and external cluster workers process blocks where the stats layer never looked.Fix: counting moves to the server, where every worker's block returns already arrive over TCP:
Register { task_id, worker_id }, sent byClient::connectright after the connection opens (appended as the last enum variant, so existing message discriminants are unchanged on the wire).ReleaseBlockorBlockFailed) increments aRunTallycounter per task and per worker.build_run_statsmerges the per-worker counts into the exit-channelWorkerStatsby(task_id, worker_id); registered workers with no thread in the server process (fully external workers) get synthetic entries carrying their block counts.Semantics notes:
Internal API change:
Client::connectand_rs.SyncClientnow take aworker_id(both internal;daisy.Clientreads it fromDAISY_CONTEXTas before).docs/source/design/RUN_STATS.mdis updated — the "known gap" section is replaced by the Register design, withdaisy.profile_blockkept as the future path for per-block CPU/RSS.Tests (
9f6bf9f)tests/test_worker_serialization.py(new): forces the by-value failure mode deterministically with a synthetic module carrying athreading.local+struct.Struct(so coverage doesn't depend on install layout), the daisy-module-global shape,__main__-defined function end-to-end through real subprocess workers, eager-failure guidance, and_PicklableLocalround-trip.tests/test_run_stats.py: the previously pinned tests now run the default subprocess mode and theirblocks_processedassertions pass; a new pinned test keeps thread-mode counting covered.RunTallycounting and thebuild_run_statsmerge (including synthetic external-worker entries and the unregistered-client fallback); bookkeeper registration lifecycle + recycled-port hygiene; the TCP integration test now asserts externally-connected registered workers appear in run stats.cargo test -p daisy-core46 passed;pytest tests/204 passed, 1 xfailed (the intentionaldaisy.messagesdocumentation xfail).🤖 Generated with Claude Code
https://claude.ai/code/session_01KxYd8bCjgZLoESSe3smbwF