Sincronizarea Goroutine-urilor Paralele și Propagarea Erorilor cu errgroup în Go

Sincronizare (sync) golang.org/x/sync/errgroup

Ghid esențial pentru pachetul errgroup în Go: gestionarea elegantă a grupurilor de sarcini concurente, capturarea primei erori returnate și propagarea automată a anulării către celelalte goroutine prin context.

Sintaxă
g, ctx := errgroup.WithContext(ctx); g.Go(func() error { ... }); if err := g.Wait(); err != nil

Când o cerere HTTP trebuie să execute mai multe operațiuni paralele (de exemplu interogarea simultană a unui profil de utilizator, a listei de comenzi și a recomandărilor din baze de date separate), utilizarea clasică a lui sync.WaitGroup este insuficientă, deoarece nu oferă un mod nativ de a propaga erorile și de a anula celelalte operațiuni dacă una dintre ele eșuează. Pachetul errgroup rezolvă exact această problemă: coordonează un grup de goroutine-uri, captează prima eroare apărută și anulează automat contextul pentru restul sarcinilor.

Execuție paralelă cu anulare automată la eroare
package main

import (
    "context"
    "fmt"
    "net/http"
    "golang.org/x/sync/errgroup"
)

func verificaServicii(ctx context.Context, urls []string) error {
    // 1. Creăm un errgroup legat de un context derivat:
    g, ctx := errgroup.WithContext(ctx)

    // (Opțional): Limităm numărul maxim de goroutine-uri concurente:
    g.SetLimit(3)

    for _, u := range urls {
        url := u // Captură variabilă de buclă
        g.Go(func() error {
            req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
            if err != nil {
                return err
            }
            resp, err := http.DefaultClient.Do(req)
            if err != nil {
                return fmt.Errorf("esec la interogarea %s: %w", url, err)
            }
            defer resp.Body.Close()
            fmt.Printf("Succes: %s -> Status %d
", url, resp.StatusCode)
            return nil
        })
    }

    // 2. Așteptăm finalizarea tuturor goroutine-urilor:
    // Returnează prima eroare întâlnită; celelalte goroutine-uri active primesc ctx.Done()!
    return g.Wait()
}