mirror of https://github.com/django/django.git
Fixed #34806 -- Made cached_db session backend resilient to cache write errors.
Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
This commit is contained in:
parent
6feaad9113
commit
eceb5e2eea
|
@ -2,12 +2,16 @@
|
|||
Cached, database-backed sessions.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.backends.db import SessionStore as DBStore
|
||||
from django.core.cache import caches
|
||||
|
||||
KEY_PREFIX = "django.contrib.sessions.cached_db"
|
||||
|
||||
logger = logging.getLogger("django.contrib.sessions")
|
||||
|
||||
|
||||
class SessionStore(DBStore):
|
||||
"""
|
||||
|
@ -52,7 +56,10 @@ class SessionStore(DBStore):
|
|||
|
||||
def save(self, must_create=False):
|
||||
super().save(must_create)
|
||||
self._cache.set(self.cache_key, self._session, self.get_expiry_age())
|
||||
try:
|
||||
self._cache.set(self.cache_key, self._session, self.get_expiry_age())
|
||||
except Exception:
|
||||
logger.exception("Error saving to cache (%s)", self._cache)
|
||||
|
||||
def delete(self, session_key=None):
|
||||
super().delete(session_key)
|
||||
|
|
|
@ -286,6 +286,17 @@ Messages to this logger have ``params`` and ``sql`` in their extra context (but
|
|||
unlike ``django.db.backends``, not duration). The values have the same meaning
|
||||
as explained in :ref:`django-db-logger`.
|
||||
|
||||
.. _django-contrib-sessions-logger:
|
||||
|
||||
``django.contrib.sessions``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Log messages related to the :doc:`session framework</topics/http/sessions>`.
|
||||
|
||||
* Non-fatal errors occurring when using the
|
||||
:class:`django.contrib.sessions.backends.cached_db.SessionStore` engine are
|
||||
logged as ``ERROR`` messages with the corresponding traceback.
|
||||
|
||||
Handlers
|
||||
--------
|
||||
|
||||
|
|
|
@ -115,7 +115,10 @@ Minor features
|
|||
:mod:`django.contrib.sessions`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ...
|
||||
* :class:`django.contrib.sessions.backends.cached_db.SessionStore` now handles
|
||||
exceptions when storing session information in the cache, logging proper
|
||||
error messages with their traceback via the newly added
|
||||
:ref:`sessions logger <django-contrib-sessions-logger>`.
|
||||
|
||||
:mod:`django.contrib.sitemaps`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -76,9 +76,17 @@ Once your cache is configured, you have to choose between a database-backed
|
|||
cache or a non-persistent cache.
|
||||
|
||||
The cached database backend (``cached_db``) uses a write-through cache --
|
||||
session writes are applied to both the cache and the database. Session reads
|
||||
use the cache, or the database if the data has been evicted from the cache. To
|
||||
use this backend, set :setting:`SESSION_ENGINE` to
|
||||
session writes are applied to both the database and cache, in that order. If
|
||||
writing to the cache fails, the exception is handled and logged via the
|
||||
:ref:`sessions logger <django-contrib-sessions-logger>`, to avoid failing an
|
||||
otherwise successful write operation.
|
||||
|
||||
.. versionchanged:: 5.1
|
||||
|
||||
Handling and logging of exceptions when writing to the cache was added.
|
||||
|
||||
Session reads use the cache, or the database if the data has been evicted from
|
||||
the cache. To use this backend, set :setting:`SESSION_ENGINE` to
|
||||
``"django.contrib.sessions.backends.cached_db"``, and follow the configuration
|
||||
instructions for the `using database-backed sessions`_.
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
from django.core.cache.backends.locmem import LocMemCache
|
||||
|
||||
|
||||
class CacheClass(LocMemCache):
|
||||
|
||||
def set(self, *args, **kwargs):
|
||||
raise Exception("Faked exception saving to cache")
|
|
@ -517,6 +517,22 @@ class CacheDBSessionTests(SessionTestsMixin, TestCase):
|
|||
with self.assertRaises(InvalidCacheBackendError):
|
||||
self.backend()
|
||||
|
||||
@override_settings(
|
||||
CACHES={"default": {"BACKEND": "cache.failing_cache.CacheClass"}}
|
||||
)
|
||||
def test_cache_set_failure_non_fatal(self):
|
||||
"""Failing to write to the cache does not raise errors."""
|
||||
session = self.backend()
|
||||
session["key"] = "val"
|
||||
|
||||
with self.assertLogs("django.contrib.sessions", "ERROR") as cm:
|
||||
session.save()
|
||||
|
||||
# A proper ERROR log message was recorded.
|
||||
log = cm.records[-1]
|
||||
self.assertEqual(log.message, f"Error saving to cache ({session._cache})")
|
||||
self.assertEqual(str(log.exc_info[1]), "Faked exception saving to cache")
|
||||
|
||||
|
||||
@override_settings(USE_TZ=True)
|
||||
class CacheDBSessionWithTimeZoneTests(CacheDBSessionTests):
|
||||
|
|
Loading…
Reference in New Issue