Skip to content

perf(cache): add functools.lru_cache to hot deterministic reads #98

@AndresL230

Description

@AndresL230

Problem

Several read paths recompute or re-fetch identical data many times within the lifetime of a single worker process. There is no in-process caching today beyond the ad-hoc stat_cache dict in backend/routes/profile.py:491.

Hot candidates:

  • backend/services/graph_service.py — graph reads invoked by the streaming tutor (routes/learn.py) on every chat turn.
  • backend/services/course_context_service.py:66get_course_context() is called frequently and returns DB-aggregated data that changes only on graph updates.
  • Auth/JWT decoding paths in backend/services/auth_guard.py — same token decoded repeatedly across a session.

Proposal

Use functools.lru_cache (or a small TTL-LRU helper) for deterministic, per-process reads where staleness is acceptable for short windows or where we have a clear invalidation hook.

Targets:

  • Graph node/edge fetch helpers in graph_service.py — invalidated explicitly in apply_graph_update (line 375).
  • get_course_context(course_id) — invalidated after the existing update_course_context background task runs.
  • Token decode / require_self claim parsing — TTL bounded to token lifetime.

Acceptance criteria

  • Identify 3–5 functions where lru_cache is safe (pure inputs, no hidden mutation).
  • Add a small cache_clear() hook called from apply_graph_update and from the document/grade post-roll background tasks already in routes/documents.py:567,785,991.
  • Document in CLAUDE.md (Conventions section) that lru_cache is reserved for per-process reads and that any state-mutating function must call the matching cache_clear.
  • Tests cover the invalidation path (mutate → cached read returns fresh value).

Tradeoffs / risks

  • Per-worker only — does not dedupe across uvicorn workers or Docker replicas. That's the Redis issue's job. These two layers compose.
  • Risk of stale reads if invalidation hooks are missed. Mitigation: keep the surface area small and only cache functions whose mutators are already centralized (graph mutations all go through apply_graph_update per CLAUDE.md).
  • lru_cache keys must be hashable — watch for dict/list args.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions