async.txt 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. ====================
  2. Asynchronous support
  3. ====================
  4. .. versionadded:: 3.0
  5. .. currentmodule:: asgiref.sync
  6. Django has support for writing asynchronous ("async") views, along with an
  7. entirely async-enabled request stack if you are running under
  8. :doc:`ASGI </howto/deployment/asgi/index>`. Async views will still work under
  9. WSGI, but with performance penalties, and without the ability to have efficient
  10. long-running requests.
  11. We're still working on async support for the ORM and other parts of Django.
  12. You can expect to see this in future releases. For now, you can use the
  13. :func:`sync_to_async` adapter to interact with the sync parts of Django.
  14. There is also a whole range of async-native Python libraries that you can
  15. integrate with.
  16. .. versionchanged:: 3.1
  17. Support for async views was added.
  18. Async views
  19. ===========
  20. .. versionadded:: 3.1
  21. Any view can be declared async by making the callable part of it return a
  22. coroutine - commonly, this is done using ``async def``. For a function-based
  23. view, this means declaring the whole view using ``async def``. For a
  24. class-based view, this means making its ``__call__()`` method an ``async def``
  25. (not its ``__init__()`` or ``as_view()``).
  26. .. note::
  27. Django uses ``asyncio.iscoroutinefunction`` to test if your view is
  28. asynchronous or not. If you implement your own method of returning a
  29. coroutine, ensure you set the ``_is_coroutine`` attribute of the view
  30. to ``asyncio.coroutines._is_coroutine`` so this function returns ``True``.
  31. Under a WSGI server, async views will run in their own, one-off event loop.
  32. This means you can use async features, like parallel async HTTP requests,
  33. without any issues, but you will not get the benefits of an async stack.
  34. The main benefits are the ability to service hundreds of connections without
  35. using Python threads. This allows you to use slow streaming, long-polling, and
  36. other exciting response types.
  37. If you want to use these, you will need to deploy Django using
  38. :doc:`ASGI </howto/deployment/asgi/index>` instead.
  39. .. warning::
  40. You will only get the benefits of a fully-asynchronous request stack if you
  41. have *no synchronous middleware* loaded into your site. If there is a piece
  42. of synchronous middleware, then Django must use a thread per request to
  43. safely emulate a synchronous environment for it.
  44. Middleware can be built to support :ref:`both sync and async
  45. <async-middleware>` contexts. Some of Django's middleware is built like
  46. this, but not all. To see what middleware Django has to adapt, you can turn
  47. on debug logging for the ``django.request`` logger and look for log
  48. messages about *`"Synchronous middleware ... adapted"*.
  49. In both ASGI and WSGI mode, you can still safely use asynchronous support to
  50. run code in parallel rather than serially. This is especially handy when
  51. dealing with external APIs or data stores.
  52. If you want to call a part of Django that is still synchronous, like the ORM,
  53. you will need to wrap it in a :func:`sync_to_async` call. For example::
  54. from asgiref.sync import sync_to_async
  55. results = sync_to_async(Blog.objects.get)(pk=123)
  56. You may find it easier to move any ORM code into its own function and call that
  57. entire function using :func:`sync_to_async`. For example::
  58. from asgiref.sync import sync_to_async
  59. @sync_to_async
  60. def get_blog(pk):
  61. return Blog.objects.select_related('author').get(pk=pk)
  62. If you accidentally try to call a part of Django that is still synchronous-only
  63. from an async view, you will trigger Django's
  64. :ref:`asynchronous safety protection <async-safety>` to protect your data from
  65. corruption.
  66. Performance
  67. -----------
  68. When running in a mode that does not match the view (e.g. an async view under
  69. WSGI, or a traditional sync view under ASGI), Django must emulate the other
  70. call style to allow your code to run. This context-switch causes a small
  71. performance penalty of around a millisecond.
  72. This is also true of middleware. Django will attempt to minimize the number of
  73. context-switches between sync and async. If you have an ASGI server, but all
  74. your middleware and views are synchronous, it will switch just once, before it
  75. enters the middleware stack.
  76. However, if you put synchronous middleware between an ASGI server and an
  77. asynchronous view, it will have to switch into sync mode for the middleware and
  78. then back to async mode for the view. Django will also hold the sync thread
  79. open for middleware exception propagation. This may not be noticeable at first,
  80. but adding this penalty of one thread per request can remove any async
  81. performance advantage.
  82. You should do your own performance testing to see what effect ASGI versus WSGI
  83. has on your code. In some cases, there may be a performance increase even for
  84. a purely synchronous codebase under ASGI because the request-handling code is
  85. still all running asynchronously. In general you will only want to enable ASGI
  86. mode if you have asynchronous code in your project.
  87. .. _async-safety:
  88. Async safety
  89. ============
  90. Certain key parts of Django are not able to operate safely in an async
  91. environment, as they have global state that is not coroutine-aware. These parts
  92. of Django are classified as "async-unsafe", and are protected from execution in
  93. an async environment. The ORM is the main example, but there are other parts
  94. that are also protected in this way.
  95. If you try to run any of these parts from a thread where there is a *running
  96. event loop*, you will get a
  97. :exc:`~django.core.exceptions.SynchronousOnlyOperation` error. Note that you
  98. don't have to be inside an async function directly to have this error occur. If
  99. you have called a sync function directly from an async function,
  100. without using :func:`sync_to_async` or similar, then it can also occur. This is
  101. because your code is still running in a thread with an active event loop, even
  102. though it may not be declared as async code.
  103. If you encounter this error, you should fix your code to not call the offending
  104. code from an async context. Instead, write your code that talks to async-unsafe
  105. functions in its own, sync function, and call that using
  106. :func:`asgiref.sync.sync_to_async` (or any other way of running sync code in
  107. its own thread).
  108. You may still be forced to run sync code from an async context. For example,
  109. if the requirement is forced on you by an external environment, such as in a
  110. Jupyter_ notebook. If you are sure there is no chance of the code being run
  111. concurrently, and you *absolutely* need to run this sync code from an async
  112. context, then you can disable the warning by setting the
  113. ``DJANGO_ALLOW_ASYNC_UNSAFE`` environment variable to any value.
  114. .. warning::
  115. If you enable this option and there is concurrent access to the
  116. async-unsafe parts of Django, you may suffer data loss or corruption. Be
  117. very careful and do not use this in production environments.
  118. If you need to do this from within Python, do that with ``os.environ``::
  119. import os
  120. os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
  121. .. _Jupyter: https://jupyter.org/
  122. Async adapter functions
  123. =======================
  124. It is necessary to adapt the calling style when calling sync code from an async
  125. context, or vice-versa. For this there are two adapter functions, from the
  126. ``asgiref.sync`` module: :func:`async_to_sync` and :func:`sync_to_async`. They
  127. are used to transition between the calling styles while preserving
  128. compatibility.
  129. These adapter functions are widely used in Django. The `asgiref`_ package
  130. itself is part of the Django project, and it is automatically installed as a
  131. dependency when you install Django with ``pip``.
  132. .. _asgiref: https://pypi.org/project/asgiref/
  133. ``async_to_sync()``
  134. -------------------
  135. .. function:: async_to_sync(async_function, force_new_loop=False)
  136. Takes an async function and returns a sync function that wraps it. Can be used
  137. as either a direct wrapper or a decorator::
  138. from asgiref.sync import async_to_sync
  139. async def get_data(...):
  140. ...
  141. sync_get_data = async_to_sync(get_data)
  142. @async_to_sync
  143. async def get_other_data(...):
  144. ...
  145. The async function is run in the event loop for the current thread, if one is
  146. present. If there is no current event loop, a new event loop is spun up
  147. specifically for the single async invocation and shut down again once it
  148. completes. In either situation, the async function will execute on a different
  149. thread to the calling code.
  150. Threadlocals and contextvars values are preserved across the boundary in both
  151. directions.
  152. :func:`async_to_sync` is essentially a more powerful version of the
  153. :py:func:`asyncio.run` function in Python's standard library. As well
  154. as ensuring threadlocals work, it also enables the ``thread_sensitive`` mode of
  155. :func:`sync_to_async` when that wrapper is used below it.
  156. ``sync_to_async()``
  157. -------------------
  158. .. function:: sync_to_async(sync_function, thread_sensitive=False)
  159. Takes a sync function and returns an async function that wraps it. Can be used
  160. as either a direct wrapper or a decorator::
  161. from asgiref.sync import sync_to_async
  162. async_function = sync_to_async(sync_function)
  163. async_function = sync_to_async(sensitive_sync_function, thread_sensitive=True)
  164. @sync_to_async
  165. def sync_function(...):
  166. ...
  167. Threadlocals and contextvars values are preserved across the boundary in both
  168. directions.
  169. Sync functions tend to be written assuming they all run in the main
  170. thread, so :func:`sync_to_async` has two threading modes:
  171. * ``thread_sensitive=False`` (the default): the sync function will run in a
  172. brand new thread which is then closed once the invocation completes.
  173. * ``thread_sensitive=True``: the sync function will run in the same thread as
  174. all other ``thread_sensitive`` functions. This will be the main thread, if
  175. the main thread is synchronous and you are using the :func:`async_to_sync`
  176. wrapper.
  177. Thread-sensitive mode is quite special, and does a lot of work to run all
  178. functions in the same thread. Note, though, that it *relies on usage of*
  179. :func:`async_to_sync` *above it in the stack* to correctly run things on the
  180. main thread. If you use ``asyncio.run()`` or similar, it will fall back to
  181. running thread-sensitive functions in a single, shared thread, but this will
  182. not be the main thread.
  183. The reason this is needed in Django is that many libraries, specifically
  184. database adapters, require that they are accessed in the same thread that they
  185. were created in. Also a lot of existing Django code assumes it all runs in the
  186. same thread, e.g. middleware adding things to a request for later use in views.
  187. Rather than introduce potential compatibility issues with this code, we instead
  188. opted to add this mode so that all existing Django sync code runs in the same
  189. thread and thus is fully compatible with async mode. Note that sync code will
  190. always be in a *different* thread to any async code that is calling it, so you
  191. should avoid passing raw database handles or other thread-sensitive references
  192. around.