Merge pull request #2844 from matrix-org/rav/evicted_metrics
montoring metrics for number of cache evictions
This commit is contained in:
commit
db91e72ade
|
@ -193,7 +193,9 @@ class DistributionMetric(object):
|
|||
|
||||
|
||||
class CacheMetric(object):
|
||||
__slots__ = ("name", "cache_name", "hits", "misses", "size_callback")
|
||||
__slots__ = (
|
||||
"name", "cache_name", "hits", "misses", "evicted_size", "size_callback",
|
||||
)
|
||||
|
||||
def __init__(self, name, size_callback, cache_name):
|
||||
self.name = name
|
||||
|
@ -201,6 +203,7 @@ class CacheMetric(object):
|
|||
|
||||
self.hits = 0
|
||||
self.misses = 0
|
||||
self.evicted_size = 0
|
||||
|
||||
self.size_callback = size_callback
|
||||
|
||||
|
@ -210,6 +213,9 @@ class CacheMetric(object):
|
|||
def inc_misses(self):
|
||||
self.misses += 1
|
||||
|
||||
def inc_evictions(self, size=1):
|
||||
self.evicted_size += size
|
||||
|
||||
def render(self):
|
||||
size = self.size_callback()
|
||||
hits = self.hits
|
||||
|
@ -219,6 +225,9 @@ class CacheMetric(object):
|
|||
"""%s:hits{name="%s"} %d""" % (self.name, self.cache_name, hits),
|
||||
"""%s:total{name="%s"} %d""" % (self.name, self.cache_name, total),
|
||||
"""%s:size{name="%s"} %d""" % (self.name, self.cache_name, size),
|
||||
"""%s:evicted_size{name="%s"} %d""" % (
|
||||
self.name, self.cache_name, self.evicted_size
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ class Cache(object):
|
|||
self.cache = LruCache(
|
||||
max_size=max_entries, keylen=keylen, cache_type=cache_type,
|
||||
size_callback=(lambda d: len(d)) if iterable else None,
|
||||
evicted_callback=self._on_evicted,
|
||||
)
|
||||
|
||||
self.name = name
|
||||
|
@ -83,6 +84,9 @@ class Cache(object):
|
|||
self.thread = None
|
||||
self.metrics = register_cache(name, self.cache)
|
||||
|
||||
def _on_evicted(self, evicted_count):
|
||||
self.metrics.inc_evictions(evicted_count)
|
||||
|
||||
def check_thread(self):
|
||||
expected_thread = self.thread
|
||||
if expected_thread is None:
|
||||
|
|
|
@ -79,7 +79,11 @@ class ExpiringCache(object):
|
|||
while self._max_len and len(self) > self._max_len:
|
||||
_key, value = self._cache.popitem(last=False)
|
||||
if self.iterable:
|
||||
self._size_estimate -= len(value.value)
|
||||
removed_len = len(value.value)
|
||||
self.metrics.inc_evictions(removed_len)
|
||||
self._size_estimate -= removed_len
|
||||
else:
|
||||
self.metrics.inc_evictions()
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
|
|
|
@ -49,7 +49,24 @@ class LruCache(object):
|
|||
Can also set callbacks on objects when getting/setting which are fired
|
||||
when that key gets invalidated/evicted.
|
||||
"""
|
||||
def __init__(self, max_size, keylen=1, cache_type=dict, size_callback=None):
|
||||
def __init__(self, max_size, keylen=1, cache_type=dict, size_callback=None,
|
||||
evicted_callback=None):
|
||||
"""
|
||||
Args:
|
||||
max_size (int):
|
||||
|
||||
keylen (int):
|
||||
|
||||
cache_type (type):
|
||||
type of underlying cache to be used. Typically one of dict
|
||||
or TreeCache.
|
||||
|
||||
size_callback (func(V) -> int | None):
|
||||
|
||||
evicted_callback (func(int)|None):
|
||||
if not None, called on eviction with the size of the evicted
|
||||
entry
|
||||
"""
|
||||
cache = cache_type()
|
||||
self.cache = cache # Used for introspection.
|
||||
list_root = _Node(None, None, None, None)
|
||||
|
@ -61,8 +78,10 @@ class LruCache(object):
|
|||
def evict():
|
||||
while cache_len() > max_size:
|
||||
todelete = list_root.prev_node
|
||||
delete_node(todelete)
|
||||
evicted_len = delete_node(todelete)
|
||||
cache.pop(todelete.key, None)
|
||||
if evicted_callback:
|
||||
evicted_callback(evicted_len)
|
||||
|
||||
def synchronized(f):
|
||||
@wraps(f)
|
||||
|
@ -111,12 +130,15 @@ class LruCache(object):
|
|||
prev_node.next_node = next_node
|
||||
next_node.prev_node = prev_node
|
||||
|
||||
deleted_len = 1
|
||||
if size_callback:
|
||||
cached_cache_len[0] -= size_callback(node.value)
|
||||
deleted_len = size_callback(node.value)
|
||||
cached_cache_len[0] -= deleted_len
|
||||
|
||||
for cb in node.callbacks:
|
||||
cb()
|
||||
node.callbacks.clear()
|
||||
return deleted_len
|
||||
|
||||
@synchronized
|
||||
def cache_get(key, default=None, callbacks=[]):
|
||||
|
|
|
@ -141,6 +141,7 @@ class CacheMetricTestCase(unittest.TestCase):
|
|||
'cache:hits{name="cache_name"} 0',
|
||||
'cache:total{name="cache_name"} 0',
|
||||
'cache:size{name="cache_name"} 0',
|
||||
'cache:evicted_size{name="cache_name"} 0',
|
||||
])
|
||||
|
||||
metric.inc_misses()
|
||||
|
@ -150,6 +151,7 @@ class CacheMetricTestCase(unittest.TestCase):
|
|||
'cache:hits{name="cache_name"} 0',
|
||||
'cache:total{name="cache_name"} 1',
|
||||
'cache:size{name="cache_name"} 1',
|
||||
'cache:evicted_size{name="cache_name"} 0',
|
||||
])
|
||||
|
||||
metric.inc_hits()
|
||||
|
@ -158,4 +160,14 @@ class CacheMetricTestCase(unittest.TestCase):
|
|||
'cache:hits{name="cache_name"} 1',
|
||||
'cache:total{name="cache_name"} 2',
|
||||
'cache:size{name="cache_name"} 1',
|
||||
'cache:evicted_size{name="cache_name"} 0',
|
||||
])
|
||||
|
||||
metric.inc_evictions(2)
|
||||
|
||||
self.assertEquals(metric.render(), [
|
||||
'cache:hits{name="cache_name"} 1',
|
||||
'cache:total{name="cache_name"} 2',
|
||||
'cache:size{name="cache_name"} 1',
|
||||
'cache:evicted_size{name="cache_name"} 2',
|
||||
])
|
||||
|
|
Loading…
Reference in New Issue