Skip to content

Commit

Permalink
Implement metrics for redis cache (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
TheOldMop authored and korfuri committed Jul 18, 2018
1 parent f11aea3 commit e439c45
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 15 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Expand Up @@ -8,6 +8,7 @@ env:
- DJANGO_VERSION=1.8
services:
- memcached
- redis
- mysql
- postgresql
matrix:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -70,7 +70,7 @@ DATABASES = {

### Monitoring your caches

Filebased and memcached caches can be monitored. Just replace
Filebased, memcached, redis caches can be monitored. Just replace
the cache backend to use the one provided by django_prometheus
`django.core.cache.backends` with `django_prometheus.cache.backends`.

Expand Down
36 changes: 36 additions & 0 deletions django_prometheus/cache/backends/redis.py
@@ -0,0 +1,36 @@
from django_redis import cache, exceptions

from django_prometheus.cache.metrics import (
django_cache_get_fail_total,
django_cache_get_total,
django_cache_hits_total,
django_cache_misses_total,
)


class RedisCache(cache.RedisCache):
"""Inherit redis to add metrics about hit/miss/interruption ratio"""

@cache.omit_exception
def get(self, key, default=None, version=None, client=None):
try:
django_cache_get_total.labels(backend='redis').inc()
cached = self.client.get(
key,
default=None,
version=version,
client=client
)
except exceptions.ConnectionInterrupted as e:
django_cache_get_fail_total.labels(backend='redis').inc()
if cache.DJANGO_REDIS_IGNORE_EXCEPTIONS or self._ignore_exceptions:
if cache.DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS:
cache.logger.error(str(e))
return default
raise
else:
if cached is not None:
django_cache_hits_total.labels(backend='redis').inc()
else:
django_cache_misses_total.labels(backend='redis').inc()
return cached or default
19 changes: 13 additions & 6 deletions django_prometheus/cache/metrics.py
@@ -1,8 +1,15 @@
from prometheus_client import Counter

django_cache_get_total = Counter('django_cache_get_total',
'Total get requests on cache', ['backend'])
django_cache_hits_total = Counter('django_cache_get_hits_total',
'Total hits on cache', ['backend'])
django_cache_misses_total = Counter('django_cache_get_misses_total',
'Total misses on cache', ['backend'])
django_cache_get_total = Counter(
'django_cache_get_total', 'Total get requests on cache', ['backend']
)
django_cache_hits_total = Counter(
'django_cache_get_hits_total', 'Total hits on cache', ['backend']
)
django_cache_misses_total = Counter(
'django_cache_get_misses_total', 'Total misses on cache', ['backend']
)
django_cache_get_fail_total = Counter(
'django_cache_get_fail_total',
'Total get request failures by cache', ['backend']
)
16 changes: 16 additions & 0 deletions django_prometheus/tests/end2end/testapp/settings.py
Expand Up @@ -153,6 +153,22 @@ def GetMiddlewareClasses():
'locmem': {
'BACKEND': 'django_prometheus.cache.backends.locmem.LocMemCache',
'LOCATION': '/var/tmp/locmem_cache',
},
'redis': {
'BACKEND': 'django_prometheus.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
},
# Fake redis config emulated stopped service
'stopped_redis': {
'BACKEND': 'django_prometheus.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6666/1',
},
'stopped_redis_ignore_exception': {
'BACKEND': 'django_prometheus.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6666/1',
"OPTIONS": {
"IGNORE_EXCEPTIONS": True,
}
}
}

Expand Down
81 changes: 73 additions & 8 deletions django_prometheus/tests/end2end/testapp/test_caches.py
@@ -1,20 +1,27 @@
from unittest import skipUnless
from django.core.cache import caches
from django.test import TestCase
from redis import RedisError

from django_prometheus.testutils import PrometheusTestCaseMixin
from django.test import TestCase
from django.core.cache import caches


class TestCachesMetrics(PrometheusTestCaseMixin, TestCase):
"""Test django_prometheus.caches metrics."""

def testCounters(self):
supported_caches = ['memcached', 'filebased', 'locmem']
supported_caches = ['memcached', 'filebased', 'locmem', 'redis']

# Note: those tests require a memcached server running
for supported_cache in supported_caches:
tested_cache = caches[supported_cache]

total_before = self.getMetric(
'django_cache_get_total', backend=supported_cache) or 0
hit_before = self.getMetric(
'django_cache_get_hits_total', backend=supported_cache) or 0
miss_before = self.getMetric(
'django_cache_get_misses_total', backend=supported_cache) or 0

tested_cache.set('foo1', 'bar')
tested_cache.get('foo1')
tested_cache.get('foo1')
Expand All @@ -25,14 +32,72 @@ def testCounters(self):
assert result == 'default'

self.assertMetricEquals(
4, 'django_cache_get_total', backend=supported_cache)
total_before + 4,
'django_cache_get_total', backend=supported_cache)
self.assertMetricEquals(
2, 'django_cache_get_hits_total', backend=supported_cache)
hit_before + 2,
'django_cache_get_hits_total', backend=supported_cache)
self.assertMetricEquals(
2, 'django_cache_get_misses_total', backend=supported_cache)
miss_before + 2,
'django_cache_get_misses_total', backend=supported_cache)

def test_redis_cache_fail(self):

# Note: test use fake service config (like if server was stopped)
supported_cache = 'redis'

total_before = self.getMetric(
'django_cache_get_total', backend=supported_cache) or 0
fail_before = self.getMetric(
'django_cache_get_fail_total', backend=supported_cache) or 0
hit_before = self.getMetric(
'django_cache_get_hits_total', backend=supported_cache) or 0
miss_before = self.getMetric(
'django_cache_get_misses_total', backend=supported_cache) or 0

tested_cache = caches['stopped_redis_ignore_exception']
tested_cache.get('foo1')

self.assertMetricEquals(
hit_before,
'django_cache_get_hits_total', backend=supported_cache
)
self.assertMetricEquals(
miss_before,
'django_cache_get_misses_total', backend=supported_cache
)
self.assertMetricEquals(
total_before + 1,
'django_cache_get_total', backend=supported_cache
)
self.assertMetricEquals(
fail_before + 1,
'django_cache_get_fail_total', backend=supported_cache
)

tested_cache = caches['stopped_redis']
with self.assertRaises(RedisError):
tested_cache.get('foo1')

self.assertMetricEquals(
hit_before,
'django_cache_get_hits_total', backend=supported_cache
)
self.assertMetricEquals(
miss_before,
'django_cache_get_misses_total', backend=supported_cache
)
self.assertMetricEquals(
total_before + 2,
'django_cache_get_total', backend=supported_cache
)
self.assertMetricEquals(
fail_before + 2,
'django_cache_get_fail_total', backend=supported_cache
)

def test_cache_version_support(self):
supported_caches = ['memcached', 'filebased', 'locmem']
supported_caches = ['memcached', 'filebased', 'locmem', 'redis']

# Note: those tests require a memcached server running
for supported_cache in supported_caches:
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
@@ -1,4 +1,5 @@
Django>=1.8
django-redis>=4.8
pep8>=1.6.2
prometheus-client>=0.0.21
pip-prometheus>=1.1.0
Expand Down

0 comments on commit e439c45

Please sign in to comment.