The new synctest
package makes time deterministic within a testing bubble. One consequence of this is that the goroutine schedule also becomes more deterministic within the bubble. But it's a fragile determinism that depends on the scheduler implementation. If we leave this alone, Hyrum's Law will lead to tests that depend on the details of the scheduler implementation, making the scheduler more difficult to change in the future. We've successfully defended against this before through intentional randomization, most notably in map iteration and the select
poll order. We should do the same for synctest
and defend against Hyrum's Law by introducing intentional randomization in the selection of which runnable goroutine to run when inside a bubble. We already do this in -race
mode and can probably reuse that same mechanism.
cc @neild , @golang/runtime
Comment From: neild
I'm not sure that we actually need to do anything here.
Synctest only makes scheduling more deterministic in the sense that it (by design) makes time deterministic. An event that happens at time T will always happen before one that happens at time T+1.
When time advances in a synctest bubble, it runs timers using the same mechanism as the regular runtime scheduler (runtime.timers.check
). I think this results in exactly as much deterministic as usual, for timers scheduled at the same instant. The only place where determinism differs is when two timers are scheduled for almost, but not quite, the same instant--in that case, the usual runtime will probably execute both at once, while synctest will always execute them in order.
Comment From: thepudds
See also #73876.
Comment From: mknyszek
@aclements We discussed this in the weekly triage meeting, and we tend to agree with @neild. If two goroutines become runnable simultaneously (for example, from two timers firing simultaneously, or close in time), it's still as non-deterministic as before. @neild notes there is some unwanted determinism around selecting on timer channels, and maybe we do something there.
Is there something we're missing? Applying okay-after-rc1 for now.
Comment From: glycerine
I tried to make it more obvious what I'm seeing, in this comment-- https://github.com/golang/go/issues/73876#issuecomment-2917883308
It sure does not look (pseudo) random to me.
If it makes sense to someone, please help me understand what I'm missing.
Update: nevermind. I found my bug, it was in my printing of the wrong timer.
Comment From: gopherbot
Change https://go.dev/cl/677276 mentions this issue: runtime: randomize order of timers at the same instant in bubbles
Comment From: glycerine
Putting a back pointer to a behavior that need randomization here.
https://github.com/golang/go/issues/73934
Examples of the behavior were posted here: https://github.com/golang/go/issues/73876#issuecomment-2918615251
Comment From: gopherbot
Change https://go.dev/cl/678075 mentions this issue: runtime: make bubbled timers more consistent with unbubbled
Comment From: mknyszek
@neild In triage we're just checking, is there anything else to do here? Thanks!
Comment From: glycerine
I did a quick check at Go 1.25 prerelease tip 3432c68467d50ffc622fed230a37cd401d82d4bf
Regular channels versus timers are looking well randomized in this test https://github.com/glycerine/reflect_not_random_synctest/blob/master/recv_compare_test.go#L164 that looks at how often a given 12 case select arm is chosen first and last. N=100_000 trials:
Go 1.24.3 synctest:
first = '[]int{9947, 10021, 9999, 10057, 9915, 0, 0, 9986, 10083, 9990, 10029, 9973}'
last = '[]int{0, 0, 0, 0, 0, 0, 100000, 0, 0, 0, 0, 0}'
Go 1.25-prerelease/tip synctest:
first = '[]int{8416, 8365, 8383, 8221, 8346, 8412, 8391, 8241, 8243, 8116, 8414, 8452}'
last = '[]int{8336, 8293, 8419, 8378, 8266, 8265, 8158, 8357, 8253, 8453, 8511, 8311}'
Nice work, @neild.
Comment From: neild
I'm calling this done: We're certainly not going to adjust the scheduler itself in 1.25 (and I think having it behave the same in a bubble as out of one is fine), and we've now randomized every form of unintentional time-based determinism that has been reported.