From 11225ab2b01596f98af49061e4bc8abfc20b1823 Mon Sep 17 00:00:00 2001 From: dhairya Date: Sat, 3 Oct 2026 00:22:33 +0530 Subject: [PATCH 1/2] fix(purview): remove replaced cache entries before eviction --- python/packages/purview/agent_framework_purview/_cache.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/packages/purview/agent_framework_purview/_cache.py b/python/packages/purview/agent_framework_purview/_cache.py index df5d03b97ee..1635ea517b1 100644 --- a/python/packages/purview/agent_framework_purview/_cache.py +++ b/python/packages/purview/agent_framework_purview/_cache.py @@ -139,10 +139,10 @@ async def set(self, key: str, value: Any, ttl_seconds: int | None = None) -> Non expiry = time.time() + ttl size = self._estimate_size(value) - # Remove old entry if exists - if key in self._cache: - old_size = self._cache[key][2] - self._current_size_bytes -= old_size + # Remove the old entry so eviction cannot count its size again. + old_entry = self._cache.pop(key, None) + if old_entry is not None: + self._current_size_bytes -= old_entry[2] # Evict if needed self._evict_if_needed(size) From db8cc2e52c6fb6839997f8ce2adb0439d796b815 Mon Sep 17 00:00:00 2001 From: dhairya Date: Sat, 3 Oct 2026 00:24:19 +0530 Subject: [PATCH 2/2] test(purview): cover replacement eviction at cache capacity Added a test to verify cache behavior when updating entries and evicting others based on size constraints. --- .../purview/tests/purview/test_cache.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/python/packages/purview/tests/purview/test_cache.py b/python/packages/purview/tests/purview/test_cache.py index 1892842d424..57c5d603dd5 100644 --- a/python/packages/purview/tests/purview/test_cache.py +++ b/python/packages/purview/tests/purview/test_cache.py @@ -4,6 +4,8 @@ import asyncio +import pytest + from agent_framework_purview._cache import ( InMemoryCacheProvider, create_protection_scopes_cache_key, @@ -65,6 +67,23 @@ async def test_cache_update_existing_key(self) -> None: assert result == "value2" + async def test_cache_growing_update_evicts_other_entries(self, monkeypatch: pytest.MonkeyPatch) -> None: + """Replacing the earliest-expiring entry must only subtract its size once.""" + monkeypatch.setattr("agent_framework_purview._cache.time.time", lambda: 1000.0) + cache = InMemoryCacheProvider(max_size_bytes=20) + + # JSON string quotes add two bytes to each value's estimated size. + await cache.set("older", "a" * 6, ttl_seconds=10) + await cache.set("newer", "b" * 6, ttl_seconds=20) + await cache.set("older", "c" * 14, ttl_seconds=30) + + assert await cache.get("older") == "c" * 14 + assert await cache.get("newer") is None + assert cache._current_size_bytes == 16 + + await cache.remove("older") + assert cache._current_size_bytes == 0 + async def test_cache_remove(self) -> None: """Test removing a cache entry.""" cache = InMemoryCacheProvider()