Pro Yearly is on sale from $80 to $50! »

Taking Django Async

Taking Django Async

A talk I gave at PyCon US 2018

077e9a0cb34fa3eba2699240c9509717?s=128

Andrew Godwin

May 11, 2018
Tweet

Transcript

  1. TAKING Andrew Godwin @andrewgodwin DJANGO ASYNC

  2. Hi, I’m Andrew Godwin • Django core team member •

    Senior Software Engineer at • Does network programming for "fun"
  3. 2015: Channels 0.1

  4. Adds async protocol support to Django Headline protocol is WebSockets

    Also allows background jobs
  5. 2017: Channels 1.0

  6. Python 2.7 compatible Twisted for webserver Synchronous Django code

  7. Webserver (Twisted) Django (Sync worker process) Client Channel Layer (Redis

    or other)
  8. Webserver (Twisted) Django (Sync worker process) Clients Channel Layer (Redis

    or other) Webserver (Twisted) Django (Sync worker process)
  9. Too many moving pieces No asyncio support Easy to shoot

    yourself in the foot
  10. I was wrong.

  11. 2018: Channels 2.0

  12. Asyncio-native Python 3.5 and up Supports async coroutines and sync

    threads
  13. Webserver (asyncio-native) Django (coroutines/threads) Client

  14. Webserver (Twisted) Django (Sync worker process) Clients Webserver (Twisted) Django

    (Sync worker process) Channel Layer (Redis or other)
  15. 75% rewrite

  16. Async interfaces are separate to sync You can't provide both

    through one API
  17. bit.ly/py-async-simp https://www.aeracode.org/2018/02/19/python-async-simplified/

  18. Django had to be partially async

  19. WSGI Handler (WSGI-to-request translator) URL Routing (path to view mapping)

    Django Middleware (auth, sessions, etc) Views (logic and presentation) ORM (database kerfuffling)
  20. ASGI Handler (ASGI-to-request translator) URL Routing (path to view mapping)

    Django Middleware (auth, sessions, etc.) Views (logic and presentation) ORM (database kerfuffling)
  21. ASGI Handler (ASGI-to-request translator) URL Routing (path to view mapping)

    Django Middleware (auth, sessions, etc.) Views (logic and presentation) ORM (database kerfuffling) ASGI Routing (path/protocol/etc. mapping) ASGI Middleware (auth, sessions, etc.) Consumers (logic and presentation) synchronous
  22. Async-native most of the way

  23. But how?

  24. sync_to_async async_to_sync

  25. sync_to_async async_to_sync

  26. Synchronous code has to run in threads ThreadPoolExecutor does most

    of the hard work
  27. loop = asyncio.get_event_loop() future = loop.run_in_executor( self.threadpool, func, ) return

    await future
  28. Calling the ORM Rendering templates Handing off to Django views

  29. sync_to_async async_to_sync

  30. Async code runs on the event loop We need to

    go find it! Or make our own.
  31. # Make a future for the return information call_result =

    Future() # Use call_soon_threadsafe to schedule a synchronous callback on the # main event loop's thread if not (self.main_event_loop and self.main_event_loop.is_running()): # Make our own event loop and run inside that. loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) try: loop.run_until_complete(self.main_wrap(args, kwargs, call_result)) finally: try: if hasattr(loop, "shutdown_asyncgens"): loop.run_until_complete(loop.shutdown_asyncgens()) finally: loop.close() asyncio.set_event_loop(self.main_event_loop) else: self.main_event_loop.call_soon_threadsafe( self.main_event_loop.create_task, self.main_wrap( args, kwargs, call_result, ), ) # Wait for results from the future. return call_result.result()
  32. Make a Future Jump to the main thread and add

    the coroutine Tie the coroutine's end to triggering the Future Block the thread on the Future
  33. Lets us provide async-native APIs And still let synchronous code

    use them
  34. Both async and sync code are useful Channels lets you

    write your code as both
  35. ASGI Handler (ASGI-to-request translator) URL Routing (path to view mapping)

    Django Middleware (auth, sessions, etc.) Views (logic and presentation) ORM (database kerfuffling) ASGI Routing (path/protocol/etc. mapping) ASGI Middleware (auth, sessions, etc.) Consumers (logic and presentation) synchronous
  36. How to make WSGI async?

  37. ASGI: It's like WSGI but with an A in it

    Also asyncio and stuff like that
  38. def application(environ, start_response): ... start_response(...) yield data

  39. class Application: def __init__(self, scope): ... async def __call__(self, receive,

    send): event = await receive() ... await send(data)
  40. "Turtles all the way down" Routing is an ASGI app.

    Middleware is too.
  41. What does this mean for Django?

  42. How much can we make async? Can we keep sync

    compatability?
  43. What does an async ORM look like? Is it even

    a sensible endeavour?
  44. Can we make Django more pluggable? Finally get to that

    shareable-middleware goal
  45. Do we need to replace WSGI? Or does nobody want

    long-polling or websockets?
  46. Multiple servers (daphne, uvicorn) Multiple frameworks (Django, …?)

  47. Do we want to have everyone writing async? What's the

    balance? How do we be flexible enough?
  48. What is Django?

  49. Thanks. Andrew Godwin @andrewgodwin aeracode.org