Skip to content

perf(tunnel): reuse HTTP client in CustomTunnel health checks#1201

Open
willamhou wants to merge 3 commits intonearai:stagingfrom
willamhou:fix/reuse-http-client-1039
Open

perf(tunnel): reuse HTTP client in CustomTunnel health checks#1201
willamhou wants to merge 3 commits intonearai:stagingfrom
willamhou:fix/reuse-http-client-1039

Conversation

@willamhou
Copy link

Summary

  • Store a shared reqwest::Client on CustomTunnel struct instead of creating one per health_check() call
  • Client is initialized once in the constructor with a 5s timeout
  • Eliminates per-call TLS session setup and connection pool churn during periodic health monitoring

Fixes #1039

Test plan

  • cargo check passes
  • All 57 tunnel tests pass (cargo test -- tunnel)
  • Existing health_with_unreachable_url_is_false test exercises the changed code path

🤖 Generated with Claude Code

@github-actions github-actions bot added the size: XS < 10 changed lines (excluding docs) label Mar 15, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the performance of health checks within the CustomTunnel by optimizing HTTP client usage. Instead of repeatedly creating new HTTP clients for each check, a single client instance is now reused. This change reduces overhead associated with connection management and TLS handshakes during frequent health monitoring, enhancing efficiency without altering the functional behavior of the health checks.

Highlights

  • Performance Optimization: Replaced the creation of a new reqwest::Client for each health_check() call with a single, shared client instance stored on the CustomTunnel struct. This change eliminates overhead from repeated TLS session setups and connection pool churn during periodic health monitoring.
  • Client Initialization: The reqwest::Client is now initialized once in the CustomTunnel constructor with a 5-second timeout, ensuring consistent client configuration and reducing setup time.
Activity
  • No human activity has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions bot added risk: low Changes to docs, tests, or low-risk modules contributor: new First-time contributor labels Mar 15, 2026
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request improves performance by reusing the reqwest::Client for health checks, which is a good optimization. I've found one area for improvement regarding error handling during client creation to ensure robustness and prevent a potential regression in behavior. My feedback is detailed in the comment below.

Comment on lines +45 to +48
http_client: reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(5))
.build()
.unwrap_or_else(|_| reqwest::Client::new()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using unwrap_or_else here could lead to unexpected behavior. If build() fails, it falls back to a default reqwest::Client that doesn't have the configured timeout. This is a regression, as the previous implementation always had a 5-second timeout, and could cause health checks to hang indefinitely.

A more robust solution would be to propagate the error from build(). This would involve changing CustomTunnel::new to return a Result<Self>:

// In src/tunnel/custom.rs
pub fn new(...) -> anyhow::Result<Self> {
    Ok(Self {
        // ...
        http_client: reqwest::Client::builder()
            .timeout(std::time::Duration::from_secs(5))
            .build()?,
    })
}

This would also require updating the call site in src/tunnel/mod.rs to handle the Result with ?. This ensures that a failure to create the HTTP client is handled explicitly instead of silently creating a client with different behavior.

References
  1. Avoid fallbacks like unwrap_or() or unwrap_or_else() that could cause the system to silently check the wrong logic or operate in an unexpected state. Prefer explicit error propagation or failure.

@github-actions github-actions bot added size: S 10-49 changed lines and removed size: XS < 10 changed lines (excluding docs) labels Mar 15, 2026
Copy link
Collaborator

@zmanian zmanian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: REQUEST CHANGES

Good idea, clean implementation -- reusing reqwest::Client avoids per-call TLS negotiation and connection pool setup. Constructor correctly changed to Result<Self>.

Blocking: missing test update
The stdout_drain_prevents_zombie test (~line 274) still calls CustomTunnel::new("yes".into(), None, None) without .unwrap(). Since the constructor now returns Result<Self>, this will fail to compile. 6 of 7 test callsites were updated, this one was missed. Trivial fix:

let tunnel = CustomTunnel::new("yes".into(), None, None).unwrap();

CI concern: Only classify/scope checks ran -- no build/test. This would have caught the missing .unwrap().

Everything else looks good: timeout correctly moved to client builder, error handling uses .context(), no .unwrap() in production code. Once this one-liner is fixed and CI runs green, this is ready to approve.

willamhou and others added 3 commits March 16, 2026 16:52
Previously, `health_check()` created a new `reqwest::Client` on every
call. Since health checks run periodically, this caused unnecessary TLS
session setup and connection pool churn. Store a shared client on the
struct instead, created once in the constructor with a 5s timeout.

Fixes nearai#1039

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
…back

Address review feedback: `unwrap_or_else(|_| Client::new())` silently
drops the configured timeout on builder failure. Change `new()` to
return `Result<Self>` and propagate with `?` instead.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
CustomTunnel::new was changed to return Result<Self> but this test
callsite was missed. Fixes compilation error.

[skip-regression-check]
@willamhou willamhou force-pushed the fix/reuse-http-client-1039 branch from edf8bdb to 8f96f85 Compare March 16, 2026 08:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: new First-time contributor risk: low Changes to docs, tests, or low-risk modules size: S 10-49 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HIGH] Per-request HTTP client creation in hot path

2 participants