From a326298f51e6ff39e410be00a94fe13c0515db71 Mon Sep 17 00:00:00 2001 From: Jonny Rhea <5555162+jrhea@users.noreply.github.com> Date: Tue, 16 Jun 2026 07:46:34 -0500 Subject: [PATCH] rpc: fix flaky TestTracingHTTPTimeout (#35172) `TestTracingHTTPTimeout` still flakes in CI after #35101, failing at the POST: --- FAIL: TestTracingHTTPTimeout (0.26s) tracing_test.go:633: request: Post "http://127.0.0.1:43497": EOF The test sets a short server `WriteTimeout` and posts a blocking call. `ContextRequestTimeout` leaves a fixed 100ms for the server to write its timeout response before the HTTP write deadline cuts the connection. I can't repro it locally, but my theory is that under load that write can miss the window, so the connection is dropped and the client POST returns `EOF`, failing the test before it inspects the span. This is the only test exposed to it because it is the only one that configures a `WriteTimeout`. The EOF is benign: the server sets the timeout error on the SERVER span before attempting the write, independent of whether the client receives the response. Since that span status is all the test asserts, `tryPostJSONRPC` tolerates the transport error instead of failing on it. --- rpc/tracing_test.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/rpc/tracing_test.go b/rpc/tracing_test.go index fa2ef09e76..dafa9fb8a7 100644 --- a/rpc/tracing_test.go +++ b/rpc/tracing_test.go @@ -346,17 +346,27 @@ func TestTracingSubscribeUnsubscribe(t *testing.T) { // like notifications (no "id" field). func postJSONRPC(t *testing.T, url, body string) { t.Helper() + if err := tryPostJSONRPC(url, body); err != nil { + t.Fatalf("request: %v", err) + } +} + +// tryPostJSONRPC is like postJSONRPC but returns the transport error instead of +// failing the test. The write-timeout test uses this because the HTTP +// WriteTimeout can drop the connection before the response is flushed. +func tryPostJSONRPC(url, body string) error { req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(body)) if err != nil { - t.Fatalf("new request: %v", err) + return err } req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { - t.Fatalf("request: %v", err) + return err } _, _ = io.Copy(io.Discard, resp.Body) resp.Body.Close() + return nil } // TestTracingHTTPNotification verifies that a JSON-RPC notification emits the @@ -630,7 +640,12 @@ func TestTracingHTTPTimeout(t *testing.T) { // test_block waits on ctx.Done() and returns an error. The internal // timer cancels ctx, so test_block unblocks shortly after the timeout // response goes out. - postJSONRPC(t, httpsrv.URL, `{"jsonrpc":"2.0","id":1,"method":"test_block"}`) + // + // Ignore the client-side result. Under load the HTTP WriteTimeout can + // drop the connection before the timeout response is flushed, which the + // client sees as EOF. The server still records the timeout on its span, + // which is what we assert below. + _ = tryPostJSONRPC(httpsrv.URL, `{"jsonrpc":"2.0","id":1,"method":"test_block"}`) // Wait for the in-flight request to finish so the deferred spanEnd fires // before GetSpans is called.