Что такое `errgroup` в Go и какую проблему он решает?

Ответ

errgroup — это пакет (golang.org/x/sync/errgroup), который предоставляет средства для синхронизации группы горутин и централизованной обработки ошибок между ними.

Он решает распространенную задачу: запустить несколько параллельных операций, дождаться их завершения и, если хотя бы одна из них вернет ошибку, немедленно отменить остальные и вернуть первую возникшую ошибку.

Ключевые возможности:

  1. Синхронизация: Метод g.Wait() блокирует выполнение, пока все запущенные через g.Go() горутины не завершатся (аналогично sync.WaitGroup).
  2. Распространение ошибки: g.Wait() возвращает первую не-nil ошибку от любой из горутин.
  3. Отмена через контекст: При использовании errgroup.WithContext(), если одна горутина возвращает ошибку, контекст, переданный в другие горутины, автоматически отменяется. Это позволяет им грациозно завершить свою работу, не выполняя лишних действий.

Пример: параллельное выполнение HTTP-запросов

func fetchURLs(ctx context.Context, urls []string) error {
    g, gCtx := errgroup.WithContext(ctx)

    for _, url := range urls {
        // Захватываем переменную url в замыкании
        url := url 
        g.Go(func() error {
            req, err := http.NewRequestWithContext(gCtx, http.MethodGet, url, nil)
            if err != nil {
                return err
            }
            resp, err := http.DefaultClient.Do(req)
            // Если gCtx был отменен (из-за ошибки в другой горутине),
            // http.Client вернет ошибку, и эта горутина завершится.
            if err != nil {
                return err
            }
            resp.Body.Close()
            fmt.Printf("Fetched %s with status %sn", url, resp.Status)
            return nil
        })
    }

    // Ждем завершения всех горутин и возвращаем первую ошибку, если она была.
    return g.Wait()
}

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