You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Discovered while loading the simulation fixture into a local debug database at Simulation__HistoryDays=730.
What happens
seed --profile simulation exits 1 with a count mismatch that is only ever about the daily-entry lifecycle split:
Simulation seed completion check failed — the seed is short/partial and must NOT be marked complete:
… dailyEntries.submitted: expected 10, got 1070; dailyEntries.locked: expected 1446, got 633 …
DailyEntryLockSweep.LockDueEntriesAsync takes .Take(BatchSize) with BatchSize = 200 (DailyEntryLockSweep.cs:21) — one call locks at most 200 entries and returns; the sweep is designed to be re-polled by DurableJobWorker, which never starts in a run-then-exit verb.
ExpectedLockedEntryCount mirrors the sweep’s cutoff rule and therefore expects every eligible entry locked: FlockTopologyCount × (HistoryDays − LockAfterDays).
Those agree only while 2 × (HistoryDays − 7) ≤ 200, i.e. HistoryDays ≤ 107. Above that the assertion can never be satisfied, on any database, however clean.
The class header at SimulationDataSeeder.cs:1890 claims the expectations are "depth-robust by construction". They are, arithmetically — but the seeding is not, so the claim reads as broader than it is.
Blast radius today
Low, and that is why it went unnoticed: the harness default is HistoryDays=90 and the test fixture is 12, both under the ceiling. It only bites someone deliberately asking for a deep fixture.
Note the failure is also post-commit: the check runs in EmitManifestAsync after every row is written, in no transaction, so an over-ceiling run still leaves its data behind — just unlocked past the first 200, with no manifest and a red exit.
Suggested fix
Drain the sweep in the seeder rather than assuming one pass is enough — re-invoke until a pass locks nothing, with a bound so a genuine bug cannot spin forever. Do not raise BatchSize: that is a serving-path constant sized for the background worker, and changing it to satisfy a seeder would move the ceiling rather than remove it.
Then add a test at a depth above the current ceiling (e.g. HistoryDays=120) that asserts a green seed — one that fails today.
Docs
docs/runbooks/simulation-fixture-on-a-dev-database.md documents the ceiling and points here. Remove that section when this closes.
Two follow-ups found while drafting the runbook — both lower the severity, neither removes the defect.
The ceiling is per run, not per database. Each invocation locks up to 200, so a re-run at a deeper HistoryDays only needs to lock what the increase newly made eligible, 2 × (new − old). Ramping in steps of ≤100 days (90 → 190 → 290 → …) converges green at any depth on the same durable anchor.
And an overshot run is recoverable by repetition. The validation throws in EmitManifestAsync, after every row is committed and outside any transaction, so the rows survive the red exit; re-running the identical command locks the next 200. About 2 × (HistoryDays − 7) ÷ 200 runs walks it green.
Both are read off the code, not yet observed on a real run — the runbook says so and its drill exercises them.
So the user-visible cost is a confusing red exit and a manual ramp, not an unreachable depth. The fix (drain the sweep in SeedAsync) is still worth doing: nothing about the failure message points at the ramp, and the "depth-robust by construction" comment at SimulationDataSeeder.cs:1890 actively points away from it.
Activity
mforce commentedon Sep 1, 2026
Two follow-ups found while drafting the runbook — both lower the severity, neither removes the defect.
The ceiling is per run, not per database. Each invocation locks up to 200, so a re-run at a deeper
HistoryDaysonly needs to lock what the increase newly made eligible,2 × (new − old). Ramping in steps of ≤100 days (90 → 190 → 290 → …) converges green at any depth on the same durable anchor.And an overshot run is recoverable by repetition. The validation throws in
EmitManifestAsync, after every row is committed and outside any transaction, so the rows survive the red exit; re-running the identical command locks the next 200. About2 × (HistoryDays − 7) ÷ 200runs walks it green.Both are read off the code, not yet observed on a real run — the runbook says so and its drill exercises them.
So the user-visible cost is a confusing red exit and a manual ramp, not an unreachable depth. The fix (drain the sweep in
SeedAsync) is still worth doing: nothing about the failure message points at the ramp, and the "depth-robust by construction" comment atSimulationDataSeeder.cs:1890actively points away from it.docs: add runbook for loading the simulation fixture into a dev datab…
fix(seed): drain the daily-entry lock sweep so deep simulation fixtur…
chore(main): release 0.1.0 (#542)