Skip to content

Episode count cache is per process and on by default, so episode counts go stale across replicas #1746

Description

@edwinyyyu

What happened

CountCachingEpisodeStorage (common/episode_store/count_caching_episode_storage.py) keeps _count_cache: dict[str, _CacheEntry] (:49) behind an asyncio.Lock (:50). It is filled on the first session-only count (:128-131), incremented only by this process's own add_episodes (:64-71), and cleared only by this process's own deletes (:148-153, :167-171). There is no TTL and no cross-process invalidation. Its own docstring (:39-44) says: "As an incoherent cache, changes to the underlying store are not reflected in the cache. Making this cache unsuited to concurrent deployment."

The wrapper is enabled by episode_store.with_count_cache, which defaults to true (common/configuration/__init__.py:89-92), and POST /projects/episode_count/get reads through it (MemMachine.episodes_count, main/memmachine.py:1130).

With two replicas: once replica A has served a count for a session, adds and deletes served by replica B never reach A's cache, and A keeps returning the stale number until A itself deletes something.

Expected

Either the cache is off by default and documented as a single-process optimization, or the count is coherent: a counter maintained in the same transaction as the insert, or a short TTL with the staleness stated. docs/open_source/configuration.mdx recommends the flag today with no caveat.

Notes

Code read at d6068cdbf (main), paths under packages/server/src/memmachine_server/. Not reproduced with two live replicas; the cache has no invalidation path except local writes, so the staleness follows from the code.


🤖 Written by Claude Code (Claude Fable 5.1) on behalf of @edwinyyyu.

Metadata

Metadata

Assignees

No one assigned

    Labels

    horizontal scalingWrong or unsafe when more than one server process serves the same backends (replicas or workers)

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions