Audit/reproducibility types: emit seed and RNG state per record
Maintainers usually reply within 4 days
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
Research direction
Start by reading core/src/main/java/com/datagenerator/core/seed/RandomProvider.java and generators/src/main/java/com/datagenerator/generators/semantic/FakerCache.java, then review docs/internal/CONTAINER-CICD-IDEAS.md. The issue is still a design discussion: decide the seed outputs and RNG-state granularity, then validate whether the relevant Datafaker providers can be replayed from RNG state. Done means those decisions and validation are agreed before implementation.
Written by the indexing model from the issue text.
Description
Summary
Two new field types for audit / reproducibility, so a generated record can carry the information needed to trace and re-generate it:
seed— emit the seed that produced the record.rngstate(working name) — emit the RNG state at the point the record was produced, so the exact value(s) can be re-generated later.
Opening this to capture the design questions and the grounded technical findings, then discuss here before implementing.
Current seeding architecture (grounded in code)
core/.../seed/RandomProvider.java- one global
masterSeed(from job config / CLI--seed). - per worker thread:
deriveSeed(masterSeed, workerId)whereworkerIdis a logical id (0,1,2…) assigned viaAtomicIntegeron firstgetRandom()— deliberately not the JVM thread id (see class Javadoc), so runs are reproducible. - each worker gets
new Random(threadSeed)— plainjava.util.Random(48-bit LCG, state is a singlelong), held in aThreadLocal.
- one global
generators/.../semantic/FakerCache.javanew Faker(locale, THREAD_RANDOM.get())— Datafaker holds a reference to our thread-localRandom. So primitive generators and Datafaker draw from the same per-worker stream.
Consequences for this feature:
- The reproducibility coordinates of a record are essentially (masterSeed, workerId, draw-position-in-that-worker's-stream).
threadSeedis fully derivable from(masterSeed, workerId)viaderiveSeed, so it is redundant with those two.
Type 1 — seed
Question: global seed, thread seed, or two types?
Observations:
- The global masterSeed is constant for the whole run — already known from the job config/CLI. Emitting it per record is cheap but low-information (same on every row).
- The per-worker derived seed (or equivalently the
workerId) is the bit that varies and actually identifies which deterministic sub-stream produced the row.
Proposal to discuss:
- A single
seedtype with a selector, e.g.seed[master]andseed[worker](and maybeseed[worker_id]), rather than two separate types — keeps the type system smaller. - Minimum useful audit datum = masterSeed + workerId; from those the worker's
Randomstart state is reconstructable.threadSeedcould be a convenience output but is derivable.
Open: do we want all three (master / worker_id / derived_thread_seed), or just master + worker_id?
Type 2 — rngstate (emit RNG state to re-generate the value)
Main question: can we, in Java, restore a Random to a state that reproduces the same value?
Yes, for java.util.Random (which is what this project uses):
- Its entire state is a single 48-bit value in the private
seed(AtomicLong) field. - Capture: read that field (reflection) — or better, use a small
Randomsubclass exposinggetState()/setState(). - Restore caveat:
setSeed(s)does not restore raw state — the constructor/setSeedapplyinitialScramble((s ^ 0x5DEECE66DL) & mask). To restore exact state you must write the raw 48-bit internal value back (reflection or the subclass), not callsetSeed. - After restoring, the next
nextX()reproduces the same draw. Confirmed feasible. - (
SplittableRandomwould be harder — no state accessors. Sticking withjava.util.Randomkeeps this tractable. A purpose-builtRandomsubclass is the cleanest path and avoids reflection.)
Can we do it for Datafaker?
- Likely yes, because
Fakerholds a reference to ourRandom(seeFakerCache). If we restore thatRandom's internal state to the point immediately before a Faker call and replay the same call, Datafaker should consume the same draws and produce the same fake value — Datafaker value generation is a function of the injectedRandom. - Caveats to validate (the discussion meat):
- Capture granularity. A record has many fields; each field consumes a variable number of draws (random-length strings, regex, etc.). One state snapshot before the whole record reproduces the entire record only if all fields are replayed in the same order. Reproducing a single field needs a per-field snapshot. Which granularity do we want to emit — per record, or per field?
- Shared interleaved stream. Primitives and Faker share the worker's
Random. Reproduction must replay the same interleaving of primitive + Faker draws. - Non-RNG state in Datafaker providers. Features like
unique/distinct, options caches, or any provider holding internal mutable state beyond theRandomwould not be reproduced by RNG-state restore alone. Need to confirm which providers (if any) we use that violate "pure function of the injected Random." - Storage form. The state is a 48-bit long → emit as a long/hex string. Cheap.
- Thread-safety of capture. Snapshotting mid-generation on the worker thread (synchronous to generation) is fine; doing it off-thread is not.
Open questions for discussion
seedtype: one type withmaster/worker/worker_idselectors, or separate types? Which outputs are actually worth emitting giventhreadSeedis derivable?rngstate: capture granularity — per record (reproduce the whole row) vs per field (reproduce one value)? This drives where we snapshot.- Implementation of state access: dedicated
Randomsubclass withgetState()/setState()(preferred, no reflection) vs reflection onjava.util.Random.seed. - Verify Datafaker providers in use are pure functions of the injected
Random(nounique/options/internal caches) — otherwise restore won't reproduce. - Interaction with the container/CI work: this is exactly the "record self-documents what produced it" idea parked in
docs/internal/CONTAINER-CICD-IDEAS.md(Future enhancements) — supersedes the earlier "echo the resolved seed to logs" half-measure.
Refs: core/src/main/java/com/datagenerator/core/seed/RandomProvider.java, generators/src/main/java/com/datagenerator/generators/semantic/FakerCache.java.
- Dominant language
- Java
- Stars
- 3
- Forks
- 4
- Avg merge
- 8h 10m
- Merged PRs (30d)
- 9
Getting set up
- Ships a Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from mferretti/SeedStream
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
mferretti/SeedStream#443 ·
Maintainers usually reply within 4 days
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
mferretti/SeedStream#438 ·
Maintainers usually reply within 4 days
-
enhancement
Difficulty 4/5 3-5 days Newbie friendliness 25/100
mferretti/SeedStream#442 ·
Maintainers usually reply within 4 days
-
enhancement
Difficulty 3/5 1-2 days Newbie friendliness 45/100
mferretti/SeedStream#441 ·
Maintainers usually reply within 4 days
-
bug
Difficulty 3/5 1-2 days Newbie friendliness 45/100
mferretti/SeedStream#440 ·
Maintainers usually reply within 4 days
All issues in mferretti/SeedStream
Similar issues
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
apache/maven-enforcer#1074 ·
Maintainers usually reply within 1 day
-
Gradle liquibaseDiffChangelog drops jhi_date_time_wrapperPossibly taken @mraible claimed this today. Openarea: triage theme: undefined
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
jhipster/generator-jhipster#35998 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
openai/openai-java#1337 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
checkstyle/checkstyle#22447 ·
Maintainers usually reply within 1 day