Implementing Deadlines and Timeouts
1. Setting Client Deadline
| API | Detail |
|---|---|
WithDeadline | Absolute time |
WithTimeout | Relative duration |
| Sent as | grpc-timeout header |
2. Setting Client Timeout
Example: Per-call timeout
ctx, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
defer cancel()
_, err := client.GetUser(ctx, req)
3. Checking Remaining Time
Example: Compute leftover budget
if dl, ok := ctx.Deadline(); ok {
remaining := time.Until(dl)
if remaining < 50*time.Millisecond {
return nil, status.Error(codes.DeadlineExceeded, "insufficient budget")
}
}
4. Propagating Deadlines
| Behavior | Detail |
|---|---|
| Auto-propagation | gRPC forwards grpc-timeout across hops via ctx |
| Subtract margin | Allow time for local processing |
| Don't extend | Never increase parent deadline downstream |
5. Setting Per-Call Timeout
| Mechanism | Detail |
|---|---|
| Context | Preferred — explicit per call |
| Service config | Default for method via JSON |
6. Using Default Timeout
Example: Service config default
{
"methodConfig": [{
"name": [{ "service": "user.v1.UserService" }],
"timeout": "2s"
}]
}
7. Handling Deadline Exceeded
| Side | Detail |
|---|---|
| Client | codes.DeadlineExceeded |
| Server | ctx.Err() = context.DeadlineExceeded — abort cleanly |
8. Implementing Timeout Retry
| Rule | Detail |
|---|---|
| Only for idempotent | Per idempotency_level |
| Bound attempts | Use retry policy or hedging |
| Reset deadline | Allocate fresh budget per attempt or use parent |
9. Setting Server-Side Timeout
| Approach | Detail |
|---|---|
| Interceptor with default | Apply timeout if client didn't set one |
| Honor client deadline | Always check ctx |
10. Using WaitForReady Option
| Behavior | Detail |
|---|---|
| true | Queue call until subchannel ready (or deadline hits) |
| false (default) | Fail-fast with Unavailable if not ready |