use redis MGET for metrics instead of django's cache.get
should improve performance
This commit is contained in:
parent
0a10de795d
commit
d95167350d
2 changed files with 39 additions and 24 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
import itertools
|
||||||
import re
|
import re
|
||||||
from typing import Optional, Sequence
|
from typing import Optional, Sequence
|
||||||
|
|
||||||
|
@ -7,6 +8,17 @@ from django.core.cache import cache
|
||||||
|
|
||||||
from c3nav.mapdata.models.report import Report
|
from c3nav.mapdata.models.report import Report
|
||||||
|
|
||||||
|
|
||||||
|
def chunked_iterable(iterable, size):
|
||||||
|
it = iter(iterable)
|
||||||
|
while True:
|
||||||
|
chunk = tuple(itertools.islice(it, size))
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
yield chunk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if settings.METRICS:
|
if settings.METRICS:
|
||||||
from prometheus_client import Gauge
|
from prometheus_client import Gauge
|
||||||
from prometheus_client.core import CounterMetricFamily
|
from prometheus_client.core import CounterMetricFamily
|
||||||
|
@ -28,35 +40,37 @@ if settings.METRICS:
|
||||||
metrics: dict[str, CounterMetricFamily] = dict()
|
metrics: dict[str, CounterMetricFamily] = dict()
|
||||||
if settings.CACHES['default']['BACKEND'] == 'django.core.cache.backends.redis.RedisCache':
|
if settings.CACHES['default']['BACKEND'] == 'django.core.cache.backends.redis.RedisCache':
|
||||||
client = cache._cache.get_client()
|
client = cache._cache.get_client()
|
||||||
for key in client.keys(f"*{settings.CACHES['default'].get('KEY_PREFIX', '')}apistats__*"):
|
keys = client.keys(f"*{settings.CACHES['default'].get('KEY_PREFIX', '')}apistats__*")
|
||||||
key: str = key.decode('utf-8').split(':', 2)[2]
|
|
||||||
value = cache.get(key)
|
|
||||||
key = key[10:] # trim apistats__ from the beginning
|
|
||||||
|
|
||||||
# some routing stats don't use double underscores to separate fields, workaround for now
|
for group in chunked_iterable(keys, settings.METRICS_REDIS_CHUNK_SIZE):
|
||||||
if key.startswith('route_tuple_'):
|
values = client.mget(group)
|
||||||
key = re.sub(r'^route_tuple_(.*)_(.*)$', r'route_tuple__\1__\2', key)
|
for key, value in zip(group, values):
|
||||||
if key.startswith('route_origin_') or key.startswith('route_destination_'):
|
key: str = key.decode('utf-8').split(':', 2)[2]
|
||||||
key = re.sub(r'^route_(origin|destination)_(.*)$', r'route_\1__\2', key)
|
key = key[10:] # trim apistats__ from the beginning
|
||||||
|
# some routing stats don't use double underscores to separate fields, workaround for now
|
||||||
|
if key.startswith('route_tuple_'):
|
||||||
|
key = re.sub(r'^route_tuple_(.*)_(.*)$', r'route_tuple__\1__\2', key)
|
||||||
|
if key.startswith('route_origin_') or key.startswith('route_destination_'):
|
||||||
|
key = re.sub(r'^route_(origin|destination)_(.*)$', r'route_\1__\2', key)
|
||||||
|
|
||||||
name, *labels = key.split('__')
|
name, *labels = key.split('__')
|
||||||
try:
|
try:
|
||||||
label_names = self.name_registry[name]
|
label_names = self.name_registry[name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if label_names is None:
|
if label_names is None:
|
||||||
label_names = list()
|
label_names = list()
|
||||||
|
|
||||||
if len(label_names) != len(labels):
|
if len(label_names) != len(labels):
|
||||||
raise ValueError('configured labels and number of extracted labels doesn\'t match.')
|
raise ValueError('configured labels and number of extracted labels doesn\'t match.')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
counter = metrics[name]
|
counter = metrics[name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
counter = metrics[name] = CounterMetricFamily(f'c3nav_{name}', f'c3nav_{name}',
|
counter = metrics[name] = CounterMetricFamily(f'c3nav_{name}', f'c3nav_{name}',
|
||||||
labels=label_names)
|
labels=label_names)
|
||||||
counter.add_metric(labels, value)
|
counter.add_metric(labels, value)
|
||||||
return metrics.values()
|
return metrics.values()
|
||||||
|
|
||||||
def describe(self):
|
def describe(self):
|
||||||
|
|
|
@ -413,6 +413,7 @@ with suppress(ImportError):
|
||||||
INSTALLED_APPS.append('django_extensions')
|
INSTALLED_APPS.append('django_extensions')
|
||||||
|
|
||||||
METRICS = config.getboolean('c3nav', 'metrics', fallback=False)
|
METRICS = config.getboolean('c3nav', 'metrics', fallback=False)
|
||||||
|
METRICS_REDIS_CHUNK_SIZE = config.getint('c3nav', 'metrics_redis_chunk_size', fallback=10)
|
||||||
if METRICS:
|
if METRICS:
|
||||||
try:
|
try:
|
||||||
import django_prometheus # noqa
|
import django_prometheus # noqa
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue