fix(mcp): keep a held OAuth metadata lock registered even when no fetcher slot claims it

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
yucheng 2026-09-28 23:04:40 +00:00
parent 9786e3509a
commit a3c3ca08cd

View file

@ -142,7 +142,7 @@ def _prune_oauth_metadata_cache(now: float | None = None) -> None:
# Drop locks whose cache entry has been evicted and that nobody holds or
# waits on; the rest stay so in-flight callers continue to coalesce.
for cache_key in list(_OAUTH_METADATA_FETCH_LOCKS):
if cache_key in _OAUTH_METADATA_CACHE or cache_key in _OAUTH_METADATA_FETCHERS:
if cache_key in _OAUTH_METADATA_CACHE or not _oauth_metadata_lock_idle(cache_key):
continue
_OAUTH_METADATA_FETCH_LOCKS.pop(cache_key, None)
@ -154,6 +154,13 @@ def _oauth_metadata_fetch_in_flight(server_id: str) -> bool:
return any(cache_key[0] == server_id for cache_key in _OAUTH_METADATA_FETCHERS)
def _oauth_metadata_lock_idle(cache_key: tuple[str, str]) -> bool:
if cache_key in _OAUTH_METADATA_FETCHERS:
return False
lock: Final = _OAUTH_METADATA_FETCH_LOCKS.get(cache_key)
return lock is None or not lock.locked()
@asynccontextmanager
async def _oauth_metadata_fetch_slot(cache_key: tuple[str, str]) -> AsyncIterator[None]:
_OAUTH_METADATA_FETCHERS[cache_key] = _OAUTH_METADATA_FETCHERS.get(cache_key, 0) + 1
@ -177,7 +184,7 @@ def invalidate_oauth_metadata_cache(server_id: str) -> None:
for cache_key in [key for key in _OAUTH_METADATA_CACHE if key[0] == server_id]:
del _OAUTH_METADATA_CACHE[cache_key]
for cache_key in [key for key in _OAUTH_METADATA_FETCH_LOCKS if key[0] == server_id]:
if cache_key in _OAUTH_METADATA_FETCHERS:
if not _oauth_metadata_lock_idle(cache_key):
continue
_OAUTH_METADATA_FETCH_LOCKS.pop(cache_key, None)