make expiring caches resizeable

anoa/temp_working_cache_config
Amber H. Brown 2020-01-17 02:14:33 +11:00
parent 125c5a01dd
commit 0b069b70a1
5 changed files with 30 additions and 12 deletions

View File

@ -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,

View File

@ -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()

View File

@ -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"]

View File

@ -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()

View File

@ -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)