session: Enforce that changes cannot happen in a transaction.

This commit is contained in:
Alex Vandiver 2022-03-09 01:41:27 +00:00
parent b70a071124
commit 7650b5a972
4 changed files with 29 additions and 2 deletions

View File

@ -140,6 +140,8 @@ not_yet_fully_covered = [
"zerver/webhooks/teamcity/view.py",
"zerver/webhooks/travis/view.py",
"zerver/webhooks/zapier/view.py",
# Cannot have coverage, as tests run in a transaction
"zerver/lib/safe_session_cached_db.py",
]
enforce_fully_covered = sorted(

View File

@ -56,7 +56,7 @@ def huddle_cache_items(items_for_remote_cache: Dict[str, Tuple[Huddle]], huddle:
def session_cache_items(items_for_remote_cache: Dict[str, str], session: Session) -> None:
if settings.SESSION_ENGINE != "django.contrib.sessions.backends.cached_db":
if settings.SESSION_ENGINE != "zerver.lib.safe_session_cached_db":
# If we're not using the cached_db session engine, we there
# will be no store.cache_key attribute, and in any case we
# don't need to fill the cache, since it won't exist.

View File

@ -0,0 +1,25 @@
from typing import Optional
from django.contrib.sessions.backends.cached_db import SessionStore as CachedDbSessionStore
from django.db.transaction import get_connection
class SessionStore(CachedDbSessionStore):
"""Caching session object which does not leak into the cache.
django.contrib.sessions.backends.cached_db does write-through to
the cache and the backing database. If the database is in a
transaction, this may leak not-yet-committed changes to the cache,
which can lead to inconsistent state. This class wraps changes to
the session in assertions which enforce that the database cannot
be in a transaction before writing.
"""
def save(self, must_create: bool = False) -> None:
assert not get_connection().in_atomic_block
super().save(must_create)
def delete(self, session_key: Optional[str] = None) -> None:
assert not get_connection().in_atomic_block
super().delete(session_key)

View File

@ -334,7 +334,7 @@ RABBITMQ_PASSWORD = get_secret("rabbitmq_password")
# CACHING CONFIGURATION
########################################################################
SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
SESSION_ENGINE = "zerver.lib.safe_session_cached_db"
MEMCACHED_PASSWORD = get_secret("memcached_password")