perf(api): add optional Redis read caching - #1168
Conversation
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8bff587dd7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1f146493ae
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 621f642efc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 87ee016a53
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c80d631212
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 78e109d750
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5994aca43f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2b7eea3ffc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9f02ff39d8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
Signed-off-by: phernandez <paul@basicmachines.co>
Signed-off-by: phernandez <paul@basicmachines.co>
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d4121acdf2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 007b54ca43
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b8c5f72470
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1badd6fbf4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 53076d7731
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 930b61f2de
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d46d420805
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 596356038d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 305c85879b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a562574723
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: efed2c8ab8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
Signed-off-by: phernandez <paul@basicmachines.co>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7428125a10
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: phernandez <paul@basicmachines.co>
|
@codex review |
|
Codex Review: Didn't find any major issues. What shall we delve into next? Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Why
Repeated Basic Memory reads currently execute the authoritative database or storage path even
when agents and the web explorer ask for the same data again. This PR gives Basic Memory
ownership of its semantic read-cache policy while preserving the existing boundary: Cloud owns
tenant/control-plane caching and rate limiting; Basic Memory owns caching for its own read API.
Production Logfire data from July 30 calibrated the phase-one surface:
read_notesearchThe existing Cloud gateway cache showed useful directory locality: 62.5% for directory
structures, 23.0% for directory lists, and 14.8% for full directory trees. On web folder-list
traces, a directory miss plus a project-list tenant dispatch averaged about 2.4 seconds, while
requests with cached dependencies averaged about 0.64 seconds. That evidence moves Basic Memory
directory reads into phase one. Search remains deferred because latency and volume are clear but
exact repeated-query locality is not yet observable.
Redis remains optional. Local-first installations keep the current default behavior and do not
need a Redis server or import redis-py.
What Changed
redis>=8,<9package extra and a narrow asyncReadCacheprotocol.The configured backend is
ReadCache | None; disabled installations do not construct or calla no-op cache.
invalidation, bounded generation and response TTLs, payload limits, stale-fill protection,
operational bypass behavior, and low-cardinality cache telemetry.
ModelReadCache[ResponseType]facades. Each facade binds one Pydanticresponse type and its policy while all facades share one injected backend, Redis pool,
namespace, and project generation.
define a named cache key, enter
read_cache.read(...), and keep the authoritative operationinline. Mutation and indexing flows use
invalidate_cache(...)at their durable boundary.hosted read repair, import attempts, direct and watcher indexing, project indexing and search
reindexing, relation resolution, directory moves and deletes, project-root changes, and startup
recovery. These hooks do not make indexing read from Redis; they prevent API responses from
remaining valid after an authoritative mutation commits.
status publication, before potentially slow indexing begins, and again after indexing. Status
polling therefore cannot retain a cached
pendingorwritingentity throughout the index.InvalidatingIndexFileExecutor, which wraps the transaction-bearing individual-fileindexing operation. Local watcher indexing composes it only when caching is enabled. Hosted
storage-event workers can compose the same decorator at the equivalent boundary.
failures, payload policy, and cancellation. Redis is not mocked or emulated.
docs/REDIS_READ_CACHE_PLAN.mdwith the measured production calibration, Cloudintegration requirements, testing strategy, rollout stages, and intentionally deferred work.
Implementation Details
Key and invalidation model
The keyspace is versioned and Redis Cluster compatible:
scope_digesthashes a host-owned namespace plus the canonical project external UUID. The hostnamespace is the tenant isolation boundary; the project UUID is an inner semantic scope.
Authorization and tenant database selection still happen before cache lookup.
Cache values carry the project generation observed by their lookup. Lua scripts initialize
generations atomically and store a completed miss only while that generation is still current, so
a slow read cannot repopulate stale data after a concurrent mutation. Generation keys and
responses both have bounded TTLs; inactive projects age out without scans.
Operational Redis failures such as connection, timeout, capacity, read-only replica, and server
response errors bypass reads or become observable best-effort invalidation failures after a
committed write. Invalid data, client-input errors, and local programming errors fail fast. The
initial 60-second response TTL bounds stale exposure if post-commit invalidation is temporarily
unavailable.
Python and FastAPI composition
The Core composition root exposes an optional raw backend. FastAPI dependencies wrap that backend
in response-specific
ModelReadCache[ResponseType]facades. Routes do not pass a model type foreach lookup, reach through to Redis, install loader callbacks, or implement disabled states.
The read path stays ordinary Python:
The actual routes keep the authoritative flow inline rather than extracting artificial callbacks.
Named keys make the context-manager boundary easy to scan.
Directory list keys include directory name, depth, file-name glob, page, and page size. Directory
tree and structure responses use a measured two MiB payload ceiling: today's production
directory-tree maximum was about 1.29 MiB and the seven-day structure maximum was about 0.92 MiB.
Directory-list responses keep the generic one MiB ceiling.
Cloud requirements
No Cloud code changes are included in this PR. Cloud must complete the following wiring before
enabling Basic Memory cached reads:
for authenticated requests and workers; do not create a Redis server, client, pool, or model
facade per tenant.
its UUID. Never accept the namespace from a public request or derive it from a display name,
slug, or API key.
not replace the tenant isolation boundary.
publish project state. Do not enable cached reads until request and worker invalidation have
namespace parity.
response-specific facades, types, TTLs, payload limits, and generation behavior.
build_cloud_index_file_runtime, decorate the transaction-bearingFileIndexerwithInvalidatingIndexFileExecutorbefore passing it torun_index_file. This places invalidationafter each committed file even when cancellation or later search/reconciliation work fails.
workers, project indexing and reindexing, read repair, import, relation resolution, moves,
deletes, and recovery. Preserve Core's cancellation-safe committed-mutation boundaries.
Invalidate immediately after the terminal status transaction returns, before indexing starts,
and retain a second invalidation after indexing.
GatewayCacheinvalidation temporarily for outer response-cache andlive-update parity. It is not the Basic Memory semantic-cache correctness boundary.
GET /api/v2/projectscomposesaccessible workspaces and active-project tenant queries; it is not a Basic Memory semantic
read and this PR does not accelerate it.
database queries, and end-to-end MCP and web latency. Remove overlapping Cloud directory,
entity, and resource response caches only after behavior and observability reach parity.
These requirements are also checked in at
docs/REDIS_READ_CACHE_PLAN.md.Testing
All cache integration tests use a real Redis 8.8 server through testcontainers unless
BASIC_MEMORY_TEST_REDIS_URLpoints to an existing server.LOGFIRE_IGNORE_NO_CONFIG=1 uv run pytest -p pytest_mock --no-cov -q test-int/read_cacheBASIC_MEMORY_TEST_POSTGRES=1 LOGFIRE_IGNORE_NO_CONFIG=1 uv run pytest -p pytest_mock --no-cov -q test-int/read_cacheLOGFIRE_IGNORE_NO_CONFIG=1 uv run pytest -p pytest_mock --no-cov -q test-int/read_cache/test_materialization_invalidation.py tests/cloud/test_note_content_materialization.pyadvances the generation before the final post-index bump.
LOGFIRE_IGNORE_NO_CONFIG=1 uv run pytest -p pytest_mock --no-cov -q test-int/read_cache/test_api_read_cache.py tests/api/v2/test_directory_router.pyLOGFIRE_IGNORE_NO_CONFIG=1 just test-sqliteplatform/backend skips and semantic benchmark deselections.
just fast-checktyon the current head.just doctoruv lock --checkuv run mdformat --check docs/REDIS_READ_CACHE_PLAN.mdgit diff --checkGitHub-hosted Windows skips the real-Redis integration module when no external
BASIC_MEMORY_TEST_REDIS_URLis configured because it cannot start the Linux testcontainer. TheLinux SQLite and PostgreSQL jobs retain the real-server gate.
Local benchmarks prove that hits avoid authoritative database work; they are not hosted-latency
promises:
Risks / Follow-ups
Narrower invalidation should follow only if production telemetry shows that unrelated writes
materially reduce useful hits.
60-second TTL expires. The failure is observable and deliberately does not fail an already
committed authoritative write.
safely represent both dependency scopes. The broader resolve API contract is tracked
separately.
it promising, but caching cannot help predominantly unique queries.
tree and folder navigation work, but it does not remove active-project reconciliation or
Cloud-side excerpt, share, sorting, and pagination work.
would complicate freshness reasoning and obscure which layer produced the benefit.
threads are resolved, and Codex posts a fresh current-head approval.