Для чего нужен ErrGroup в Go

Ответ

errgroup из пакета golang.org/x/sync/errgroup — это мощный инструмент для управления группой горутин, которые выполняют связанные задачи. Он расширяет функциональность sync.WaitGroup тремя ключевыми возможностями:

  1. Синхронизация и ожидание. Как и WaitGroup, он позволяет запустить несколько горутин и дождаться их завершения с помощью метода Wait().
  2. Обработка первой ошибки. Если любая из горутин в группе возвращает ненулевую ошибку, Wait() немедленно вернет эту ошибку. Это основной сценарий использования.
  3. Отмена через контекст. При возникновении первой ошибки errgroup автоматически отменяет context, связанный с группой. Это позволяет остальным горутинам грациозно завершить свою работу, прекратив бесполезные вычисления.

Ключевое отличие от sync.WaitGroup:
WaitGroup просто ждет завершения горутин, но не имеет встроенных механизмов для сбора ошибок или для отмены других горутин при сбое одной из них. errgroup решает обе эти проблемы.

Пример использования:

// Создаем группу с контекстом, который будет отменен при первой ошибке.
g, ctx := errgroup.WithContext(context.Background())

urls := []string{
    "http://example.com",
    "http://example.net",
    "http://invalid.url.that.will.fail",
}

for _, url := range urls {
    // Запускаем каждую задачу в отдельной горутине.
    // Важно передавать url в горутину как аргумент, чтобы избежать проблем с замыканием.
    u := url
    g.Go(func() error {
        // http.Get автоматически учитывает отмену контекста.
        resp, err := http.NewRequestWithContext(ctx, "GET", u, nil)
        if err != nil {
            return err // Возвращаем ошибку, что приведет к отмене контекста.
        }
        // ... какая-то работа с resp
        fmt.Printf("Successfully fetched %sn", u)
        return nil
    })
}

// Ждем завершения всех горутин. Если была ошибка, она вернется здесь.
if err := g.Wait(); err != nil {
    fmt.Printf("An error occurred: %vn", err)
}

Когда использовать: Идеально подходит для параллельного выполнения независимых задач (например, запросы к разным API, обработка файлов), где сбой одной задачи должен приводить к остановке всего процесса.