Senior · Architecture and systems
WithoutCancel is not a background-job owner
A handler launches a goroutine with context.WithoutCancel(r.Context()) to guarantee an email is sent. What guarantee is actually missing?
Try explaining this aloud before opening the answer. State your assumptions and trace a boundary case.
package main
import (
"context"
"errors"
"fmt"
)
func main() {
ctx, cancel := context.WithCancelCause(context.Background())
cancel(errors.New("request no longer needed"))
fmt.Println(ctx.Err()) // context canceled
fmt.Println(context.Cause(ctx)) // request no longer needed
detached := context.WithoutCancel(ctx)
fmt.Println(detached.Err(), detached.Done() == nil) // <nil> true
}Reveal a strong answer
Passing context.Context as the first parameter lets cancellation and deadlines cross a call boundary. It does not force the callee to stop, and it does not prove that a goroutine has exited. Context values are for request-scoped metadata that must cross API boundaries, not an alternative constructor or a hidden transport for required service dependencies.
Pass the request context into storage and outgoing calls. Check cancellation at meaningful handoffs and stop producers as well as consumers. For must-complete work, prefer a durable job or an application-owned worker with an explicit shutdown budget. Keep tenant/principal values typed and verified; an arbitrary value in context does not establish authorization. Err identifies cancellation broadly; Cause retains the chosen explanation. WithoutCancel removes the parent cancellation, deadline and Done signal, so it must not be used as a convenient way to make an unowned goroutine live forever. A background owner must supply its own bounded context and join or durably queue the work.
Storing a request context in a long-lived struct can retain request data and cancel unrelated work. Replacing every context with Background hides client cancellation. Calling cancel alone does not join a worker. Test cancellation while waiting, during an operation and after the caller stops reading results. Explain which side owns the result channel and how both sides exit.
Common trap
Making a request uncancelled does not make its work bounded, durable or correctly authorized forever.
Try it
Explain cancellation, deadline, Done channel, shutdown and process-crash behavior; propose best-effort and durable alternatives.
Worked solution
WithoutCancel removes parent cancellation and deadline propagation; it does not supply a new timeout, join, durability or recovery. Best-effort detached work needs an application-owned bounded worker and shutdown policy. Must-complete work should be durably queued with stable identity and replay-safe effects before acceptance. Copy only required data, avoid retaining request objects and do not confuse cancellation signaling with proof that goroutines exited.
Change the constraint
How should authorization and tenant scope be rechecked when a queued job executes much later?
How did you do?
Self-assessment, not a compiler pass or interview score. Explain the reasoning unaided before marking understood.
Not marked. Progress stays on this browser, separate from course completion.
Primary references
Checked 2026-10-28. Use documentation for your pinned runtime, driver and tool version.