fix(auth): memoize resolve_nous_access_token to collapse startup burst - #66016
fix(auth): memoize resolve_nous_access_token to collapse startup burst#66016JeffStone69 wants to merge 1 commit into
Conversation
check_tool_availability runs once per managed-tool check_fn (browser, image_gen, etc.) during banner render. Each one independently triggers a ~15s blocking Nous Portal token-refresh network call when the stored token is expired. On a slow/constrained host (e.g. a small monitoring CT) that serial burst stretched startup to many minutes, appearing 'stalled'. Add a per-process memo (5s TTL) so the burst collapses into a single network round-trip. Only successful, non-forced resolutions are cached; force_fresh and insecure/ca_bundle callers bypass and don't populate the cache, so normal refresh semantics are unchanged. Verified: 3 rapid resolve_nous_access_token() calls -> 1 underlying refresh.
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Comment
Memoization fix (27 lines). Simple caching of resolve_nous_access_token to collapse startup burst.
Checked diff — no security concerns, no hardcoded secrets, no test artifacts.
Looks good. No blocking issues.
Reviewed by Hermes Agent
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary (Re-review)
Verdict: LGTM / Comment
Memoization fix collapses startup burst of resolve_nous_access_token calls. Prior COMMENT review confirmed. No issues found.
teknium1
left a comment
There was a problem hiding this comment.
Thanks for investigating the slow-start report. The current code does not support the proposed availability-path premise.
Problems
tools/managed_tool_gateway.py:75-93documents and implementspeek_nous_access_token()specifically so banner/status/provider availability scans avoid synchronous refresh.is_managed_tool_gateway_ready()selects that reader attools/managed_tool_gateway.py:176-192;tests/tools/test_managed_tool_gateway.py:103-134verifies an expired token produces noresolve_nous_access_token()call.- The new early return at
hermes_cli/auth.py:5338-5343precedes both_provider_state_transaction()and the existingrefresh_skew_secondscheck, so it can return a cached token despite changed persisted state or a caller requiring a stricter refresh window.
Suggested changes
- Please re-scope from a reproduced current refresh path rather than the availability scan, and add a regression test for that concrete caller. Any cache must preserve the existing state and refresh-skew checks.
Automated hermes-sweeper review.
| # Memo: collapse the startup burst of managed-tool check_fns into one | ||
| # network refresh. Only cache a successful, non-forced resolution for a | ||
| # short window; force_fresh / error paths bypass and don't populate it. | ||
| if not insecure and ca_bundle is None: |
There was a problem hiding this comment.
This cache is consulted before _provider_state_transaction() and before _is_expiring(..., refresh_skew_seconds), so it can return a token after auth state changes or when this caller's requested skew requires a refresh. The documented banner availability path already uses peek_nous_access_token() and does not call this resolver.
|
Merged via #76930 — thank you @JeffStone69. Your commit was cherry-picked, so you remain the author in git history. The memo design was sound — correct scoping (insecure/ca_bundle bypass), thread-safe, refresh-failure never cached, and the 5s TTL is provably safe against the token's ≥120s refresh skew. The salvage extended it with one follow-up: populating the memo on the valid-token fast path too (the startup burst usually finds a valid token, and that return still paid the cross-process lock/read cost per check_fn), plus dedicated memo tests. Your core mechanism is unchanged. |
Problem
check_tool_availabilityruns once per managed-toolcheck_fn(browser, image_gen, etc.) during banner render. Each one independently triggers a ~15s blocking Nous Portal token-refresh network call (resolve_nous_access_token->_refresh_access_token->httpx.post) when the stored token is expired.On a slow/constrained host (a small monitoring CT observed this) that serial burst stretched
hermesstartup to many minutes, appearing "stalled" — the user had to Ctrl-C out of it. Thehttpxclient already has a 15s connect timeout, so it isn't an infinite hang; it's N×15s where N is the number of managed-tool checks.Fix
Add a per-process memo (5s TTL) to
resolve_nous_access_tokenso the startup burst collapses into a single network round-trip. Only successful, non-forced resolutions are cached.force_freshcallers andinsecure/ca_bundlecallers bypass the cache and never populate it, so normal refresh semantics (interactive re-auth, custom TLS) are unchanged.Verification
auth.pyparses clean.resolve_nous_access_token()calls (against a fake expired-token state with the network refresh monkeypatched to count) -> exactly 1 underlying refresh, identical token returned all 3 times.UnboundLocalError(module global read before the later in-function assignment made Python treat it as local) by addingglobal _RESOLVE_TOKEN_CACHE.Note
This addresses the startup slowness class. The separate exit-watchdog fix (PR #65998) covers shutdown wedges; they are different bugs.