I'm running into an unusual scenario where:
That works but it's fetching the same config file many times concurrently when it could be fetched only once. To experiment with this I have a minimal version here https://go.dev/play/p/Nx-kidmprQx that gives back random ints rather than doing HTTP calls.
Currently it prints:
#2 start
#1 start
#1 result 5577006791947779410
#2 result 8674665223082153551
#3 start
#3 result 6129484611666145821
But I would like the first 2 calls to return the same value because they are done concurrently:
#2 start
#1 start
#1 result 5577006791947779410
#2 result 5577006791947779410
#3 start
#3 result 6129484611666145821
I'm struggling to imagine a solution for this. The fact that multiple goroutines should wait on a single result is confusing. How could it be done?
You can use the Group.Do
implementation from golang.org/x/sync/singleflight
// Do executes and returns the results of the given function, making
// sure that only one execution is in-flight for a given key at a
// time. If a duplicate comes in, the duplicate caller waits for the
// original to complete and receives the same results.
// The return value shared indicates whether v was given to multiple callers.
Modifying your example to separate the synchronous part of the function from the channel operations:
func httpCall(run int) int {
fmt.Printf("#%d start\n", run)
time.Sleep(time.Second)
return rand.Int()
}
You can call that with a simple wrapper function to handle the singleflight.Group
call:
go func() {
res, _, _ := g.Do("httpCall", func() (any, error) {
return httpCall(1), nil
})
done <- res.(int)
}()