Skip to content

perf: Two-phase HTTP client init to avoid macOS Keychain blocking#12208

Merged
anthonyshew merged 1 commit intomainfrom
perf/two-phase-http-client
Mar 9, 2026
Merged

perf: Two-phase HTTP client init to avoid macOS Keychain blocking#12208
anthonyshew merged 1 commit intomainfrom
perf/two-phase-http-client

Conversation

@anthonyshew
Copy link
Contributor

Summary

Splits HTTP client initialization into two phases to remove ~200ms of macOS Keychain enumeration from the critical path.

  • Phase 1 (instant): Builds a client with bundled Mozilla CAs (webpki-roots). No system calls, no Keychain access.
  • Phase 2 (background): Builds a full client with system Keychain CAs (native-roots) on spawn_blocking. Once ready, get_or_init() returns this client instead.

Why

reqwest::Client::builder().build() with rustls-tls-native-roots takes ~200ms on macOS because rustls-native-certs enumerates the system Keychain via the Security framework. This was on the critical path during activate(), adding 200ms to every turbo run.

No HTTP request is made in the first 200ms of a run — remote cache requests happen after index construction (~350ms+) and telemetry flushes at shutdown (~1000ms+). The full client is always ready before it's needed.

Impact

~100ms wall clock improvement on macOS (the HTTP init now fully overlaps with index construction instead of partially overlapping). On Linux, the native-root loading is already fast (<20ms), so this is a no-op.

What changes

File Change
Cargo.toml Added reqwest/rustls-tls-webpki-roots feature
lib.rs Added build_http_client_webpki_only() (webpki-only, instant)
shared_http_client.rs Two OnceLocks: fast_client (Phase 1) and native_client (Phase 2). get_or_init() prefers native if ready, falls back to fast.

Corporate proxy support

Corporate environments with custom root CAs are fully supported. The Phase 2 client loads all system CAs, including custom ones installed in the macOS Keychain. Since Phase 2 completes ~200ms after activate() and the earliest HTTP request happens ~350ms+ into the run, the full client is always available when needed.

@anthonyshew anthonyshew requested a review from a team as a code owner March 9, 2026 12:26
@anthonyshew anthonyshew requested review from tknickman and removed request for a team March 9, 2026 12:26
@vercel
Copy link
Contributor

vercel bot commented Mar 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
examples-basic-web Ready Ready Preview, Comment, Open in v0 Mar 9, 2026 0:33am
examples-designsystem-docs Ready Ready Preview, Comment, Open in v0 Mar 9, 2026 0:33am
examples-gatsby-web Ready Ready Preview, Comment, Open in v0 Mar 9, 2026 0:33am
examples-kitchensink-blog Ready Ready Preview, Comment, Open in v0 Mar 9, 2026 0:33am
examples-nonmonorepo Ready Ready Preview, Comment, Open in v0 Mar 9, 2026 0:33am
examples-svelte-web Ready Ready Preview, Comment, Open in v0 Mar 9, 2026 0:33am
examples-tailwind-web Ready Ready Preview, Comment, Open in v0 Mar 9, 2026 0:33am
examples-vite-web Building Building Preview, Comment, Open in v0 Mar 9, 2026 0:33am
turbo-site Ready Ready Preview, Comment, Open in v0 Mar 9, 2026 0:33am
turborepo-agents Ready Ready Preview, Comment, Open in v0 Mar 9, 2026 0:33am
turborepo-test-coverage Ready Ready Preview, Comment, Open in v0 Mar 9, 2026 0:33am

Build the reqwest HTTP client in two phases to remove ~200ms of macOS
Keychain enumeration from the critical path:

Phase 1 (instant): Build a client with bundled Mozilla CAs
(webpki-roots). Available immediately for any early callers.

Phase 2 (background): Build a full client with system Keychain CAs
(native-roots) on a background thread. Once ready, all subsequent
requests use this client, supporting corporate proxy CAs.

This is transparent to all users — standard HTTPS works immediately
via Phase 1, and corporate proxy environments get full CA support
once Phase 2 completes (~200ms later, well before any remote cache
or API request is made).
@github-actions
Copy link
Contributor

github-actions bot commented Mar 9, 2026

Coverage Report

Metric Coverage
Lines 85.38%
Functions 81.34%
Branches 0.00%

View full report

@anthonyshew anthonyshew enabled auto-merge (squash) March 9, 2026 12:45
@anthonyshew
Copy link
Contributor Author

The Windows partition flake occurred because the Chocolatey CDN flakes, as it is known for doing. Merging past this.

@anthonyshew anthonyshew disabled auto-merge March 9, 2026 12:48
@anthonyshew anthonyshew merged commit 892cb1b into main Mar 9, 2026
51 of 54 checks passed
@anthonyshew anthonyshew deleted the perf/two-phase-http-client branch March 9, 2026 12:48
github-actions bot added a commit that referenced this pull request Mar 9, 2026
## Release v2.8.15-canary.11

Versioned docs: https://v2-8-15-canary-11.turborepo.dev

### Changes

- release(turborepo): 2.8.15-canary.10 (#12205) (`a9bbb9e`)
- perf: Race parallel git subprocesses against filesystem walk for
optimal index construction (#12206) (`9fef3f5`)
- perf: Two-phase HTTP client init to avoid macOS Keychain blocking
(#12208) (`892cb1b`)

---------

Co-authored-by: Turbobot <turbobot@vercel.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant