cache.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. from django.conf import settings
  2. from django.contrib.sessions.backends.base import CreateError, SessionBase, UpdateError
  3. from django.core.cache import caches
  4. KEY_PREFIX = "django.contrib.sessions.cache"
  5. class SessionStore(SessionBase):
  6. """
  7. A cache-based session store.
  8. """
  9. cache_key_prefix = KEY_PREFIX
  10. def __init__(self, session_key=None):
  11. self._cache = caches[settings.SESSION_CACHE_ALIAS]
  12. super().__init__(session_key)
  13. @property
  14. def cache_key(self):
  15. return self.cache_key_prefix + self._get_or_create_session_key()
  16. async def acache_key(self):
  17. return self.cache_key_prefix + await self._aget_or_create_session_key()
  18. def load(self):
  19. try:
  20. session_data = self._cache.get(self.cache_key)
  21. except Exception:
  22. # Some backends (e.g. memcache) raise an exception on invalid
  23. # cache keys. If this happens, reset the session. See #17810.
  24. session_data = None
  25. if session_data is not None:
  26. return session_data
  27. self._session_key = None
  28. return {}
  29. async def aload(self):
  30. try:
  31. session_data = await self._cache.aget(await self.acache_key())
  32. except Exception:
  33. session_data = None
  34. if session_data is not None:
  35. return session_data
  36. self._session_key = None
  37. return {}
  38. def create(self):
  39. # Because a cache can fail silently (e.g. memcache), we don't know if
  40. # we are failing to create a new session because of a key collision or
  41. # because the cache is missing. So we try for a (large) number of times
  42. # and then raise an exception. That's the risk you shoulder if using
  43. # cache backing.
  44. for i in range(10000):
  45. self._session_key = self._get_new_session_key()
  46. try:
  47. self.save(must_create=True)
  48. except CreateError:
  49. continue
  50. self.modified = True
  51. return
  52. raise RuntimeError(
  53. "Unable to create a new session key. "
  54. "It is likely that the cache is unavailable."
  55. )
  56. async def acreate(self):
  57. for i in range(10000):
  58. self._session_key = await self._aget_new_session_key()
  59. try:
  60. await self.asave(must_create=True)
  61. except CreateError:
  62. continue
  63. self.modified = True
  64. return
  65. raise RuntimeError(
  66. "Unable to create a new session key. "
  67. "It is likely that the cache is unavailable."
  68. )
  69. def save(self, must_create=False):
  70. if self.session_key is None:
  71. return self.create()
  72. if must_create:
  73. func = self._cache.add
  74. elif self._cache.get(self.cache_key) is not None:
  75. func = self._cache.set
  76. else:
  77. raise UpdateError
  78. result = func(
  79. self.cache_key,
  80. self._get_session(no_load=must_create),
  81. self.get_expiry_age(),
  82. )
  83. if must_create and not result:
  84. raise CreateError
  85. async def asave(self, must_create=False):
  86. if self.session_key is None:
  87. return await self.acreate()
  88. if must_create:
  89. func = self._cache.aadd
  90. elif await self._cache.aget(await self.acache_key()) is not None:
  91. func = self._cache.aset
  92. else:
  93. raise UpdateError
  94. result = await func(
  95. await self.acache_key(),
  96. await self._aget_session(no_load=must_create),
  97. await self.aget_expiry_age(),
  98. )
  99. if must_create and not result:
  100. raise CreateError
  101. def exists(self, session_key):
  102. return (
  103. bool(session_key) and (self.cache_key_prefix + session_key) in self._cache
  104. )
  105. async def aexists(self, session_key):
  106. return bool(session_key) and await self._cache.ahas_key(
  107. self.cache_key_prefix + session_key
  108. )
  109. def delete(self, session_key=None):
  110. if session_key is None:
  111. if self.session_key is None:
  112. return
  113. session_key = self.session_key
  114. self._cache.delete(self.cache_key_prefix + session_key)
  115. async def adelete(self, session_key=None):
  116. if session_key is None:
  117. if self.session_key is None:
  118. return
  119. session_key = self.session_key
  120. await self._cache.adelete(self.cache_key_prefix + session_key)
  121. @classmethod
  122. def clear_expired(cls):
  123. pass
  124. @classmethod
  125. async def aclear_expired(cls):
  126. pass