cache.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import functools
  2. import logging
  3. from typing import Any, Callable, Dict, Optional
  4. from django import VERSION as DJANGO_VERSION
  5. from django.conf import settings
  6. from django.core.cache.backends.base import BaseCache
  7. from django.utils.module_loading import import_string
  8. from .exceptions import ConnectionInterrupted
  9. CONNECTION_INTERRUPTED = object()
  10. def omit_exception(
  11. method: Optional[Callable] = None, return_value: Optional[Any] = None
  12. ):
  13. """
  14. Simple decorator that intercepts connection
  15. errors and ignores these if settings specify this.
  16. """
  17. if method is None:
  18. return functools.partial(omit_exception, return_value=return_value)
  19. @functools.wraps(method)
  20. def _decorator(self, *args, **kwargs):
  21. try:
  22. return method(self, *args, **kwargs)
  23. except ConnectionInterrupted as e:
  24. if self._ignore_exceptions:
  25. if self._log_ignored_exceptions:
  26. self.logger.exception("Exception ignored")
  27. return return_value
  28. raise e.__cause__
  29. return _decorator
  30. class RedisCache(BaseCache):
  31. def __init__(self, server: str, params: Dict[str, Any]) -> None:
  32. super().__init__(params)
  33. self._server = server
  34. self._params = params
  35. self._default_scan_itersize = getattr(
  36. settings, "DJANGO_REDIS_SCAN_ITERSIZE", 10
  37. )
  38. options = params.get("OPTIONS", {})
  39. self._client_cls = options.get(
  40. "CLIENT_CLASS", "django_redis.client.DefaultClient"
  41. )
  42. self._client_cls = import_string(self._client_cls)
  43. self._client = None
  44. self._ignore_exceptions = options.get(
  45. "IGNORE_EXCEPTIONS",
  46. getattr(settings, "DJANGO_REDIS_IGNORE_EXCEPTIONS", False),
  47. )
  48. self._log_ignored_exceptions = getattr(
  49. settings, "DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS", False
  50. )
  51. self.logger = (
  52. logging.getLogger(getattr(settings, "DJANGO_REDIS_LOGGER", __name__))
  53. if self._log_ignored_exceptions
  54. else None
  55. )
  56. @property
  57. def client(self):
  58. """
  59. Lazy client connection property.
  60. """
  61. if self._client is None:
  62. self._client = self._client_cls(self._server, self._params, self)
  63. return self._client
  64. @omit_exception
  65. def set(self, *args, **kwargs):
  66. return self.client.set(*args, **kwargs)
  67. @omit_exception
  68. def incr_version(self, *args, **kwargs):
  69. return self.client.incr_version(*args, **kwargs)
  70. @omit_exception
  71. def add(self, *args, **kwargs):
  72. return self.client.add(*args, **kwargs)
  73. def get(self, key, default=None, version=None, client=None):
  74. value = self._get(key, default, version, client)
  75. if value is CONNECTION_INTERRUPTED:
  76. value = default
  77. return value
  78. @omit_exception(return_value=CONNECTION_INTERRUPTED)
  79. def _get(self, key, default, version, client):
  80. return self.client.get(key, default=default, version=version, client=client)
  81. @omit_exception
  82. def delete(self, *args, **kwargs):
  83. """returns a boolean instead of int since django version 3.1"""
  84. result = self.client.delete(*args, **kwargs)
  85. return bool(result) if DJANGO_VERSION >= (3, 1, 0) else result
  86. @omit_exception
  87. def delete_pattern(self, *args, **kwargs):
  88. kwargs.setdefault("itersize", self._default_scan_itersize)
  89. return self.client.delete_pattern(*args, **kwargs)
  90. @omit_exception
  91. def delete_many(self, *args, **kwargs):
  92. return self.client.delete_many(*args, **kwargs)
  93. @omit_exception
  94. def clear(self):
  95. return self.client.clear()
  96. @omit_exception(return_value={})
  97. def get_many(self, *args, **kwargs):
  98. return self.client.get_many(*args, **kwargs)
  99. @omit_exception
  100. def set_many(self, *args, **kwargs):
  101. return self.client.set_many(*args, **kwargs)
  102. @omit_exception
  103. def incr(self, *args, **kwargs):
  104. return self.client.incr(*args, **kwargs)
  105. @omit_exception
  106. def decr(self, *args, **kwargs):
  107. return self.client.decr(*args, **kwargs)
  108. @omit_exception
  109. def has_key(self, *args, **kwargs):
  110. return self.client.has_key(*args, **kwargs)
  111. @omit_exception
  112. def keys(self, *args, **kwargs):
  113. return self.client.keys(*args, **kwargs)
  114. @omit_exception
  115. def iter_keys(self, *args, **kwargs):
  116. return self.client.iter_keys(*args, **kwargs)
  117. @omit_exception
  118. def ttl(self, *args, **kwargs):
  119. return self.client.ttl(*args, **kwargs)
  120. @omit_exception
  121. def pttl(self, *args, **kwargs):
  122. return self.client.pttl(*args, **kwargs)
  123. @omit_exception
  124. def persist(self, *args, **kwargs):
  125. return self.client.persist(*args, **kwargs)
  126. @omit_exception
  127. def expire(self, *args, **kwargs):
  128. return self.client.expire(*args, **kwargs)
  129. @omit_exception
  130. def expire_at(self, *args, **kwargs):
  131. return self.client.expire_at(*args, **kwargs)
  132. @omit_exception
  133. def pexpire(self, *args, **kwargs):
  134. return self.client.pexpire(*args, **kwargs)
  135. @omit_exception
  136. def pexpire_at(self, *args, **kwargs):
  137. return self.client.pexpire_at(*args, **kwargs)
  138. @omit_exception
  139. def lock(self, *args, **kwargs):
  140. return self.client.lock(*args, **kwargs)
  141. @omit_exception
  142. def close(self, **kwargs):
  143. self.client.close(**kwargs)
  144. @omit_exception
  145. def touch(self, *args, **kwargs):
  146. return self.client.touch(*args, **kwargs)