#33533: SESSION_SAVE_EVERY_REQUEST = True does not handle parallel requests
well if
some scenarios
----------------------------------+--------------------------------------
Reporter: Michael | Owner: nobody
Type: Uncategorized | Status: closed
Component: contrib.sessions | Version: 4.0
Severity: Normal | Resolution: invalid
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
----------------------------------+--------------------------------------
Description changed by Michael:
Old description:
> In my project, there are some methods on a custom user model that require
> the `request` to calculate certain values. This simple middleware does
> the trick:
>
> {{{
> class AttachRequestToUserMiddleware:
> """Adds the request to the user object, so session information can be
> looked
> up by the custom user model.
>
> Must come after
> django.contrib.auth.middleware.AuthenticationMiddleware which adds the
> user"""
>
> def __init__(self, get_response):
> self.get_response = get_response
>
> def __call__(self, request):
> request.user.request = request
> return self.get_response(request)
> }}}
>
> In production, when there are multiple workers running parrallel by
> uWSGI, if one has `SESSION_SAVE_EVERY_REQUEST = True`, then if one makes
> async requests from JavaScript (e.g. say a Service Worker caching pages
> on install), then the way it saves/retrieves sessions on every request
> fails in many spectacular ways.
>
> Here are some example trace backs:
> {{{
> Django Version: 4.0.1
> Python Version: 3.8.5
>
> Exception Type: ProgrammingError
> Exception Value:
> no results to fetch
> Exception Location: /home/michael/.venv/project/lib/python3.8/site-
> packages/django/db/utils.py, line 97, in inner
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/db/utils.py, line 97, in inner
> return func(*args, **kwargs)
>
> Exception Type: RuntimeError
> Exception Value:
> generator raised StopIteration
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/contrib/auth/__init__.py, line 60, in
> _get_user_session_key
> return get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])
>
> Exception Type: MultipleObjectsReturned
> Exception Value:
> get() returned more than one Session -- it returned 2!
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/contrib/sessions/backends/base.py, line 180, in
> _get_session
> return self._session_cache
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/core/handlers/exception.py, line 47, in inner
> response = get_response(request)
> ./core/accounts/middleware.py, line 33, in __call__
> request.user.request = request
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/utils/functional.py, line 278, in __setattr__
> self._setup()
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/utils/functional.py, line 384, in _setup
> self._wrapped = self._setupfunc()
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/contrib/auth/middleware.py, line 25, in <lambda>
> request.user = SimpleLazyObject(lambda: get_user(request))
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/contrib/auth/middleware.py, line 11, in get_user
> request._cached_user = auth.get_user(request)
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/contrib/auth/__init__.py, line 177, in get_user
> user_id = _get_user_session_key(request)
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/contrib/auth/__init__.py, line 60, in
> _get_user_session_key
> return
> get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/contrib/sessions/backends/base.py, line 50, in
> __getitem__
> return self._session[key]
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/contrib/sessions/backends/base.py, line 185, in
> _get_session
> self._session_cache = self.load()
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/contrib/sessions/backends/db.py, line 43, in load
> s = self._get_session_from_db()
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/contrib/sessions/backends/db.py, line 32, in
> _get_session_from_db
> return self.model.objects.get(
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/db/models/manager.py, line 85, in manager_method
> return getattr(self.get_queryset(), name)(*args,
> **kwargs)
> /home/michael/.venv/project/lib/python3.8/site-
> packages/django/db/models/query.py, line 443, in get
> raise self.model.MultipleObjectsReturned(
> }}}
New description:
In my project, there are some methods on a custom user model that require
the `request` to calculate certain values. This simple middleware does the
trick:
{{{
class AttachRequestToUserMiddleware:
"""Adds the request to the user object, so session information can be
looked
up by the custom user model.
Must come after
django.contrib.auth.middleware.AuthenticationMiddleware which adds the
user"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.user.request = request
return self.get_response(request)
}}}
In production, when there are multiple workers running parrallel by uWSGI,
if one has `SESSION_SAVE_EVERY_REQUEST = True`, then if one makes async
requests from JavaScript (e.g. say a Service Worker caching pages on
install), then the way it saves/retrieves sessions on every request fails
in many spectacular ways.
Here are some example tracebacks of the many errors raised:
{{{
Django Version: 4.0.1
Python Version: 3.8.5
Exception Type: ProgrammingError
Exception Value:
no results to fetch
Exception Location: /home/michael/.venv/project/lib/python3.8/site-
packages/django/db/utils.py, line 97, in inner
/home/michael/.venv/project/lib/python3.8/site-
packages/django/db/utils.py, line 97, in inner
return func(*args, **kwargs)
Exception Type: RuntimeError
Exception Value:
generator raised StopIteration
/home/michael/.venv/project/lib/python3.8/site-
packages/django/contrib/auth/__init__.py, line 60, in
_get_user_session_key
return get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])
Exception Type: MultipleObjectsReturned
Exception Value:
get() returned more than one Session -- it returned 2!
/home/michael/.venv/project/lib/python3.8/site-
packages/django/contrib/sessions/backends/base.py, line 180, in
_get_session
return self._session_cache
/home/michael/.venv/project/lib/python3.8/site-
packages/django/core/handlers/exception.py, line 47, in inner
response = get_response(request)
./core/accounts/middleware.py, line 33, in __call__
request.user.request = request
/home/michael/.venv/project/lib/python3.8/site-
packages/django/utils/functional.py, line 278, in __setattr__
self._setup()
/home/michael/.venv/project/lib/python3.8/site-
packages/django/utils/functional.py, line 384, in _setup
self._wrapped = self._setupfunc()
/home/michael/.venv/project/lib/python3.8/site-
packages/django/contrib/auth/middleware.py, line 25, in <lambda>
request.user = SimpleLazyObject(lambda: get_user(request))
/home/michael/.venv/project/lib/python3.8/site-
packages/django/contrib/auth/middleware.py, line 11, in get_user
request._cached_user = auth.get_user(request)
/home/michael/.venv/project/lib/python3.8/site-
packages/django/contrib/auth/__init__.py, line 177, in get_user
user_id = _get_user_session_key(request)
/home/michael/.venv/project/lib/python3.8/site-
packages/django/contrib/auth/__init__.py, line 60, in
_get_user_session_key
return
get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])
/home/michael/.venv/project/lib/python3.8/site-
packages/django/contrib/sessions/backends/base.py, line 50, in __getitem__
return self._session[key]
/home/michael/.venv/project/lib/python3.8/site-
packages/django/contrib/sessions/backends/base.py, line 185, in
_get_session
self._session_cache = self.load()
/home/michael/.venv/project/lib/python3.8/site-
packages/django/contrib/sessions/backends/db.py, line 43, in load
s = self._get_session_from_db()
/home/michael/.venv/project/lib/python3.8/site-
packages/django/contrib/sessions/backends/db.py, line 32, in
_get_session_from_db
return self.model.objects.get(
/home/michael/.venv/project/lib/python3.8/site-
packages/django/db/models/manager.py, line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
/home/michael/.venv/project/lib/python3.8/site-
packages/django/db/models/query.py, line 443, in get
raise self.model.MultipleObjectsReturned(
}}}
--
--
Ticket URL: <https://code.djangoproject.com/ticket/33533#comment:5>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/0107017f8f4df4b9-60a98d71-e6ba-41cb-9f13-c42f1c25090b-000000%40eu-central-1.amazonses.com.