From c0b5fd5a6decd31fa00ac1b22915401226fc595e Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Tue, 17 Mar 2026 05:43:58 +0000 Subject: [PATCH] fix(op-service): harden RetryProxy to stabilize TestImplementations flaky test The TestImplementations test (66 flakes) relies on the devnet RetryProxy to forward RPC calls to external mainnet/sepolia endpoints. Under CI load (12 parallel nodes on a 2xlarge), the proxy's tight timeouts caused intermittent failures: - Per-request timeout of 5s was insufficient for slow external RPCs under load; increased to 30s (matching the Anvil startup timeout bump in #19424) - Max retries of 5 was too few for sustained rate limiting; increased to 10 - Start() had a race condition: used a 100ms timer instead of a proper ready signal, and failed to return after net.Listen errors. Replaced with a channel-based ready signal that blocks until the listener is actually bound. Co-Authored-By: Claude Opus 4.6 (1M context) --- op-service/testutils/devnet/proxy.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/op-service/testutils/devnet/proxy.go b/op-service/testutils/devnet/proxy.go index 0f1ab2c89ee..b1fa83d9818 100644 --- a/op-service/testutils/devnet/proxy.go +++ b/op-service/testutils/devnet/proxy.go @@ -46,7 +46,7 @@ func NewRetryProxy(lgr log.Logger, upstream string, opts ...Option) *RetryProxy upstream: upstream, client: &http.Client{}, strategy: strategy, - maxRetries: 5, + maxRetries: 10, } for _, opt := range opts { @@ -57,28 +57,31 @@ func NewRetryProxy(lgr log.Logger, upstream string, opts ...Option) *RetryProxy } func (p *RetryProxy) Start() error { + readyC := make(chan struct{}, 1) errC := make(chan error, 1) go func() { ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { errC <- fmt.Errorf("failed to listen: %w", err) + return } p.listenPort = ln.Addr().(*net.TCPAddr).Port + readyC <- struct{}{} p.srv = &http.Server{ - Addr: "127.0.0.1:0", Handler: p, } - errC <- p.srv.Serve(ln) + if err := p.srv.Serve(ln); err != nil && err != http.ErrServerClosed { + p.lgr.Error("server exited with error", "err", err) + } }() - timer := time.NewTimer(100 * time.Millisecond) select { case err := <-errC: - return fmt.Errorf("failed to start server: %w", err) - case <-timer.C: + return err + case <-readyC: p.lgr.Info("server started", "port", p.listenPort) return nil } @@ -114,7 +117,7 @@ func (p *RetryProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { //nolint:bodyClose res, resBody, err := retry.Do2(r.Context(), p.maxRetries, p.strategy, func() (*http.Response, []byte, error) { - ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second) + ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second) defer cancel() res, err := p.doProxyReq(ctx, reqBody) if err != nil {