Skip to content

test-flake: Story243 RendersRideThePerUnitBudget is still a wall-clock race (Orchestration FakeTimeProvider has no timers) β€” red on the js-yaml dependabot PR #716Β #723

Description

@genwave-radio

πŸ› What happened

PR #716 (dependabot, admin-ui/package-lock.json only β€” cannot touch .NET) went red on build-test, run 34435202778 attempt 1:

Failed GenWave.Orchestration.Tests.Specs.FeatureDjsHandOffAudibly+ScenarioBlurbCachePosture.RendersRideThePerUnitBudget [271 ms]
Assert.DoesNotContain() Failure: Filter matched in collection  … MediaItem { MediaId = tts:signon-2, … SegmentKind = SignOn }
   at …/tests/GenWave.Orchestration.Tests/Specs/Story243_DjsHandOffAudibly.cs:line 508

The sign-off was dropped, the sign-on aired. It ran at 00:00:01.55 of the Orchestration run β€” the runner's cold first second.

πŸ” Why β€” the race is still wall-clock

The fact arranges a 10 ms render budget against a 200 ms fake render and expects both handoff pieces dropped. The orchestrator's race is Task.WhenAny(renderTask, Task.Delay(renderBudget, timeProvider, ct)) (src/GenWave.Orchestration/Orchestrator.cs:1624), which gh-#554 made ride the injected clock so a spec can decide it by fake due-order. But:

  • Orchestration.Tests' own FakeTimeProvider (tests/GenWave.Orchestration.Tests/Fakes/FakeTimeProvider.cs) overrides only GetUtcNow. No CreateTimer, so the budget delay falls through to the base TimeProvider's real System.Threading.Timer.
  • FakeTtsSegmentSource.RenderDelay is a real await Task.Delay(200 ms).

So it is a 10 ms timer versus a 200 ms delay on the wall clock, and both pieces are kicked before the first is awaited (Kick at :1173, the await loop at :1617). Any ~190 ms stall between the kick and the second WhenAny (cold JIT of the first drop's logging/booth-log path, a GC pause, a busy runner) lets the sign-on render finish first, and it airs. The #554 fix went into Host.Tests (tests/GenWave.Host.Tests/Specs/Story139_SettingsSurfaceCompletion.cs: Microsoft's FakeTimeProvider + a DelayableTtsSegmentSource(time) whose delay rides the same clock); this Orchestration fact never got the same treatment.

πŸ”§ Fix

Port the Story139 arrangement: Microsoft.Extensions.TimeProvider.Testing into Orchestration.Tests (dotnet add package), and a render double whose delay rides that clock, so the race is decided by due-order and the budget-drop fact cannot be load-sensitive. The hand-rolled FakeTimeProvider is shared by every Orchestration spec β€” either give it timers or swap it project-wide.

πŸ‘ͺ Same family

tests/GenWave.Tts.Tests/Specs/Story390_AdScriptWriter.cs:454 ASlowFirstDraftDoesNotStarveTheReask β€” a real 3 s budget against a mock HTTP server's 1.8 s delay. Reds about 1-in-2 on a loaded dev box; the sponsors build loop carried a standing "rerun once" exemption for it through every gate, which means a real regression in that fact would be waved through as the known flake. HttpClient timeouts are wall-clock by nature, so either drive the attempt budget off a TimeProvider (CancellationTokenSource(delay, timeProvider)) and fake it, or widen the ratio to one load cannot erase.

Rerun of #716's job: attempt 2 (result on the PR).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium prioritybugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions