Search code examples
gochannelgoroutineworker

How to exit when the first error occurs for one of the goroutines within a wait Group?


func getRecords(ctx context.Context, ids *[]Id) error {
    ctx, cancel := context.WithTimeout(ctx, DefaultTimeout)
    defer cancel()

    var wg sync.WaitGroup
    size := len(*ids)
    allErrors := make(chan error, size)

    for i := 0; i < size; i++ {
        wg.Add(1)

        go func(x int){
            err := //call to other func which return error type. nil or error
            if err != nil { // I will be creating n goroutines. How to exit rightaway if one of 
                            // them return error
                allErrors <- err 
            }
            wg.Done()
        }(i)
    }

    go func() {
        wg.Wait()
        close(allErrors)
    }
    return nil
}

How to exit on the anonymous function here?


Solution

  • You need to use ErrGroup

    This is what ErrGroup.Go Does

    The first call to return a non-nil error cancels the group; its error will be returned by Wait.

    So the other goroutines will be automatically canceled when any one of the goroutine return error.

    Usage:

    errs, ctx = errgroup.WithContext(ctx)
    
    for i := 0; i < size; i++ {
        errs.Go( func() error{
            ...
        })
    }
    
    if err := g.Wait(); err != nil {
        // Handle Errors from GoRoutines
    }
    

    Edit:
    Maybe the answer was not descriptive enough. when the docs say other goroutines will be canceled, it means the context that was returned by errGroup will be issued a cancelation. So each go routine owner must handle this cancelation in their code.
    Example: https://go.dev/play/p/hXWjtN4uj06

    Real World Example: When your golang service starts, maybe you start a HTTP server in one routine, a Kafka consumer in another and say a grpc server in the last one. Now if any one of them stops, you want to stop the whole service (consider the service being down). Then you share the errGroupd context with all these goroutines (Luckily these underlying services already handle this context cancelation logic, so you don't have to write one) but if that goroutine does something custom then its the goroutine owner's responsibility to handle cancelation signal