#36863: Under WSGI, multiple calls to asgiref.sync.async_to_sync within the same
request do not share the same event loop.
-------------------------------------+-------------------------------------
     Reporter:  Mykhailo Havelia     |                    Owner:  Vishy
         Type:                       |  Algo
  Cleanup/optimization               |                   Status:  closed
    Component:  HTTP handling        |                  Version:  6.0
     Severity:  Normal               |               Resolution:  needsinfo
     Keywords:  async, wsgi          |             Triage Stage:
                                     |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Comment (by Mykhailo Havelia):

 Replying to [comment:9 Jacob Walls]:

 Hey. Thanks for response 😌


 **Async upside**

 Right now async views under the dev server go through `async_to_sync`,
 which runs them in a separate event loop. That breaks loop-bound resources
 (DB clients, HTTP clients, etc.) and we see errors like "event loop is
 closed". This change keeps async work in a consistent per-request loop, so
 async code behaves like it does under ASGI. The win is mainly correctness
 and lifecycle safety, not raw speed.


 **Cost for WSGI-only users**

 You're right that the current approach introduces a reference cycle by
 wrapping `response.close`. That means the response object is reclaimed on
 a GC pass instead of immediately via refcounting.

 {{{
 gc.disable()
 gc.collect()

 response = application(environ, start_response)
 ref_response = weakref.ref(response)
 response.close()
 del response

 print("before", ref_response() is None)
 gc.collect()
 print("after", ref_response() is None)
 }}}
 Output:
 {{{
 before False
 after True
 }}}

 **Mitigation**

 We can avoid the cycle entirely by moving the cleanup hook into the
 response layer (registering extra closers instead of wrapping close). That
 keeps refcount-based cleanup and makes the WSGI path effectively neutral.

 {{{
 class HttpResponseBase:
     def close(self):
         ...
         signals.request_finished.send(sender=self._handler_class)

         for closer in self._after_resource_closers:
             try:
                 closer()
             except Exception:
                 pass

         self._after_resource_closers.clear()
 }}}

 {{{
 response._add_after_resorce_closer(cleanup_stack.close)
 }}}

 What do you think? 🙂
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36863#comment:10>
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 visit 
https://groups.google.com/d/msgid/django-updates/0107019c0f6e31fd-d00904fa-5345-4aa2-85ba-21bf5072a26a-000000%40eu-central-1.amazonses.com.

Reply via email to