-
-
Notifications
You must be signed in to change notification settings - Fork 23
Make reflex rendering thread safe #99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
The monkey patch used to render the original view's context was not thread safe. This commit fixes the issue by patching all the get_context_data methods before accepting requests, instead of while rendering the reflex' response.
@jonathan-s this is the solution that I suggested you over email. While working on this I noticed that the view and its context are rendered twice during the page_render call. This is quite inefficient, because it could potentially result in many duplicate database queries in the view. The page rendering logic should be streamlined further probably. |
Hi @blubber, |
There are some cases where this is necessary. If you're using a ListView you need to execute the However if you keep all your queries in |
Hi,
Threading issues are always hard to reproduce or even prove. So I did some
digging into Channel's code, and found this:
https://github.com/django/channels/blob/ece488b31b4e20a55e52948f21622da3e38223cb/channels/consumer.py#L104
It seems that your consumer ultimately inherits from a consumer that has
_sync set to True, and it's dispatch method is not awaitable. This results
in the code that's doing the dispatch running the handler in async_to_sync
in a thread pool (as the comment also says). So regardless of being able to
produce an actual problem, the method swapping is not thread safe and the
code is running in a thread. If you deploy the current version in any kind
of production load it will probably break and possible leak information.
…On Sat, Feb 27, 2021 at 3:53 PM Jonathan Sundqvist ***@***.***> wrote:
While working on this I noticed that the view and its context are rendered
twice during the page_render call.
There are some cases where this is necessary. If you're using a ListView
you need to execute the get request as some instance variables are set in
the get part.
However if you keep all your queries in get_context_data the queries
should only be triggered once as they are cached in the reflex class.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#99 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AABJ3ZUQC2GVH3EHDOPS72DTBEBPLANCNFSM4YJ6X77Q>
.
--
- Tiemo
|
@@ -36,7 +38,7 @@ def get_context_data(self, *args, **kwargs): | |||
view.request = self.request | |||
try: | |||
view.kwargs = resolved.kwargs | |||
context = view.get_context_data() | |||
context = view._patched_get_context_data() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you manage to fix so that we can use get_context_data
here I think this PR will be usable. Because that's the issue where we get an infinite recursion here.
Unfortunately I haven't had time to look into this PR. However, I did take some time today to get to the bottom of the blocking behavior. I was a little weird to me that views or consumers where actually blocking, because the websocket consumer's dispatch method is wrapper in asgiref's sync_to_async which should execute it in a thread pool. This was bothering me, since I really couldn't explain the blocking behavior, but I now found out where my brain errored. The sync_to_async class has an argument I believe that if you wrap a view class used with sockpuppet in |
Ok, so I finally managed to pin this down. It seems that asgiref 3.3 has changed If you remove asgiref>=3.3 and install asgiref<3.3 instead, the behavior reverts to its previous default of concurrency using a thread pool (for sync parts like the consumer). Again, I'm pretty sure you can break the |
Thank you @blubber for this in-depth investigation! Much appreciated. So in other words this would be an issue if you were running channels 2.4, and not be an issue if you were running channels >=3.0 (as channels installs asgiref >3.3 by default there). |
Well, if you force asgiref <3.3, which I did locally, than you get the old behavior, which also implies a threading issue. Also, it's unclear at this point how Channels are going to manage this change. They might decide to force |
Relates to django/channels#1582 and django/channels#1587 |
Yeah, I was actually kind of waiting to see where they are going with this. That PR you've linked doesn't really solve the entire problem and seems to be stranded. Probably a good idea to wait and see how they handle this. |
The monkey patch used to render the original view's context was not
thread safe. This commit fixes the issue by patching all the
get_context_data methods before accepting requests, instead of while
rendering the reflex' response.