What happened
CachingSemanticConfigStorage (semantic_memory/config_store/caching_semantic_config_storage.py) caches set configs, registered set ids, categories, tags, set-type categories and set-type lists per process (:40-48) with no TTL. Its docstring (:21-24) says entries are invalidated on write operations, which means writes through the same instance. semantic_memory.with_config_cache defaults to true (common/configuration/__init__.py:154-157). #1730 covers the two instances inside one process; this issue is the cross-replica case, which no in-process invalidation fixes.
Three consequences:
- A category, tag or set type created, disabled or deleted on replica A is invisible to replica B's searches and to B's background ingestion, which keeps extracting with B's cached categories and prompts (
semantic_memory/semantic_memory.py:505 reads the cached config for ingestion).
- Lost update.
_set_id_resource (semantic_memory/semantic_memory.py:505-515) calls set_setid_config(set_id=..., embedder_name=<default>) whenever its cached config has no embedder. The upsert (semantic_memory/config_store/config_store_sqlalchemy.py:276-285) writes both embedder_name and language_model_name, and this call passes no language model. After configure_set(embedder_name=X, llm_name=Y) on replica A, the first ingestion or search on replica B that still holds the pre-configure config resets the set to the default embedder and a null language model, and B embeds the set with the wrong model. The same statement erases llm_name on a single process whenever embedder_name is unset and llm_name is set.
_registered_set_id_set_types (:41) is cleared only by the local delete_set_type_id, so after a set type is deleted on A, B skips re-registration for the set ids it remembers.
Expected
The cache is off by default for multi-replica deployments, or it is coherent: a version column read per request, or a TTL stated in the configuration docs. Independently, set_setid_config must not overwrite fields it was not given.
Notes
Code read at d6068cdbf (main), paths under packages/server/src/memmachine_server/. Not reproduced with two live replicas; the lost update follows from the upsert writing both columns unconditionally. Lower priority than the session lifecycle (#1655).
🤖 Written by Claude Code (Claude Fable 5.1) on behalf of @edwinyyyu.
What happened
CachingSemanticConfigStorage(semantic_memory/config_store/caching_semantic_config_storage.py) caches set configs, registered set ids, categories, tags, set-type categories and set-type lists per process (:40-48) with no TTL. Its docstring (:21-24) says entries are invalidated on write operations, which means writes through the same instance.semantic_memory.with_config_cachedefaults to true (common/configuration/__init__.py:154-157). #1730 covers the two instances inside one process; this issue is the cross-replica case, which no in-process invalidation fixes.Three consequences:
semantic_memory/semantic_memory.py:505reads the cached config for ingestion)._set_id_resource(semantic_memory/semantic_memory.py:505-515) callsset_setid_config(set_id=..., embedder_name=<default>)whenever its cached config has no embedder. The upsert (semantic_memory/config_store/config_store_sqlalchemy.py:276-285) writes bothembedder_nameandlanguage_model_name, and this call passes no language model. Afterconfigure_set(embedder_name=X, llm_name=Y)on replica A, the first ingestion or search on replica B that still holds the pre-configure config resets the set to the default embedder and a null language model, and B embeds the set with the wrong model. The same statement erasesllm_nameon a single process wheneverembedder_nameis unset andllm_nameis set._registered_set_id_set_types(:41) is cleared only by the localdelete_set_type_id, so after a set type is deleted on A, B skips re-registration for the set ids it remembers.Expected
The cache is off by default for multi-replica deployments, or it is coherent: a version column read per request, or a TTL stated in the configuration docs. Independently,
set_setid_configmust not overwrite fields it was not given.Notes
Code read at
d6068cdbf(main), paths underpackages/server/src/memmachine_server/. Not reproduced with two live replicas; the lost update follows from the upsert writing both columns unconditionally. Lower priority than the session lifecycle (#1655).🤖 Written by Claude Code (Claude Fable 5.1) on behalf of @edwinyyyu.