Implement a generic "first response wins" (hedged request / race) function.
Question 212HardGo 1.22 to 1.25
// First successful result wins; the others are cancelled.
func First[T any](ctx context.Context, fns ...func(context.Context) (T, error)) (T, error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel() // stops the losers
type res struct {
v T
err error
}
ch := make(chan res, len(fns)) // buffered: losers never block
for _, f := range fns {
go func() {
v, err := f(ctx)
ch <- res{v, err}
}()
}
var lastErr error
for range len(fns) {
select {
case r := <-ch:
if r.err == nil {
return r.v, nil
}
lastErr = r.err
case <-ctx.Done():
var zero T
return zero, ctx.Err()
}
}
var zero T
return zero, lastErr
}
Key design choices: the buffer size len(fns) guarantees that the goroutines of slower replicas can always finish their send and exit, so nothing leaks after we return. defer cancel() tells them to stop early. Using Go 1.22 per-iteration loop variables means f is captured correctly without f := f. This "hedged request" pattern is common in latency-sensitive systems, and questions about it come up at large companies.
More on Channels & select
- Q210What happens if you send on a closed channel inside a
selectwith adefault? - Q211Does an unbuffered channel give a happens-before guarantee? What does the memory model say about channels?
- Q213What does this print? (request/response over a channel of channels)
- Q214How would you implement rate limiting using channels?
- Q215Can you compare channels with
==, use them as map keys, and send channels over channels? - Q216How do you gracefully shut down a service that uses channels and goroutines?