Игнорирование кэша в функции Django cache_page при ошибке подключения Redis
Я использую Redis для кэширования некоторых представлений в моем Django Rest Ramework API. Допустим, у меня есть следующее представление:
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from rest_framework.response import Response
from rest_framework.views import APIView
class MyView(APIView):
@method_decorator(cache_page(60 * 15))
def get(self, request):
return Response({"timestamp": timezone.now()})
Все работает отлично, когда Redis запущен и работает. Однако в последнее время наш экземпляр Redis периодически выходит из строя (крик Heroku Redis), из-за чего все конечные точки, использующие кэш Redis, и, следовательно, все конечные точки, использующие декоратор cache_page
, падают и возвращают 500 Internal Server Error.
Я хочу реализовать механизм обхода отказа, который будет просто игнорировать кэш и успешно возвращать ответ, когда возникает ошибка redis.ConnectionError
. Вот что у меня есть на данный момент:
def cache_page_with_failover(timeout, *, cache=None, key_prefix=None):
def decorator(view_func):
@wraps(view_func)
def wrapper(*args, **kwargs):
try:
return cache_page(timeout, cache=cache, key_prefix=key_prefix)(
view_func
)(*args, **kwargs)
except redis.ConnectionError:
return view_func(*args, **kwargs)
return wrapper
return decorator
Использование:
class MyView(APIView):
@method_decorator(cache_page_with_failover(60 * 15))
def get(self, request):
return Response({"timestamp": timezone.now()})
Это работает, но это ужасно многословно, и мне кажется, что я копирую большую часть подписи cache_page
. Есть ли более чистый и элегантный способ написать это?
Прямой прием декоратора, возвращаемого cache_page
:
def with_redis_failover(cache_decorator):
def decorator(view_func):
cache_decorated_view_func = cache_decorator(view_func)
@wraps(view_func)
def wrapper(*args, **kwargs):
try:
return cache_decorated_view_func(*args, **kwargs)
except redis.ConnectionError:
return view_func(*args, **kwargs)
return wrapper
return decorator
Использование:
# @method_decorator(cache_page_with_failover(60 * 15))
@method_decorator(with_redis_failover(cache_page(60 * 15)))
Это можно обобщить до:
def with_failover(decorator, exception_type):
def _decorator(view_func):
decorated_view_func = decorator(view_func)
@wraps(view_func)
def wrapper(*args, **kwargs):
try:
return decorated_view_func(*args, **kwargs)
except exception_type:
return view_func(*args, **kwargs)
return wrapper
return _decorator
Использование:
@method_decorator(with_failover(cache_page(60 * 15), redis.ConnectionError))
with_redis_failover = partial(with_failover, exception_type=redis.ConnectionError)
@method_decorator(with_redis_failover(cache_page(60 * 15)))