From f470d3e6b05d1fbeca77949ac049dbd89d5aed0f Mon Sep 17 00:00:00 2001 From: slayerjain Date: Tue, 23 Jun 2026 14:43:57 +0530 Subject: [PATCH] fix(grpc-mongo): per-call deadlines so paced NextToken doesn't time out The client wrapped SeedTokens + 10 deliberately-paced NextToken calls in a single shared 20s absolute-deadline context. With 1s of pacing between calls plus the server's 500ms work plus keploy's proxyless-capture warmup, the cumulative wall-clock approached 20s on a loaded CI runner, so NextToken #9 sporadically failed with "rpc error: code = DeadlineExceeded" and exited 1 (flaky grpc-mongo step in the enterprise CI). Give each RPC its own bounded (10s) context. The inter-call pacing no longer eats into a later call's budget, so the run is deterministic; the assertions are unchanged (11 captures, every token asserted) and a genuinely hung RPC still times out at the per-call deadline. Signed-off-by: slayerjain --- grpc-mongo/client/main.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/grpc-mongo/client/main.go b/grpc-mongo/client/main.go index 09226b92..407a8344 100644 --- a/grpc-mongo/client/main.go +++ b/grpc-mongo/client/main.go @@ -35,23 +35,35 @@ func main() { defer conn.Close() //nolint:errcheck c := pb.NewTokenServiceClient(conn) - ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) - defer cancel() + + // Per-call deadlines: each RPC gets its own bounded context so the client's + // deliberate inter-call pacing (and the slow proxyless-capture warmup on a + // loaded CI runner) never eats into a later call's budget. A single shared + // absolute 20s deadline conflated all 11 calls plus their 1s pacing sleeps + // into one window, so under contention NextToken #9 sporadically tripped + // "DeadlineExceeded". A genuinely hung RPC still times out at callTimeout. + const callTimeout = 10 * time.Second // Seed 10 tokens (order matters, first popped = first returned) seed := []string{"alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", "iota", "kappa"} - ack, err := c.SeedTokens(ctx, &pb.SeedRequest{Tokens: seed}) + seedCtx, cancelSeed := context.WithTimeout(context.Background(), callTimeout) + ack, err := c.SeedTokens(seedCtx, &pb.SeedRequest{Tokens: seed}) + cancelSeed() if err != nil { log.Fatalf("seed: %v", err) } fmt.Println("Seed:", ack.Message) + time.Sleep(1 * time.Second) // Make 10 identical requests -> 10 different replies for i := 1; i <= 10; i++ { - r, err := c.NextToken(ctx, &pb.NextTokenRequest{}) + callCtx, cancelCall := context.WithTimeout(context.Background(), callTimeout) + r, err := c.NextToken(callCtx, &pb.NextTokenRequest{}) + cancelCall() if err != nil { log.Fatalf("NextToken #%d: %v", i, err) } fmt.Printf("NextToken #%d: %s\n", i, r.Token) + time.Sleep(1 * time.Second) } }