Problem Statement
At Uber, we need to fetch data from multiple microservices concurrently. If any service fails, we should cancel all other requests and return the first error. Implement the errgroup package functionality from scratch.
Requirements
- Run multiple goroutines concurrently
- If any goroutine returns an error, cancel all others
- Wait for all goroutines to complete
- Return the first error encountered
Implementation
package errgroup
import (
"context"
"sync"
)
type Group struct {
cancel context.CancelFunc
wg sync.WaitGroup
errOnce sync.Once
err error
}
func WithContext(ctx context.Context) (*Group, context.Context) {
ctx, cancel := context.WithCancel(ctx)
return &Group{cancel: cancel}, ctx
}
func (g *Group) Go(f func() error) {
g.wg.Add(1)
go func() {
defer g.wg.Done()
if err := f(); err != nil {
g.errOnce.Do(func() {
g.err = err
if g.cancel != nil {
g.cancel()
}
})
}
}()
}
func (g *Group) Wait() error {
g.wg.Wait()
if g.cancel != nil {
g.cancel()
}
return g.err
}Usage Example
package main
import (
"context"
"fmt"
"net/http"
)
func main() {
g, ctx := errgroup.WithContext(context.Background())
urls := []string{
"https://api.service-a.com/data",
"https://api.service-b.com/data",
"https://api.service-c.com/data",
}
results := make([]string, len(urls))
for i, url := range urls {
i, url := i, url // Capture loop variables
g.Go(func() error {
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// Process response...
results[i] = "success"
return nil
})
}
if err := g.Wait(); err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("All requests succeeded")
}Edge Cases to Handle
- Multiple goroutines returning errors simultaneously
- Panic in a goroutine (add recover)
- Context already cancelled before Go() is called
Follow-up Questions
- How would you add a limit to concurrent goroutines?
- How would you collect all errors instead of just the first?
- How does the real
golang.org/x/sync/errgrouphandle panics?