Skip to content

Optimize caching, polling, and retry strategies for performance#759

Merged
jamespepper81 merged 3 commits into
devfrom
claude/keen-faraday-qvIBZ
Jun 1, 2026
Merged

Optimize caching, polling, and retry strategies for performance#759
jamespepper81 merged 3 commits into
devfrom
claude/keen-faraday-qvIBZ

Conversation

@jamespepper81
Copy link
Copy Markdown
Contributor

Summary

This PR implements comprehensive performance optimizations across caching, polling intervals, and retry logic to reduce unnecessary origin requests and improve user experience.

Key changes:

  • HTTP caching headers: Added cache control rules for static assets (1 week), manifest (1 day), marketing pages (1 hour edge cache), and app shells (5 min edge cache with SWR)
  • Polling intervals: Increased from 60s to 5 minutes for market data and wallet price updates to reduce origin load
  • Update check: Refactored from fixed 60s interval to 15-minute base interval with jitter, visibility detection, and proper cleanup
  • Error retry logic: Implemented exponential backoff (3s → 6s → 12s → 60s cap) for wallet data failures with immediate retry on tab focus
  • Sitemap: Switched to stable LAST_MODIFIED timestamp to prevent crawlers from seeing the sitemap as "always new"
  • Marketing pages: Added revalidate = 3600 to landing and about pages for ISR-based edge caching

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Refactor / cleanup

Test Plan

  • Verified HTTP caching headers are correctly set in next.config.ts
  • Confirmed polling intervals are increased to 5 minutes for market and wallet data
  • Tested update check logic with visibility state detection and jitter
  • Verified exponential backoff retry logic with proper timeout cleanup
  • Confirmed sitemap uses stable timestamp and marketing pages have revalidate set
  • Existing tests pass; no new test files added as changes are configuration and interval adjustments

Checklist

  • npm run typecheck passes
  • npm run lint passes
  • npm run test passes
  • Documentation updated (if applicable)

https://claude.ai/code/session_01PAJeRubmNYpCjy1PvvyfgJ

claude added 2 commits June 1, 2026 20:41
Reduce origin (Cloud Run) request load that was driving the ~9x request
spike:

- Update-version checker (app layout): was fetching the full current page
  with `no-store` every 60s on every signed-in tab, forever. Now polls
  ~every 15 min, only while the tab is visible, with jitter so clients
  don't poll in lockstep, and stops once an update is detected. This was
  the single biggest driver of both the request spike and uncached CDN
  bandwidth.
- Market page: the 60s refresh is a Server Action hitting the origin;
  throttled to 5 min (underlying market data revalidates on 60s+ anyway).
- BTC price ticker: throttled from 60s to 5 min.
- Wallet error-retry: replaced the fixed 3s retry with exponential backoff
  (3s/6s/12s, capped at 60s) and debounced the on-focus retry so a focus
  storm can't fire back-to-back origin calls.

https://claude.ai/code/session_01PAJeRubmNYpCjy1PvvyfgJ
Address the Firebase App Hosting CDN cache-miss rate (uncached origin
bandwidth jumped 52 GiB -> 726 GiB). App Hosting only edge-caches
responses whose origin emits a cacheable Cache-Control header, and almost
nothing did.

next.config.ts headers():
- /public static assets (svg/png/jpg/webp/ico/woff/woff2): 1 week + SWR.
- manifest.json: 1 day.
- /landing and /about (public marketing pages): s-maxage 1h + SWR.
- Signed-in app shells (dashboard, analysis, security, chat, report,
  coin-control): s-maxage 5m + SWR. These render an identical shell per
  user — wallet data loads client-side via Server Actions and auth gating
  is client-side — so only the static shell is cached; per-user data
  requests are separate POSTs that always reach the origin. Parameterised
  routes (address/[address] etc.) and /api/* are intentionally excluded.

Also:
- /landing and /about: add `revalidate = 3600` (ISR) to back the headers.
- sitemap: use a stable lastModified instead of `new Date()` so it stops
  appearing "always new" to crawlers on every build.

https://claude.ai/code/session_01PAJeRubmNYpCjy1PvvyfgJ
@jamespepper81
Copy link
Copy Markdown
Contributor Author

@claude can you code review the pr 759 and make any changes required

Code-review follow-up. In the version-update checker, if the
`fetch(window.location.href)` is still in flight when the effect is
cleaned up (component unmounted), the resolved callback could call
`setIsUpdateAvailable` on an unmounted component. The cleanup already
sets the shared `stopped` flag, so check it before the state update.

https://claude.ai/code/session_01PAJeRubmNYpCjy1PvvyfgJ
Copy link
Copy Markdown
Contributor Author

Code review — PR #759

Reviewed the full diff (caching headers + polling/backoff changes) at high effort, verifying the riskiest parts (Next.js header source-pattern matching and the rewritten timer/retry logic) with focused checks. No correctness bugs found. Details:

  • next.config.ts header patterns — sound. /:path*.(svg|png|…) matches nested assets (e.g. /icons/icon-192x192.svg) without over-matching pages/APIs; /(landing|about) and /(dashboard|…) match only the exact single-segment routes (dynamic [param] routes correctly excluded). Next.js uses first-match-wins, so the /favicon.ico and /_next/static/*.woff2 overlaps resolve to the earlier (intended) rule. Verified empirically with curl against a production build.
  • Update-checker rewrite (layout.tsx) — no TDZ (the check reference is captured in a closure and only dereferenced when the timer fires, after declaration), single live timer, cleanup cancels it.
  • Retry backoff (wallet-context.tsx) — math is correct (3s → 6s → 12s → stop); the >= 3 cap is checked before scheduling, so a focus storm can't bypass it; pending timer is cleared on cleanup.

One minor fix applied (fed7174): in the update-checker, if the fetch was still in flight when the effect cleaned up, setIsUpdateAvailable could fire after unmount. The cleanup already sets the shared stopped flag, so I now check it before the state update.

Everything else is good to go. The one thing worth a human eye before merge is the intended-but-sensitive change: signed-in app shells (dashboard/analysis/security/chat/report/coin-control) are now edge-cacheable — safe because those shells are user-independent (wallet data loads client-side), but it's a one-rule revert in next.config.ts if you'd prefer to keep them uncached.


Generated by Claude Code

@jamespepper81 jamespepper81 merged commit 5699227 into dev Jun 1, 2026
5 checks passed
@jamespepper81 jamespepper81 deleted the claude/keen-faraday-qvIBZ branch June 1, 2026 21:14
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.

2 participants