Skip to content

fix(test): de-flake TestParallelismUpdate on coarse-resolution clocks#16224

Open
Joibel wants to merge 1 commit into
argoproj:mainfrom
pipekit:fix-flaky-parallelism-update
Open

fix(test): de-flake TestParallelismUpdate on coarse-resolution clocks#16224
Joibel wants to merge 1 commit into
argoproj:mainfrom
pipekit:fix-flaky-parallelism-update

Conversation

@Joibel

@Joibel Joibel commented Jun 4, 2026

Copy link
Copy Markdown
Member

Motivation

workflow/sync.TestParallelismUpdate fails intermittently on Windows CI:

--- FAIL: TestParallelismUpdate (0.00s)
    multi_throttler_test.go:225: Error: Should be true   // Admit("e/0")
    multi_throttler_test.go:226: Error: Should be false  // Admit("f/0")

The test adds six items — a/0f/0each in its own namespace, all
with priority 0 and creationTime = time.Now(), then raises parallelism
and expects admission to follow insertion order (e before f).

Because each item is in a distinct namespace, queueThrottled ranks the
candidates across namespaces by (priority, creationTime), iterating the
pending map. The items only differ by creationTime, so ordering depends
entirely on those timestamps being distinct.

On clocks with coarse resolution (notably Windows, ~1–15 ms), consecutive
time.Now() calls can return the same instant. The resulting
creationTime ties are then broken by Go's randomized map-iteration order,
so f/0 can win the freed slot instead of e/0, and the test fails. Linux's
nanosecond-resolution clock hides the bug, which is why it only shows up on
the Windows runner.

This was reproduced deterministically on Linux by forcing identical
timestamps: with ties, the test fails with exactly the CI symptom
(e admitted=false, f admitted=true) on a fraction of runs.

Modifications

Give the six items strictly increasing creationTimes (now, now+1ms, …
now+5ms) so the ordering is unambiguous on every platform, regardless of
clock resolution. Added a comment explaining why bare time.Now() is unsafe
here.

This is a test-only change — no production code is touched.

Verification

  • go test -run TestParallelismUpdate -count=200 ./workflow/sync/ — passes
    (was non-deterministic under tied timestamps).
  • The other tests in multi_throttler_test.go are unaffected: their tied
    items sit within a single namespace (stable heap insertion order) or are
    separated by distinct priorities / time offsets.
  • golangci-lint clean, gofmt clean.

🤖 Generated with Claude Code

TestParallelismUpdate adds six items, each in its own namespace, all with
priority 0 and creationTime=time.Now(). When parallelism is raised it
expects admission to follow insertion order (a..f). Because every item is
in a distinct namespace, queueThrottled ranks the candidates across
namespaces by (priority, creationTime), iterating the pending map. On
clocks with coarse resolution (e.g. Windows CI), consecutive time.Now()
calls can return identical instants; the resulting creationTime ties are
then broken by Go's randomized map-iteration order, so the wrong item can
win the freed slot and the test fails intermittently.

Give the items strictly increasing creationTimes so ordering is
deterministic on every platform. Test-only change; no production code is
affected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Alan Clucas <alan@clucas.org>
@Joibel Joibel requested a review from a team as a code owner June 4, 2026 07:40
@Joibel Joibel marked this pull request as draft June 4, 2026 08:17
@Joibel Joibel marked this pull request as ready for review June 4, 2026 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant