Async Runtime
Learn GoSafe goroutine launching, WithTimeout SLA enforcement, context propagation rules, and the guarantees Ginject provides for goroutine lifecycle safety.
Async Runtime
Ginject enforces a strict set of rules for async work to guarantee zero goroutine leaks and deterministic request cancellation.
The Problem with Raw go fn()
If the client disconnects, the HTTP request context is cancelled, but the goroutine has no way to know. It continues running, consuming memory and CPU.
GoSafe — The Correct Pattern
GoSafe Signature
| Parameter | Description |
|---|---|
ctx | Parent context. GoSafe checks ctx.Err() before spawning. |
fn | Function to run in the goroutine. Receives ctx as a context.Context. |
errFn | Called if fn panics. Receives the error and stack trace. If nil, panics are silently discarded. |
When GoSafe Skips Spawning
This prevents goroutine spawning on already-cancelled contexts, which is the most common cause of goroutine leaks during request teardown.
WithTimeout — SLA Enforcement
Requests have a global SLA (default 30s). Individual downstream calls should have tighter SLAs:
WithTimeout creates a child context that is cancelled when either the parent is cancelled OR the duration elapses, whichever comes first.
Metadata Inheritance
WithTimeout snapshot-copies the parent's metadata to the child:
Mutations to child do not affect exec, and vice versa (they use separate maps).
Framework Defaults
| Setting | Default | Description |
|---|---|---|
requestTimeout | 30s | Hard deadline on every HTTP request execution context. |
wsMessageTimeout | 5s | Hard deadline on each WebSocket message dispatch. |
Override via app configuration:
Context Propagation Rules
- Pass explicitly — never rely on goroutine-local storage or package-level variables to share a context.
- First parameter —
*ctx.ExecutionContextshould always be the first parameter of service and repository functions. - Never store — never store
*ExecutionContextin a struct field. It is request-scoped and will be cancelled. - Always defer cancel — every
WithTimeoutcall must be followed immediately bydefer cancel(). - Use GoSafe — every goroutine spawned during a request must use
ctx.GoSafe.
Long-Running Background Work
For work that outlives the request (e.g., queue processing), use context.Background() directly — don't inherit from the request context:
This is the one case where go fn() without GoSafe is acceptable — the goroutine is intentionally long-lived and unrelated to the request.
Cancellation Propagation Diagram
Every goroutine in the tree is guaranteed to stop when the request ends.