Skip to main content
Version: 0.2

Groups

Groups run several functions on a pool and wait for all of them. There are two, differing only in what happens when a function fails.

GroupErrorGroup
On the first errorkeeps goingcancels the group's context
Wait returnsevery error (WaitAll) or the first (Wait)the first error
Modeled on—golang.org/x/sync/errgroup

Unlike errgroup, the functions run on the pool's workers, so the pool's concurrency limit, job timeout, retries, middleware, and metrics all apply to them.

ErrorGroup​

Use it when the results only matter together — loading the pieces of one response, for instance:

g := worker.NewErrorGroupContext(r.Context(), pool)

var profile *Profile
var orders []Order

g.Go(func(ctx context.Context) error {
var err error
profile, err = loadProfile(ctx, userID)
return err
})
g.Go(func(ctx context.Context) error {
var err error
orders, err = loadOrders(ctx, userID)
return err
})

if err := g.Wait(); err != nil {
return err
}

When a function returns an error, the group's context is cancelled. Functions still running see ctx.Done(), and functions that have not started yet are skipped. Wait returns that first error.

Context() returns the group's context — cancelled on the first error, or when Wait returns — for work outside the group that should stop with it.

Group​

Use it when every task should run regardless of the others, and you want all the failures:

g := worker.NewGroup(pool)

for _, id := range accountIDs {
g.Go(func(ctx context.Context) error {
return reconcile(ctx, id)
})
}

for _, err := range g.WaitAll() {
slog.Error("reconcile failed", "err", err)
}

WaitAll returns the errors in the order the functions finished. Wait returns just the first of them, or nil.

NewGroupContext(ctx, pool) ties the group to a parent context. If it is cancelled, functions that have not started are skipped, and each records context.Canceled in WaitAll.

Things to know​

Go can block. It submits to the pool, so if the queue is full it waits for space — or until the group's context ends. Launching thousands of tasks from one loop is fine, but the loop proceeds at the pool's pace.

Don't wait from inside the same pool. A job that creates a group on its own pool and calls Wait holds a worker while it waits for other workers. If every worker does that, nothing is left to run the group's tasks and the pool deadlocks. Use a separate pool for nested fan-out.

Use a group once. Wait cancels the group's context when it returns, so functions added after Wait are skipped. Create a new group for each batch of work.

Retries are per function. With WithMaxRetries, each function is retried on its own before its result reaches the group. In an ErrorGroup, the group is only cancelled once a function has failed for good.