diff --git a/synapse/state/__init__.py b/synapse/state/__init__.py index 48324b2437..8c84156678 100644 --- a/synapse/state/__init__.py +++ b/synapse/state/__init__.py @@ -442,11 +442,10 @@ class StateResolutionHandler(object): self._state_cache = None self.resolve_linearizer = Linearizer(name="state_resolve_lock") - cache_size = 100000 * hs.config.caches.get_factor_for("state_cache") self._state_cache = ExpiringCache( cache_name="state_cache", clock=self.clock, - max_len=cache_size, + max_len=100000, expiry_ms=EVICTION_TIMEOUT_SECONDS * 1000, iterable=True, reset_expiry_on_get=True, diff --git a/synapse/util/caches/__init__.py b/synapse/util/caches/__init__.py index d7cfb83624..07f725a4f7 100644 --- a/synapse/util/caches/__init__.py +++ b/synapse/util/caches/__init__.py @@ -103,7 +103,7 @@ def register_cache( cache_hits.labels(cache_name).set(self.hits) cache_evicted.labels(cache_name).set(self.evicted_size) cache_total.labels(cache_name).set(self.hits + self.misses) - if hasattr(cache, "max_size"): + if getattr(cache, "max_size", None): cache_max_size.labels(cache_name).set(cache.max_size) if collect_callback: collect_callback() diff --git a/synapse/util/caches/expiringcache.py b/synapse/util/caches/expiringcache.py index 6d41d9dc65..043289d2d7 100644 --- a/synapse/util/caches/expiringcache.py +++ b/synapse/util/caches/expiringcache.py @@ -18,6 +18,7 @@ from collections import OrderedDict from six import iteritems, itervalues +from synapse.config import cache as cache_config from synapse.metrics.background_process_metrics import run_as_background_process from synapse.util.caches import register_cache @@ -55,18 +56,19 @@ class ExpiringCache(object): """ self._cache_name = cache_name + self.max_size = int(max_len * cache_config.DEFAULT_CACHE_SIZE_FACTOR) + self._original_max_size = max_len + self._clock = clock - self._max_len = max_len self._expiry_ms = expiry_ms - self._reset_expiry_on_get = reset_expiry_on_get self._cache = OrderedDict() self.iterable = iterable - self.metrics = register_cache("expiring", cache_name, self, resizable=False) + self.metrics = register_cache("expiring", cache_name, self) if not self._expiry_ms: # Don't bother starting the loop if things never expire @@ -82,9 +84,11 @@ class ExpiringCache(object): def __setitem__(self, key, value): now = self._clock.time_msec() self._cache[key] = _CacheEntry(now, value) + self.evict() + def evict(self): # Evict if there are now too many items - while self._max_len and len(self) > self._max_len: + while self.max_size and len(self) > self.max_size: _key, value = self._cache.popitem(last=False) if self.iterable: self.metrics.inc_evictions(len(value.value)) @@ -170,6 +174,23 @@ class ExpiringCache(object): else: return len(self._cache) + def set_cache_factor(self, factor: float) -> bool: + """ + Set the cache factor for this individual cache. + + This will trigger a resize if it changes, which may require evicting + items from the cache. + + Returns: + bool: Whether the cache changed size or not. + """ + new_size = int(self._original_max_size * factor) + if new_size != self.max_size: + self.max_size = new_size + self.evict() + return True + return False + class _CacheEntry(object): __slots__ = ["time", "value"] diff --git a/synapse/util/caches/lrucache.py b/synapse/util/caches/lrucache.py index 3779617ff9..8ff23a72da 100644 --- a/synapse/util/caches/lrucache.py +++ b/synapse/util/caches/lrucache.py @@ -13,8 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. - -import math import threading from functools import wraps @@ -81,7 +79,7 @@ class LruCache(object): # Save the original max size, and apply the default size factor. self._original_max_size = max_size - self.max_size = math.floor(max_size * cache_config.DEFAULT_CACHE_SIZE_FACTOR) + self.max_size = int(max_size * cache_config.DEFAULT_CACHE_SIZE_FACTOR) list_root = _Node(None, None, None, None) list_root.next_node = list_root @@ -285,7 +283,7 @@ class LruCache(object): Returns: bool: Whether the cache changed size or not. """ - new_size = math.floor(self._original_max_size * factor) + new_size = int(self._original_max_size * factor) if new_size != self.max_size: self.max_size = new_size self._on_resize() diff --git a/tests/util/test_expiring_cache.py b/tests/util/test_expiring_cache.py index 50bc7702d2..49ffeebd0e 100644 --- a/tests/util/test_expiring_cache.py +++ b/tests/util/test_expiring_cache.py @@ -21,7 +21,7 @@ from tests.utils import MockClock from .. import unittest -class ExpiringCacheTestCase(unittest.TestCase): +class ExpiringCacheTestCase(unittest.HomeserverTestCase): def test_get_set(self): clock = MockClock() cache = ExpiringCache("test", clock, max_len=1)