Create the concept of a cachecounter metric; generating two counters specific to caches

This commit is contained in:
Paul "LeoNerd" Evans 2015-03-04 15:47:23 +00:00
parent 7d72e44eb9
commit ce8b5769f7
3 changed files with 73 additions and 8 deletions

View File

@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from .metric import CounterMetric from .metric import CounterMetric, CacheCounterMetric
# We'll keep all the available metrics in a single toplevel dict, one shared # We'll keep all the available metrics in a single toplevel dict, one shared
@ -43,6 +43,15 @@ class Metrics(object):
return metric return metric
def register_cachecounter(self, name, *args, **kwargs):
full_name = "%s.%s" % (self.name_prefix, name)
metric = CacheCounterMetric(full_name, *args, **kwargs)
self._register(metric)
return metric
def counted(self, func): def counted(self, func):
""" A method decorator that registers a counter, to count invocations """ A method decorator that registers a counter, to count invocations
of this method. """ of this method. """

View File

@ -14,16 +14,28 @@
# limitations under the License. # limitations under the License.
class CounterMetric(object): class BaseMetric(object):
def __init__(self, name, keys=[]): def __init__(self, name, keys=[]):
self.name = name self.name = name
self.keys = keys # OK not to clone as we never write it self.keys = keys # OK not to clone as we never write it
def _render_key(self, values):
# TODO: some kind of value escape
return ",".join(["%s=%s" % kv for kv in zip(self.keys, values)])
class CounterMetric(BaseMetric):
"""The simplest kind of metric; one that stores a monotonically-increasing
integer that counts events."""
def __init__(self, *args, **kwargs):
super(CounterMetric, self).__init__(*args, **kwargs)
self.counts = {} self.counts = {}
# Scalar metrics are never empty # Scalar metrics are never empty
if not len(keys): if not len(self.keys):
self.counts[()] = 0 self.counts[()] = 0
def inc(self, *values): def inc(self, *values):
@ -42,13 +54,32 @@ class CounterMetric(object):
def fetch(self): def fetch(self):
return dict(self.counts) return dict(self.counts)
def _render_key(self, values):
# TODO: some kind of value escape
return ",".join(["%s=%s" % kv for kv in zip(self.keys, values)])
def render(self): def render(self):
if not len(self.keys): if not len(self.keys):
return ["%s %d" % (self.name, self.counts[()])] return ["%s %d" % (self.name, self.counts[()])]
return ["%s{%s} %d" % (self.name, self._render_key(k), self.counts[k]) return ["%s{%s} %d" % (self.name, self._render_key(k), self.counts[k])
for k in sorted(self.counts.keys())] for k in sorted(self.counts.keys())]
class CacheCounterMetric(object):
"""A combination of two CounterMetrics, one to count cache hits and one to
count misses.
This metric generates standard metric name pairs, so that monitoring rules
can easily be applied to measure hit ratio."""
def __init__(self, name, keys=[]):
self.name = name
self.hits = CounterMetric(name + ":hits", keys=keys)
self.misses = CounterMetric(name + ":misses", keys=keys)
def inc_hits(self, *values):
self.hits.inc(*values)
def inc_misses(self, *values):
self.misses.inc(*values)
def render(self):
return self.hits.render() + self.misses.render()

View File

@ -15,7 +15,7 @@
from tests import unittest from tests import unittest
from synapse.metrics.metric import CounterMetric from synapse.metrics.metric import CounterMetric, CacheCounterMetric
class CounterMetricTestCase(unittest.TestCase): class CounterMetricTestCase(unittest.TestCase):
@ -59,3 +59,28 @@ class CounterMetricTestCase(unittest.TestCase):
"vector{method=GET} 2", "vector{method=GET} 2",
"vector{method=PUT} 1", "vector{method=PUT} 1",
]) ])
class CacheCounterMetricTestCase(unittest.TestCase):
def test_cachecounter(self):
counter = CacheCounterMetric("cache")
self.assertEquals(counter.render(), [
"cache:hits 0",
"cache:misses 0",
])
counter.inc_misses()
self.assertEquals(counter.render(), [
"cache:hits 0",
"cache:misses 1",
])
counter.inc_hits()
self.assertEquals(counter.render(), [
"cache:hits 1",
"cache:misses 1",
])