- How to handle cancellation
- See also context doc
- Use
context
for cancellation - Parent
context
cancels children (Propagation)- child
context
does NOT cancel parentcontext
- eg.
context.Background()
is never cancelled
- child
- HTTP Server: automatically cancels context when connection closed
- HTTP Client: Use
http.NewRequestWithContext
orreq.WithContext
to handle cancellation - Cancellation is cooperative, cancellation does NOT kill a goroutine
go vet
checks that CancelFuncs are used on all control-flow paths.
- Call
cancel()
func indefer
statement - Always call
cancel()
func returned byWithDeadline
orWithCancel
func foo() {
parentCtx := context.Background()
...
ctx, cancel = context.WithCancel(parentCtx) // context.WithDeadline is similar
defer cancel() // Guarantee child cancellation
// pass ctx to another func or use below
// (eg. pass to http client, grpc client, sql client, kafka, rabbitmq client, ...)
}
- To handle cancellation, timeout, deadline expiration:
type FooResult int // or a struct with both result & error
func DoSomeExpensiveIO(ctx context.Context) (FooResult, error) {
// result of "real" call goes into channel
// sender not blocked since buffer is 1
resultCh := make(chan FooResult, 1)
go func() {
// send returned result to channel
resultCh <- doRealIOWork()
// -- alternative: func writes result to channel (instead of returning)
// doRealIOWork(resultCh)
}()
// wait for first of [a result] or [cancellation/timeout]
select {
case <-ctx.Done():
return 0, ctx.Err()
case out := <-resultCh:
return out, nil
}
}