Structured Concurrency in Go with Context
Use cancellation, ownership, and bounded parallelism to build Go services that shut down cleanly and remain understandable.
By SibileshGoroutines are inexpensive, but they are not free and they do not have an owner by default. Structured concurrency gives each unit of work a lifetime tied to the request that created it.
Cancellation is part of the API
Pass context.Context as the first argument and never store it on a long-lived struct.
func hydrate(ctx context.Context, ids []string) error {
group, ctx := errgroup.WithContext(ctx)
group.SetLimit(8)
for _, id := range ids {
id := id
group.Go(func() error {
if err := ctx.Err(); err != nil {
return err
}
return fetchAndStore(ctx, id)
})
}
return group.Wait()
}Bound every fan-out
Unbounded parallelism moves the bottleneck downstream. Choose a limit from dependency capacity, then verify it with load tests and production telemetry.
The easiest concurrency code to operate has clear ownership, a deadline, and one place where errors rejoin the caller.
Continue the discussion
Comments are enabled at deployment.
Configure the public Giscus environment variables to connect GitHub Discussions.