GRPC Client Deadline Cancel Propagation
Set per-call deadlines on gRPC clients, propagate cancellation through context, and classify DEADLINE_EXCEEDED versus CANCELED codes.
Code
package main
import (
"context"
"fmt"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
pb "example.com/proto/inventory/v1"
)
func reserveWithDeadline(
ctx context.Context,
client pb.InventoryClient,
sku string, qty int32,
) (*pb.ReserveReply, error) {
ctx, cancel := context.WithTimeout(ctx, 1200*time.Millisecond)
defer cancel()
reply, err := client.Reserve(ctx, &pb.ReserveRequest{
Sku: sku, Quantity: qty,
})
if err != nil {
st := status.Convert(err)
switch st.Code() {
case codes.DeadlineExceeded:
return nil, fmt.Errorf("reserve timed out: %v", st.Message())
case codes.Canceled:
return nil, fmt.Errorf("reserve canceled by caller: %v", st.Message())
case codes.Unavailable:
return nil, fmt.Errorf("service unavailable, retry later: %v", st.Message())
default:
return nil, fmt.Errorf("reserve failed: %v", err)
}
}
return reply, nil
}
func main() {
conn, err := grpc.NewClient(
"inventory.example.com:443",
grpc.WithDefaultCallTimeout(5*time.Second),
)
if err != nil {
fmt.Println(err)
return
}
defer conn.Close()
client := pb.NewInventoryClient(conn)
parent, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
reply, err := reserveWithDeadline(parent, client, "SKU-1", 3)
fmt.Println(reply, err)
}Report a problem with this exampleOpens a pre-filled issue on GitHub.