Skip to content

Warm the newest weekly report first, and build a cold page once - #137

Merged
mspinola merged 1 commit into
mainfrom
claude/weekly-newest-warm
Sep 25, 2026
Merged

mspinola merged 1 commit into
mainfrom
claude/weekly-newest-warm

Conversation

@mspinola

Copy link
Copy Markdown
Owner

Problem

Right after a restart on the VPS, /weekly/2026-09-22 returned 504 at 60.4s, then 200 in 54.2s, and verify-deploy.sh saw both weekly checks as 000.

What the profile found

Local cold render of report_page(newest) with the real stores (47-market params): 3.8s, of which 3.4s is get_symbols_data (mostly append_trading_signals, the trendline-break polyfit rolling apply the largest single piece at ~1.1s).

The existing note blamed a missing cache between the explicit-date matrix and the boards' None-date one. Measurement does not support that. Both go through get_symbols_data's lru_cache, so building one after the other costs ~0.1s.

VPS journal for the 16:17:52 restart: the home-page prewarm (pages/home.py::_prewarm_cache) ran 16:18:49 to 16:22:11, and nothing was served from 16:20:10 to 16:22:10. The two queued weekly requests completed at 16:22:10, the moment the prewarm finished. The warmer's heatmap and divergence steps ran ~8-9x slower than locally. So the 54s came from the same per-market frames being built in parallel by the home prewarm, the crowd warmer and each piled-up request (lru_cache does not coalesce concurrent misses) on a slower, GIL-bound box.

Change

  • report_page renders under a lock, so concurrent requests for a cold page build it once and the rest read the cache.
  • weekly_reports.warm_newest() now runs first in warm_page_caches (boot, and after a release). It still runs after the email, per Send the weekly email before warming the page caches #136. It costs the crowd warmer nothing, since both share the per-market cache.
  • The store poller also warms after a sent outcome on a tick that saw no new week. A navbar poll can consume refresh_if_stale's True, which would leave the page the email links to cold.
  • Corrected the _rendered docstring's cache claim.

Local check: warmer plus four concurrent requests for the cold newest page gave one matrix build, with all four served at 4.9s.

Not done here

  • The irreducible first build in a fresh process is ~3.5s locally, roughly 30s on the VPS. Making it cheaper means changing cotmetrics.signals.append_trading_signals, which should be its own cotmetrics change.
  • Right after a restart, verify-deploy.sh can still land in the warm window. The newest page is ready once the log says weekly pages: warmed <date>.

Tests

pytest tests/ gives 886 passed (CI env: dummy stores, PYTHONPATH=src). ruff check src tests is clean (ruff 0.15.22). New tests: concurrent cold requests build once, warm_newest fills the newest page and never raises, and the warm order pins weekly first.

Not deployed.

🤖 Generated with Claude Code

After a restart on the VPS, /weekly/2026-09-22 answered 504 at nginx's 60s
and then 200 in 54s. Profiled locally with the real stores: a cold render
is ~3.8s, essentially all of it get_symbols_data's first per-market build
(append_trading_signals). The explicit-date matrix does share that cache
with the boards' newest-week build (the second costs ~0.1s), so the note
blaming a missing cache was wrong. The VPS journal shows what happened
instead: the home-page prewarm, the crowd warmer and every piled-up request
built the same frames in parallel on a ~9x slower box, and the two weekly
requests finished the moment the home prewarm did, 3.5 minutes after boot.

- report_page renders under a lock: lru_cache does not coalesce concurrent
  misses, so waiters now read the page the first render cached.
- weekly_reports.warm_newest, called first in warm_page_caches (boot and
  after a release, still after the email per #136). It costs the crowd
  warmer nothing, both ride the same per-market cache.
- The poller also warms after a send on a tick that saw no new week, since
  a navbar poll can consume refresh_if_stale's True and leave the page the
  email links to cold.

Locally, warmer plus four concurrent requests for the cold newest page: one
matrix build, all four served at 4.9s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mspinola
mspinola merged commit 831fdbd into main Sep 25, 2026
3 checks passed
@mspinola
mspinola deleted the claude/weekly-newest-warm branch September 25, 2026 20:41
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