make expiring caches resizeable
parent
125c5a01dd
commit
0b069b70a1
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue