Yes, you are right (the link here is a link to the slide ). In the above code, only one running goroutine will be completed, the rest will hang when trying to send on channel c .
Detailing:
c - unbuffered channel- there is only one receive operation in the
return , - A new goroutine is launched for each
replicas item. - every running goroutine sends a value on channel
c - since only 1 gets from it, one goroutine will be able to send a value to it, the rest will be blocked forever
Note that depending on the number of replicas elements (which is len(replicas) ):
- if it is
0 : First() will block forever (no one sends anything to c ) - if it is
1 : will work as expected - if it is
> 1 : then it is a resource leak
The following modified version will not flow goroutines using non-blocking send (using select with default branch):
searchReplica := func(i int) { select { case c <- replicas[i](query): default: } }
The first goroutine ready for the result will send it via channel c , which will be received by goroutine under the control of First() in the return . All other goroutines, when they receive the result, will try to send over the channel and "see" that it is not ready (sending is blocked because no one is ready to accept from it), the default branch will be selected, and thus the goroutine will end normally .
Another way to fix this would be to use a buffer channel:
c := make(chan Result, len(replicas))
And thus, send operations will not be blocked. And, of course, only one (first sent) value will be received from the channel.
Note that a solution with any of the above fixes will still block if len(replicas) is 0 . To avoid this, First() should explicitly check this, for example:
func First(query string, replicas ...Search) Result { if len(replicas) == 0 { return Result{} }
Some tools / resources for leak detection: