#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.

Reply via email to