personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

apps/entities: invalidate obs count cache on save, drop per-read stat

wire clear_observation_count_cache() into save_observations and _clear_merge_caches so the count cache invalidates on the same write paths as its sibling caches.

drop the Path.stat() / st_mtime_ns check from count_observations; warm reads now make zero syscalls, matching the four sibling caches in this module.

follow up dd5c0327 (apps/entities: warm caches and memoize obs counts on all-facets path).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+15 -11
+7 -1
think/entities/merge.py
··· 17 17 scan_journal_entities, 18 18 ) 19 19 from think.entities.loading import clear_entity_loading_cache 20 - from think.entities.observations import clear_observation_cache, save_observations 20 + from think.entities.observations import ( 21 + clear_observation_cache, 22 + clear_observation_count_cache, 23 + save_observations, 24 + ) 21 25 from think.entities.relationships import ( 22 26 clear_relationship_caches, 23 27 save_facet_relationship, ··· 549 553 clear_journal_entity_cache() 550 554 clear_relationship_caches() 551 555 clear_observation_cache() 556 + clear_observation_count_cache() 552 557 clear_entity_loading_cache() 553 558 return [ 554 559 "journal_entity_cache", 555 560 "relationship_caches", 556 561 "observation_cache", 562 + "observation_count_cache", 557 563 "entity_loading_cache", 558 564 ] 559 565
+8 -10
think/entities/observations.py
··· 23 23 24 24 # Global cache for entity observations: {(facet, entity_slug): list[dict]} 25 25 _OBSERVATION_CACHE: dict[tuple[str, str], list[dict[str, Any]]] | None = None 26 - # Global cache for observation counts: {path: (mtime_ns, count)} 27 - _OBSERVATION_COUNT_CACHE: dict[Path, tuple[int, int]] | None = None 26 + # Global cache for observation counts: {path: count} 27 + _OBSERVATION_COUNT_CACHE: dict[Path, int] | None = None 28 28 29 29 30 30 def clear_observation_cache() -> None: ··· 111 111 """Count observations for an entity.""" 112 112 global _OBSERVATION_COUNT_CACHE 113 113 try: 114 - folder = entity_memory_path(facet, name) 114 + obs_file = entity_memory_path(facet, name) / "observations.jsonl" 115 115 except ValueError: 116 116 return 0 117 117 118 - obs_file = folder / "observations.jsonl" 119 - try: 120 - st = obs_file.stat() 121 - except OSError: 118 + if not obs_file.exists(): 122 119 return 0 123 120 124 121 if _OBSERVATION_COUNT_CACHE is None: 125 122 _OBSERVATION_COUNT_CACHE = {} 126 123 127 124 cached = _OBSERVATION_COUNT_CACHE.get(obs_file) 128 - if cached is not None and cached[0] == st.st_mtime_ns: 129 - return cached[1] 125 + if cached is not None: 126 + return cached 130 127 131 128 try: 132 129 with open(obs_file, "r", encoding="utf-8") as f: ··· 134 131 except OSError: 135 132 return 0 136 133 137 - _OBSERVATION_COUNT_CACHE[obs_file] = (st.st_mtime_ns, count) 134 + _OBSERVATION_COUNT_CACHE[obs_file] = count 138 135 return count 139 136 140 137 ··· 150 147 """ 151 148 # Clear cache on modification 152 149 clear_observation_cache() 150 + clear_observation_count_cache() 153 151 154 152 path = observations_file_path(facet, name) 155 153