Uh oh!
There was an error while loading. Please reload this page.
perf(cache): per-process lru_cache for hot deterministic reads (#98) - #302
Conversation
Adds in-process caching to hot reads with a clear invalidation story: - academics.offering_course_id — immutable mapping (an offering's course_id is fixed at creation), lru_cache with no invalidation needed; returns an immutable str. - academics.term_for_offering — immutable offering→term mapping; lru_cache on the body, public fn returns a deepcopy so callers can't corrupt the cache. - course_context_service.get_course_context — lru_cache + deepcopy; invalidated by clear_course_context_cache() wired into update_course_context (both write paths). update_course_context is the choke point apply_graph_update and the doc/grade post-rolls funnel through, so stale aggregates are always dropped. Test isolation: an autouse _clear_lru_caches fixture in conftest resets these caches around every test so mocked DB state can't leak across tests (the full suite passing confirms it). CLAUDE.md Conventions documents the lru_cache rule. Deliberately not cached (documented): graph reads (large mutable + hot invalidation) and token decode (security + needs TTL). Tests: cache-hit avoids 2nd DB read, distinct keys not conflated, deepcopy immunity, and update_course_context → next read is fresh. Full suite 836 passed (2 pre-existing storage-env failures). ruff clean. Spec: specs/98-lru-cache.md Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Warning Review limit reached
Next review available in:38 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying with |
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs | frontend-staging | ec060bd | Commit Preview URL Branch Preview URL | Jul 01 2026, 06:48 PM |
Uh oh!
There was an error while loading. Please reload this page.
Closes#98 (milestone #2, perf). Per-worker caching of hot deterministic reads; composes with the HTTP cache (#99) and a future cross-worker Redis layer (#97).
What
academics.offering_course_id@lru_cachecourse_idis fixed at creation (immutable mapping); returns an immutablestracademics.term_for_offering@lru_cacheon body, public fn deep-copiescourse_context_service.get_course_context@lru_cacheon body, public fn deep-copiesclear_course_context_cache()wired intoupdate_course_context(both write paths)Correctness (the important part)
offering_course_idneeds no invalidation: an offering is a course-in-a-term; itscourse_idis set at creation and never repurposed (offering ids are UUIDs), so the mapping is deterministic for the process lifetime.get_course_contextis hooked:update_course_contextis the choke point thatapply_graph_updateand the doc/grade post-rolls funnel through, so any change to the aggregates drops the stale cached read. Both its write paths (no-enrollment purge + final upsert) call the clear._clear_lru_cachesfixture inconftest.pyresets these caches around every test — the full suite passing proves mocked DB state doesn't leak across tests via a cached read.lru_cacherule (per-process, immutable-or-hooked, hashable args, deep-copy mutable returns).Deliberately NOT cached (documented)
get_graph) — large mutable structures with hot per-turn invalidation; the deep-copy cost + invalidation surface outweigh the win. Better handled by the Redis layer ([P3] Add Redis caching layer for Gemini LLM + OCR extraction #97).require_self— security-sensitive, needs a TTL bounded to token lifetime (out of scope for plainlru_cache).Testing
test_lru_cache.py: cache-hit avoids a 2nd DB read, distinct keys aren't conflated, deep-copy immunity, andupdate_course_context→ next read returns fresh (the invalidation path).test_storage_servicefailures pre-exist onmain— missing SUPABASE env).ruffclean.🤖 Generated with Claude Code