Skip to content

fix(wren): bound the session-context cache to 32 LRU entries - #2628

Open
ttw225 wants to merge 1 commit into
Canner:mainfrom
ttw225:perf/wren-bounded-session-cache
Open

fix(wren): bound the session-context cache to 32 LRU entries#2628
ttw225 wants to merge 1 commit into
Canner:mainfrom
ttw225:perf/wren-bounded-session-cache

Conversation

@ttw225

@ttw225 ttw225 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

get_session_context used functools.cache, and its cache key includes the per-query extracted manifest. As a long-lived process encountered distinct extracted manifests, the cache retained one SessionContext for each argument tuple for the lifetime of the process.

This PR replaces it with lru_cache(maxsize=32). The cache now retains at most the 32 most recently used session contexts; an evicted entry is rebuilt when its argument tuple is used again.

What failure does this repair?

The previous cache had no entry-count bound. WrenEngine._plan() passes the per-query extracted sub-manifest through:

extract_by(tables) → effective_manifest → get_session_context(...)

On the unfixed code, calling get_session_context with N distinct manifest_str values grows cache_info().currsize to N, while cache_info().maxsize remains None. Each entry retains a SessionContext and its analyzed manifest state, so varied workloads can accumulate retained contexts indefinitely.

The value 32 is an explicit initial policy: it establishes a hard bound while keeping recently used planning contexts warm. It is not derived from production memory or cache-hit measurements. Configurability can be added later if operational evidence shows that a different bound is needed.

The runtime-ownership prerequisite shipped in wren-core-py v0.7.2 via #2510: session contexts use a process-wide runtime rather than owning independent runtimes. Evicting a cached context therefore does not tear down a context-specific worker pool.

Cache eviction removes only the cache's reference. An in-flight caller retains its local reference until the operation completes. As with functools.cache, lru_cache is internally synchronized; concurrent misses may briefly build the same value more than once, but only one entry is retained for that key.

How is it tested?

tests/unit/test_session_context_cache.py uses the decorated production function while replacing the native SessionContext constructor with a fake:

  • test_cache_is_bounded_to_32 pins the capacity against the test-side EXPECTED_MAXSIZE = 32 and verifies that currsize stops at the bound.
  • test_cache_evicts_least_recently_used verifies that access refreshes recency, the surviving key returns the same instance, and the evicted key is rebuilt as a different instance.
  • Test keys vary manifest_str, matching the production extract_by → effective_manifest → get_session_context path.
  • The original instance is kept alive during the eviction assertion, so object identity proves removal from the cache rather than object destruction.

The regression tests were also verified against the unfixed @cache decorator: the capacity assertion reports maxsize=None, and the eviction assertion fails because the supposedly evicted key still returns its original instance.

Verification:

  • Targeted cache tests: 2 passed
  • Default-CI-equivalent unit suite: 1042 passed, 2 skipped
  • Ruff check and format check: passed

Summary by CodeRabbit

  • Performance

    • Session context caching is now bounded to 32 entries, preventing unbounded memory growth.
    • Older, least-recently-used entries are automatically removed as new contexts are created.
  • Documentation

    • Updated caching guidance to clarify eviction behavior and the need to avoid mutating cached session state.
  • Tests

    • Added coverage verifying the cache limit and least-recently-used eviction behavior.

get_session_context is keyed on the per-query extracted manifest, so
an unbounded functools.cache grows one SessionContext per distinct
table subset for the life of the process. Use lru_cache(maxsize=32).
@github-actions github-actions Bot added documentation Improvements or additions to documentation python Pull requests that update Python code core labels Jul 30, 2026
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c91202dc-5cbb-4eb7-89e0-4c305cd0d010

📥 Commits

Reviewing files that changed from the base of the PR and between 32d76bf and 089c37e.

📒 Files selected for processing (3)
  • core/wren/.claude/CLAUDE.md
  • core/wren/src/wren/mdl/__init__.py
  • core/wren/tests/unit/test_session_context_cache.py

Walkthrough

get_session_context now uses a bounded 32-entry LRU cache. Documentation describes eviction semantics, and isolated unit tests verify the capacity and least-recently-used behavior.

Changes

Session context cache

Layer / File(s) Summary
Bounded LRU cache contract
core/wren/src/wren/mdl/__init__.py, core/wren/.claude/CLAUDE.md
get_session_context uses @lru_cache(maxsize=32), with documentation covering cache keys, eviction, and session-state mutation.
LRU cache validation
core/wren/tests/unit/test_session_context_cache.py
Tests isolate SessionContext creation and verify the configured capacity and least-recently-used eviction behavior.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: goldmedal

Poem

A bunny bounds the cache with care,
Thirty-two slots tucked in there.
Oldest crumbs hop out of sight,
Freshly nibbled keys stay bright.
Tests guard every clever flight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: switching the session-context cache to a bounded 32-entry LRU cache.
Description check ✅ Passed It includes the required summary, failure, and testing sections; the duplicate-check section is omitted but not critical.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core documentation Improvements or additions to documentation python Pull requests that update Python code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant