@@ -82,13 +82,21 @@ async def tool_repo_summary(input: RepoSummaryInput) -> RepoSummaryOutput:
8282 cache_key = _normalize_cache_key (effective_url )
8383 db = get_cache_db ()
8484
85+ # --- Cache read outside the lock so we don't block other coroutines ---
86+ raw = await asyncio .to_thread (db .get , _REPO_INFO_NS , cache_key )
87+ if raw is not None :
88+ log .info (f"Repo info cache hit for { effective_url } " )
89+ return RepoSummaryOutput .model_validate_json (raw )
90+
91+ # --- Lock only for inflight bookkeeping (pure in-memory, no I/O) ---
8592 await _REPO_INFO_LOCK .acquire ()
8693 try :
94+ # Re-check the cache inside the lock in case another coroutine wrote it
95+ # between our lockless read above and acquiring the lock now.
8796 raw = await asyncio .to_thread (db .get , _REPO_INFO_NS , cache_key )
8897 if raw is not None :
89- cached = RepoSummaryOutput .model_validate_json (raw )
90- log .info (f"Repo info cache hit for { effective_url } " )
91- return cached
98+ log .info (f"Repo info cache hit (after lock) for { effective_url } " )
99+ return RepoSummaryOutput .model_validate_json (raw )
92100
93101 inflight = _REPO_INFO_INFLIGHT .get (cache_key )
94102 if inflight is None :
@@ -117,18 +125,20 @@ async def tool_repo_summary(input: RepoSummaryInput) -> RepoSummaryOutput:
117125 _REPO_INFO_LOCK .release ()
118126 raise
119127
128+ # --- DB write outside the lock so waiting coroutines aren't blocked ---
129+ if result .source != "error" and REPO_INFO_CACHE_TTL_SECONDS > 0 :
130+ await asyncio .to_thread (
131+ db .set ,
132+ _REPO_INFO_NS ,
133+ cache_key ,
134+ result .model_dump_json (),
135+ ttl_seconds = REPO_INFO_CACHE_TTL_SECONDS ,
136+ max_entries = REPO_INFO_CACHE_MAX_ENTRIES ,
137+ )
138+
139+ # --- Re-acquire lock only to resolve the future and clean up bookkeeping ---
120140 await _REPO_INFO_LOCK .acquire ()
121141 try :
122- if result .source != "error" and REPO_INFO_CACHE_TTL_SECONDS > 0 :
123- await asyncio .to_thread (
124- db .set ,
125- _REPO_INFO_NS ,
126- cache_key ,
127- result .model_dump_json (),
128- ttl_seconds = REPO_INFO_CACHE_TTL_SECONDS ,
129- max_entries = REPO_INFO_CACHE_MAX_ENTRIES ,
130- )
131-
132142 if not inflight .done ():
133143 inflight .set_result (result )
134144 _REPO_INFO_INFLIGHT .pop (cache_key , None )
0 commit comments