Skip to content

Commit e439c45

Browse files
TheOldMopkorfuri
authored andcommitted
Implement metrics for redis cache (#79)
1 parent f11aea3 commit e439c45

File tree

7 files changed

+141
-15
lines changed

7 files changed

+141
-15
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ env:
88
- DJANGO_VERSION=1.8
99
services:
1010
- memcached
11+
- redis
1112
- mysql
1213
- postgresql
1314
matrix:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ DATABASES = {
7070

7171
### Monitoring your caches
7272

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from django_redis import cache, exceptions
2+
3+
from django_prometheus.cache.metrics import (
4+
django_cache_get_fail_total,
5+
django_cache_get_total,
6+
django_cache_hits_total,
7+
django_cache_misses_total,
8+
)
9+
10+
11+
class RedisCache(cache.RedisCache):
12+
"""Inherit redis to add metrics about hit/miss/interruption ratio"""
13+
14+
@cache.omit_exception
15+
def get(self, key, default=None, version=None, client=None):
16+
try:
17+
django_cache_get_total.labels(backend='redis').inc()
18+
cached = self.client.get(
19+
key,
20+
default=None,
21+
version=version,
22+
client=client
23+
)
24+
except exceptions.ConnectionInterrupted as e:
25+
django_cache_get_fail_total.labels(backend='redis').inc()
26+
if cache.DJANGO_REDIS_IGNORE_EXCEPTIONS or self._ignore_exceptions:
27+
if cache.DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS:
28+
cache.logger.error(str(e))
29+
return default
30+
raise
31+
else:
32+
if cached is not None:
33+
django_cache_hits_total.labels(backend='redis').inc()
34+
else:
35+
django_cache_misses_total.labels(backend='redis').inc()
36+
return cached or default

django_prometheus/cache/metrics.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
from prometheus_client import Counter
22

3-
django_cache_get_total = Counter('django_cache_get_total',
4-
'Total get requests on cache', ['backend'])
5-
django_cache_hits_total = Counter('django_cache_get_hits_total',
6-
'Total hits on cache', ['backend'])
7-
django_cache_misses_total = Counter('django_cache_get_misses_total',
8-
'Total misses on cache', ['backend'])
3+
django_cache_get_total = Counter(
4+
'django_cache_get_total', 'Total get requests on cache', ['backend']
5+
)
6+
django_cache_hits_total = Counter(
7+
'django_cache_get_hits_total', 'Total hits on cache', ['backend']
8+
)
9+
django_cache_misses_total = Counter(
10+
'django_cache_get_misses_total', 'Total misses on cache', ['backend']
11+
)
12+
django_cache_get_fail_total = Counter(
13+
'django_cache_get_fail_total',
14+
'Total get request failures by cache', ['backend']
15+
)

django_prometheus/tests/end2end/testapp/settings.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,22 @@ def GetMiddlewareClasses():
153153
'locmem': {
154154
'BACKEND': 'django_prometheus.cache.backends.locmem.LocMemCache',
155155
'LOCATION': '/var/tmp/locmem_cache',
156+
},
157+
'redis': {
158+
'BACKEND': 'django_prometheus.cache.backends.redis.RedisCache',
159+
'LOCATION': 'redis://127.0.0.1:6379/1',
160+
},
161+
# Fake redis config emulated stopped service
162+
'stopped_redis': {
163+
'BACKEND': 'django_prometheus.cache.backends.redis.RedisCache',
164+
'LOCATION': 'redis://127.0.0.1:6666/1',
165+
},
166+
'stopped_redis_ignore_exception': {
167+
'BACKEND': 'django_prometheus.cache.backends.redis.RedisCache',
168+
'LOCATION': 'redis://127.0.0.1:6666/1',
169+
"OPTIONS": {
170+
"IGNORE_EXCEPTIONS": True,
171+
}
156172
}
157173
}
158174

django_prometheus/tests/end2end/testapp/test_caches.py

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
1-
from unittest import skipUnless
1+
from django.core.cache import caches
2+
from django.test import TestCase
3+
from redis import RedisError
24

35
from django_prometheus.testutils import PrometheusTestCaseMixin
4-
from django.test import TestCase
5-
from django.core.cache import caches
66

77

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

1111
def testCounters(self):
12-
supported_caches = ['memcached', 'filebased', 'locmem']
12+
supported_caches = ['memcached', 'filebased', 'locmem', 'redis']
1313

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

18+
total_before = self.getMetric(
19+
'django_cache_get_total', backend=supported_cache) or 0
20+
hit_before = self.getMetric(
21+
'django_cache_get_hits_total', backend=supported_cache) or 0
22+
miss_before = self.getMetric(
23+
'django_cache_get_misses_total', backend=supported_cache) or 0
24+
1825
tested_cache.set('foo1', 'bar')
1926
tested_cache.get('foo1')
2027
tested_cache.get('foo1')
@@ -25,14 +32,72 @@ def testCounters(self):
2532
assert result == 'default'
2633

2734
self.assertMetricEquals(
28-
4, 'django_cache_get_total', backend=supported_cache)
35+
total_before + 4,
36+
'django_cache_get_total', backend=supported_cache)
2937
self.assertMetricEquals(
30-
2, 'django_cache_get_hits_total', backend=supported_cache)
38+
hit_before + 2,
39+
'django_cache_get_hits_total', backend=supported_cache)
3140
self.assertMetricEquals(
32-
2, 'django_cache_get_misses_total', backend=supported_cache)
41+
miss_before + 2,
42+
'django_cache_get_misses_total', backend=supported_cache)
43+
44+
def test_redis_cache_fail(self):
45+
46+
# Note: test use fake service config (like if server was stopped)
47+
supported_cache = 'redis'
48+
49+
total_before = self.getMetric(
50+
'django_cache_get_total', backend=supported_cache) or 0
51+
fail_before = self.getMetric(
52+
'django_cache_get_fail_total', backend=supported_cache) or 0
53+
hit_before = self.getMetric(
54+
'django_cache_get_hits_total', backend=supported_cache) or 0
55+
miss_before = self.getMetric(
56+
'django_cache_get_misses_total', backend=supported_cache) or 0
57+
58+
tested_cache = caches['stopped_redis_ignore_exception']
59+
tested_cache.get('foo1')
60+
61+
self.assertMetricEquals(
62+
hit_before,
63+
'django_cache_get_hits_total', backend=supported_cache
64+
)
65+
self.assertMetricEquals(
66+
miss_before,
67+
'django_cache_get_misses_total', backend=supported_cache
68+
)
69+
self.assertMetricEquals(
70+
total_before + 1,
71+
'django_cache_get_total', backend=supported_cache
72+
)
73+
self.assertMetricEquals(
74+
fail_before + 1,
75+
'django_cache_get_fail_total', backend=supported_cache
76+
)
77+
78+
tested_cache = caches['stopped_redis']
79+
with self.assertRaises(RedisError):
80+
tested_cache.get('foo1')
81+
82+
self.assertMetricEquals(
83+
hit_before,
84+
'django_cache_get_hits_total', backend=supported_cache
85+
)
86+
self.assertMetricEquals(
87+
miss_before,
88+
'django_cache_get_misses_total', backend=supported_cache
89+
)
90+
self.assertMetricEquals(
91+
total_before + 2,
92+
'django_cache_get_total', backend=supported_cache
93+
)
94+
self.assertMetricEquals(
95+
fail_before + 2,
96+
'django_cache_get_fail_total', backend=supported_cache
97+
)
3398

3499
def test_cache_version_support(self):
35-
supported_caches = ['memcached', 'filebased', 'locmem']
100+
supported_caches = ['memcached', 'filebased', 'locmem', 'redis']
36101

37102
# Note: those tests require a memcached server running
38103
for supported_cache in supported_caches:

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
Django>=1.8
2+
django-redis>=4.8
23
pep8>=1.6.2
34
prometheus-client>=0.0.21
45
pip-prometheus>=1.1.0

0 commit comments

Comments
 (0)