Repository navigation
fix: reseed shared sampler RNGs in forked children - #1077
binaryaaron wants to merge 1 commit into
Conversation
Samplers draw from numpy's global RandomState and Faker's shared Random. CPython reseeds the stdlib random module after fork but not these, so every child forked after a parent run generated the same sampler data. An after-in-child fork hook now reseeds both from OS entropy, matching CPython's behavior for random. Seeding in the process is unchanged, and the parent's RNG is not touched. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
Code Review: PR #1077 — fix: reseed shared sampler RNGs in forked children
SummaryThe PR registers an Verified locally (CPython 3.14.3, numpy 2.4.3):
FindingsMedium / Low
Nits
Positives
Structural Impact (graphify 0.9.90, 8.3s)Risk: LOW (localized change)
High-Connectivity Changes
The change adds a module-level side effect (registering the at-fork hook) to a module with many dependents. It doesn't change any API or import direction. Engine → config layering is kept, and the hook doesn't import anything new. VerdictLooks good, with one small fix suggested. The fix is correct and well tested. Before merging, I'd recommend guarding on |
[Medium impact] The PR appears safe to merge; no actionable defects were established. SummaryThe PR reseeds shared NumPy and Faker generators after a fork without changing the parent’s generators.
No actionable issues were established. Tests were not run during this review. Reviews (1) · Last reviewed commit: "fix: reseed shared sampler RNGs in forke..." · Reviewed by Greptile |
📋 Summary
Children forked after a parent run generated identical sampler data, because samplers draw from numpy's global
RandomStateand Faker's sharedRandom, and a fork copies their state. This PR adds anos.register_at_fork(after_in_child=...)hook that reseeds both from OS entropy in the child, as CPython already does for the stdlibrandommodule.🔗 Related Issue
Fixes #1076
🔄 Changes
sampling_gen/utils.py:_reseed_shared_rngs_after_fork()callsnumpy.random.seed()andfaker.generator.random.seed()in a forked child. It reseeds only modules already imported, so the hook adds no imports. It is registered next tocheck_random_state(), whichdata_designer.interfaceimports, so the hook is in place before a user can fork.architecture/sampling.md: a design-decision entry on shared random state, in-process seeding and the fork hook.In-process seeding is unchanged:
np.random.seed(n)andFaker.seed(n)still make sampler output repeatable in one process, whichscripts/benchmarks/benchmark_engine_v2.pyrelies on. The parent's generators are not touched. A forked child that needs repeatable output seeds itself after the fork, as with the stdlibrandommodule.I considered giving each run its own generator seeded from OS entropy instead of reusing the global one. That fixes forks too, but breaks repeatable output for anyone who seeds
numpy.random, including the benchmark, so I kept the global generators and reseed only in the child.🧪 Testing
make testpasses: one pre-existing failure, see belowtest_forked_children_draw_different_samples(engine)test_forked_child_reseed_does_not_change_parent_draws(engine)test_sibling_forked_children_generate_different_sampler_data(interface)preview()generate different category and uniform columnsThe fork tests run in a fresh interpreter through the existing helpers (
tests/engine/fork_probe.py, and the subprocess pattern intest_fork_safety.py).make test(CPython 3.14.0, uv 0.13.4): config 671 passed, engine 2610 passed, data-designer 1204 passed, 3 skipped, and one failure,test_create_dataset_e2e_with_file_contents_seed_source_unreadable_file_raises_generation_error, which fails on unmodifiedmainwhen run as root. Ruff check and format pass on the changed files.✅ Checklist
triaged: N/A🤖 Generated with Claude Code