Skip to content

perf(startup): defer httpx import off the cold-start path (#13)#143

Merged
thomaschristory merged 1 commit into
mainfrom
perf/lazy-startup-imports
Jul 3, 2026
Merged

perf(startup): defer httpx import off the cold-start path (#13)#143
thomaschristory merged 1 commit into
mainfrom
perf/lazy-startup-imports

Conversation

@thomaschristory

Copy link
Copy Markdown
Owner

Closes #13 (Path C: investigate + fix, then re-baseline).

What & why

Profiling nsc --help with -X importtime showed httpx (~65ms, the single heaviest import in the tree) loading eagerly on every startup — through the always-imported runtime → client → retry chain, plus the cache / schema / login command modules registered in app.py. Nothing on the --help / cold-start path needs an HTTP client, so this was pure waste that had crept in over several releases (issue #13's exact prediction).

All six modules now import httpx lazily, at first request. Type-only references stay under TYPE_CHECKING.

Result

  • -X importtime on nsc --help (unconfigured home): httpx no longer appears at all.
  • Local median: ~330ms → ~290ms. Local wall-clock is noisy (290–340ms under load), so the deterministic importtime guard test is the real proof, not the timing.
  • Bench threshold re-baselined 250ms → 300ms (the documented project target). The OVER THRESHOLD skip had fired on every run for ~7 releases, becoming folklore; it now passes normally and only skips on a genuine regression.

Verification

  • just lint — ruff + mypy --strict clean.
  • just test — 1367 passed, 1 skipped (bench gate).
  • New tests/cli/test_lazy_httpx_import.py guards the invariant both ways (import-only + --help invocation).
  • 3 test_commands_dump tests updated: they patched the now-absent nsc.schema.loader.httpx attribute → patch httpx.stream directly instead.

Note on the threshold: it's defined "on the CI runner", and local hardware runs ~65ms slower + noisy. I'll confirm the real median against this PR's bench job (continue-on-error, informational) and tune 300ms if CI disagrees.

🤖 Generated with Claude Code

httpx is the single heaviest import in the tree (~65ms cold) yet nothing
on the `nsc --help` / startup path needs an HTTP client. It was pulled in
eagerly via the runtime → client → retry chain, plus the cache, schema,
and login command modules. Defer it to first-request in all six so cold
startup drops back under the 300ms project target.

- http/client, http/retry, cli/cache_commands, schema/source,
  schema/loader, auth/verify: move `import httpx` into the functions that
  actually issue requests; keep type-only imports under TYPE_CHECKING.
- test_lazy_httpx_import.py: importtime-based guard asserting httpx never
  loads on `nsc --help` (deterministic, unlike noisy wall-clock).
- test_commands_dump: patch `httpx.stream` directly — the lazy import
  removes the `nsc.schema.loader.httpx` module attribute the tests patched.
- benchmarks/test_startup: re-baseline THRESHOLD 250ms -> 300ms (the
  documented project target) so an over-threshold skip signals a real
  regression instead of firing on every run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thomaschristory thomaschristory merged commit 497b311 into main Jul 3, 2026
9 checks passed
@thomaschristory thomaschristory deleted the perf/lazy-startup-imports branch July 3, 2026 21:34
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.

Phase 6: re-baseline or investigate the 250ms bench startup-time soft-skip

1 participant