Keynote: Sketching out a Django redesign
CC Attribution 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Christie, Tom
This talk will take a look at how Django could look if it was designed from the ground up as an ASGI framework. We’ll examine how we could approach this design in a way that helps reduce tight coupling and framework complexity, and take a look at what new capabilities this would provide. This talk sketches out how a redesign of Django might look, if it was reworked as an ASGI web framework. It draws on the design work behind the Starlette web framework and the Uvicorn web server. Where we are now Overview of the fundamentals of the request/response flow in Django: WSGI, the Django middleware stack, and request routing. Introducing ASGI ASGI’s evolution. The ASGI interface. Capabilities that ASGI provides: Concurrency & performance, WebSockets, Server Sent Events, HTTP/2 Server Push, Background tasks. Startup / shutdown / clock events. Running threaded code in an ASGI context. Concurrency and database transactionality. An ASGI-centered design ASGI middleware vs. a middleware interface. Using ASGI middleware, but with a request/response interface. Routing as an ASGI interface. Class-based views as an ASGI interface. Mountable ASGI components. An ASGI-based TestClient. Settings and configuration Per-component configuration, over global configuration settings. Implications to framework complexity Why this approach matters in terms of complexity and framework-interoperation. Challenges Does it matter enough? How to get from A to B? What does it make more difficult? What’s the bigger picture?
 We're there. OK. Yeah, one last thing before I get started. I think my kids might be watching on the live stream at home. So hi, boys. Yeah, hi, kids. Thank you. OK, great. Hi. Thank you very much. My name is Tom Chrissy. I've been a long time Python and Django user. I'm the maintainer of several open source projects. And I'm most well known for being the author of Django Rest Framework. Now, I'm fortunate enough to be in a position where I work on open source full time now for my day job. Django Rest Framework launched a sponsorship program where lots and lots of different companies can contribute a small amount per month. And this is what pays for my day job at the moment. So and I've been spending, as well as working on Django Rest Framework and helping manage that, spending a lot of time recently on async, which is what I'm going to talk about today. So Python is at a really big crossroads right now. Python 3.5 introduced some new functionality in the form of the async and await keywords. The async and await keywords help us use an entirely new concurrency model that is much, much more efficient than the existing threading concurrency model. And this is going to let us do some really exciting things. For one thing, it lets us build very, very high throughput web services. It allows us to make non-blocking HTTP requests so we can build Python gateway APIs or proxy services that are able to handle very high concurrency, high throughput, which is a domain where Python has traditionally not excelled in. And it will also allow us to handle real time network protocols, such as WebSockets. So we'll be able to start building more and more real time responsive web applications, chat services, games, real time monitoring, all of these kinds of things. Before we dive into this, a little bit of groundwork, concurrency, what is concurrency? Concurrency is all about the number of tasks that your server is able to handle simultaneously. In the web development land, this maps on to how many HTTP connections is your server able to hold open at the same time, and then in turn also influences what is the throughput that your server is able to achieve. How many requests per second can each individual server handle? So what are some of the ways that we have to increase the concurrency of these web services that we're building? So the bluntest hammer in our toolbox, our sledge hammer, is multi-host, so horizontal scaling, where you add more servers running the same code base, and you're going to be able to handle a greater number of requests per second. Well, let's bring that down a level to a single server. How can we increase the concurrency on a single server? Within a single server, we'll have a number of CPU cores. We want to make sure that all of those CPU cores are fully utilized all the time, as much as possible, so we can run multiple processes within each machine, or all pretty much completely independent, but all running at the same time. OK, bring that down again another level. How do we increase the concurrency within a single process running on a single server? So traditionally, what we've used is threaded concurrency. With threaded concurrency, what happens is we have a number of simultaneous flows of control running through our program, and each individual flow of control will have large chunks of time where it's not utilizing the CPU because it's waiting on IO from the rest of the system. So any time your program goes off and makes an HTTP request, makes a database access, accesses some disk IO, there's this relatively huge chunk of time when that thread of control is not able to do anything else until it gets a response back from the operating system. So with multi-threading, what the operating system does for you is handles interleaving several different flows of control and switches between them very, very quickly, so it appears as if all these flows of control are happening at the same time. Now, more recently, the async model has been introduced, and the key point to take away at this point in time is async is an alternative to multi-threading. With async, you will have multiple tasks rather than multiple threads, and your multiple tasks will all be running within a single thread, but you will still have multiple processes, all running lots and lots of little tasks within it, and you will still be running across multiple different hosts. And async is, as we said, far more efficient. So what are the differences between these two? With threaded concurrency, everything's managed by the operating system, and you don't get to see, as a programmer, when am I going to switch between one of my two different threads of control? In async, it's a completely different model in that it's managed by the runtime, it's managed by Python itself, or by node, or whatever runtime you happen to be using. And the points of context switching between these different flows of control are still the points at which you're performing I.O, so network requests, database accesses, disk accesses, but they have to be explicitly marked in the program so that the runtime knows, OK, here's a point at which I can context switch. So you have this entirely new syntax that is introduced, the async and await keyword. And what's problematic is that these two models are largely incompatible. If you're going to have explicit context switching, then you need to have explicit context switching all the way through. So if you're low level, I'm making a network request as being explicitly marked, then anything that calls into that also needs to be marked up as being an async function. And although there are ways that we can mediate between these two styles, it's a bit fiddly. So there's this huge challenge for the ecosystem. It's a little bit of a fork in the road. And it's important to knowledge that, as with any new technology, there are costs as well. And we need to talk about them upfront and recognize them so that we're in a good position to judge what the trade-offs are. All right. So there's this huge upfront cost in terms of all of the new code that needs to be written in order to work with async. So for one thing, all of the low-level networking stuff, all of the database drivers or making HTTP requests, or exactly how do you go about making disk IO, people need to write low-level async libraries to interact with that because it doesn't exist in the standard library because it wasn't a thing when Python was not 3.5. Not quite true, but pretty much. What else? OK, well, it's a different paradigm. And there's a little bit more to think about as well as a developer. There's two different types of function call you've got now. You've got async function calls, which is anything that is making IO. And you've got regular function calls, which is only allowed to just be doing regular programming-type stuff. It's using the CPU, and it's performing some kind of computation. One of the other reasons that it's worthwhile being cautious here as well is, you might not care about throughput. So when you're looking at the performance of your web service, the thing that your users will most care about is how long does it take once they've made a request for them to get a page back in front of them? What is the latency of the request? And async has an influence on that, but it's more complicated. And unless you are building a very high volume service, the throughput that it's able to handle might not matter that much. Unless you have several server instances, why do you care that it could handle high load? Fine. So that's the words that could, you know, things to bear in mind, but the benefits. Right. Sometimes the performance really does matter. And in the cases where it does, it's really, really important that this should not be a blocker for businesses adopting Python. We want to be able to build hugely scalable web services with Python. And we want the mega companies who are developing these flagship services to be choosing Python right at the start, building highly successful products with it, and being able to go to the rest of the world, hey, look at this awesome thing that company XYZ has built in Python. And for them to go, yeah, this was a great development experience for our team. And we're very happy with the choice that we made. What else? Real time, because Async is much more resource efficient, we're able to hold open lots and lots of network connections without that having a high impact. So we can hold open things like WebSockins, and we could do real time communications, non-blocking HTTP requests, being able to perform parallelization within our codes without that being a heavy weight thing that branching lots of new threads would be. The explicit IO is actually also a benefit as well, but we'll come to that later in the talk. And one other thing to say here is performance can mean different things in different contexts. So being able to build very highly concurrent web services, well, a flip way round of looking at it, that is, on very lightly resourced systems, on embedded systems, these sorts of things will work really, really well. Or suppose you don't require high throughput on your site most of the time, but it's only to get a huge traffic spike. Your service is much, much more resilient to that. So it doesn't have to always be about, this is just about high throughput services all the time. There are other reasons why it's important. And there's this other thing. It's something that has been said lots of times. People say, oh, Python's slow. We don't want to use Python for xyz because Python's slow. Now, yeah, OK, benchmarks, nonsense, nonsense, nonsense. The tech and power benchmarks are the least awful ones that are around. They've got a number of different test cases that exercise various different bits of do some database stuff, do reads, writes, whatever. This particular test case, I think, is the most representative of web applications because it does a little bit of database reading. It does a little bit of template rendering, and it does a bit of exercising the web stack. And yes, I've cut some Go results off of the top here. And yes, I've got lots and lots of results from several different frameworks off the bottom here. But the important things to look at is one at the top. This is Go written against the Go standard library. The ones in yellow, those are all node based. And these ones in blue are both Python. And we've got a single very beefy server there that on its own is servicing 70,000 requests per second, which is probably more than you need. So it's about making sure that there isn't this blocker to business adoption. Python is in the same ballpark as node and Go for almost all intents and purposes that we in this room would probably need. So OK, great. We'd like to start taking advantage of this. What do we need to do? The first thing in the stack that we run into is WISCII. WISCII is the interface that exists between the server, Gunicorn or UWISCII or something like that, and the application framework, the web framework. And WISCII has a couple of very big constraints for what we'd like to be able to do, one of which is inherently a thread concurrency interface. It doesn't have that async definition on there, so it's not allowed to do any async context switching inside it. And it's designed purely for handling HTTP request and responses, so it's got no easy way to adapt it for web sockets. So hello, ASCII. Along comes Django channels, which originally was designed in order to keep SEP to deal with web sockets in Django by largely keeping separate the asynchronous nature of handling the web socket connections from the synchronous nature of the thread-based Django code base. And it has gradually evolved under Andrew Godwin's wonderful guidance, wherever there we go, into becoming a general purpose application interface, an alternative to WISCII. An async alternative to WISCII. An async alternative to WISCII that also handles web sockets, that is also able to deal with, and that is also a more general purpose interface and that is more adaptable. So this is how the interface for ASCII looks. As of ASCII 3, which is we're done now. I mean, not we're done, but we're kind of done for the big stuff, anyway. We've got these three variables that we call into the function with scope, receive send. Scope is a whole bunch of state information about the incoming connection in a dictionary with a bunch of keys in it. And receive and send are two channels on which the web application communicates with the web server. You use, for example, in the HTTP context, you'll use the receive channel to do things like pulling the body of the HTTP request. You don't want it in the scope because then you'd have to have the whole body arrive all at once, so instead you'd like to be able to stream it in in case you need to do that. And also for sending out the outgoing HTTP response. ASCII gives us a lot of nice things. So obviously we've got the potential for the performance characteristics of async that we've talked about. Real-time communication. So it's not just web sockets. There's also server-sense events, which are very similar to web sockets. But they're over HTTP only. And they are uni-directional, so just sending from the server to the clients. But they can be a nice, simple thing to use without having to go to the web socket roots. HTTP long polling. HTTP's to server push, where when a request is made to your web server, you've hit the home page. The web server knows, OK, I haven't got any cookies from this thing. It probably is going to need all of these other assets on the page. And I don't want it to have to go and do a couple of different round trips before it gets them. So I'm going to preemptively start pushing these assets over the connection. I'll send back there. Here's the home page. And I'm also going to start sending down my cat gifts at the same time. Aski also has startup and shutdown events, which gives a more nicely managed context for running tasks within that domain than Whiskey has, which doesn't have any kind of way of communicating, OK, I'm ready to go now. Or, OK, can you please start shutting things down? And actually, it's pretty powerful, because it allows us to do things like build clock or timer-driven events and know that the events that we're scheduling, if they're running, then when the server requests, when we're requested to shut down, we can wait until those events are finished and then we can send back to the server, say, OK, I'm all finished now and we know that we're going to terminate cleanly, which can potentially allow us to build really nice task queues and so on in a much more simple way. And it's a more adaptable interface, right? We can potentially extend this into other protocols as well. So it's here for the long term. Where's the Aski landscape at the moment? OK, so we've got several different server implementations already. We've got Daphne, the original one. Hypercorn, somebody, Phil Jones, been working on, and UVaCorn. It's one that I've spent my time on. We've also got lots and lots of Aski web frameworks emerging. Starlet is the one that I've been working on, and I'm going to show you some of how that looks a bit different to Django and why that's interesting in a moment. Django channels is slightly the odd one out on this list, because that's async on the front, but threaded most of the rest of the way through. And we've also got other stuff that's starting to be developed in this area as well. So what I want to do over the next few slides is take a look at if we're building an async web framework, what are some of the ways that we could do things a little bit differently? And I think a lot of this can feed into some of the work that we're hoping to start doing on Django over the coming months. Now, Starlet, OK, up here, this example looks like any old standard micro web framework, but there are a few ways that it's put together that I think are a little bit interesting. Now, it's one thing that we're going to see a lot. It's Aski all the way through. We use the Aski interface as the primary thing on which the stack is built all the way right up to the, when you're working with a view, and then you're in request response, and you're dealing with requests and responses, but all the way through every other part of the system, if you start to dig into what's happening, it's based on Aski. And here's a good example of that, is a response in Starlet itself exposes the Aski interface. So an instance of a response is a valid web framework. It's a very small one that just does one thing, but it is. Interesting. OK. Just to get a bit of an idea about how some of the components look, this isn't the level that you'll be working at normally. This is using requests and responses within a raw Aski interface. Normally, you'd be working within a proper request response view, but you can see how requests are just an interface that is instantiated over the Aski state that you can then do stuff with. OK. Fine. Let's have another look at ways in which we use Aski all the way through. The test client in Starlet is built upon requests. It is requests. It's requests, but with an adapter class, an adapter class that instead of making raw network requests, plugs directly into an A&E into an Aski framework. And that's great because we can use our test client to test any Aski web framework, not just Starlet, but any of the other ones that we saw were up on the screen earlier, or to test any Aski component as well. So here, we're just instantiating a response. We're making a request out to it using the standard requests library with all of the standard API and behaviors requests. The next place, we said we're using it all the way through. So of course, we're writing Aski middleware as well. Now, in, say, Django and lots of other web frameworks, what happens with the HTTP dispatching is that the first thing that happens, the request comes in, we create some kind of request instance. And then we pass our request instance all the way through a middleware stack that gets request instances, calls into the next thing in the chain, and returns response instances. Why wouldn't we want to do that? Sounds fine. Sounds good. Well, what's nice about using Aski as the middleware interface is that your middleware implementations are reusable across, again, any Aski web framework. They're also independently testable using the same test client that you use for testing everything else. It's also important to design things in this way because if you build your middleware as a request response interface, what do you do when you come to WebSockets? We're going to build another different type of interface. If you're working against the Aski interface, it's much clearer how to build, for example, middleware that authenticates both HTTP and WebSocket requests based on the headers in either. OK, but as a developer, you don't necessarily want to be working at that level all the time. No problem, one of the things that Starlet provides is a class that you can subclass that, to you, the end developer provides a request response interface, but to the outside world provides the Aski interface. So when the request comes in, it creates a request instance, sends it off to your dispatch method. You call into call next, and it does the job of inspecting any messages coming back down and marshalling those into response instances. And that's great because you're working at the request response level, but again, you're building reusable middleware that we can share with the rest of the community and that when the rest of the community building middleware, they can share with us. What else? Mountable apps, top example here. If you want to build a file server in Starlet, that's what you do. You can run that top example with Daphne, with HyperCorm, with Uvacorm, and that'll just work. If you want to put that within your web application and serve static files within your web application, you mount that to a particular endpoint. And again, we're building more and more reusable components. So a great example of this, talking half an hour ago about Ariadne GraphQL server. It has an ASCII interface. You can just plug it straight in, and it's not coupled to a particular web framework. It's about ASCII. Another example, class-based views. Similar sorts of thing. The class-based views in Starlet expose the ASCII interface. You can do interesting things like it. That allows you to build out more high-level variations on this. So if you want to build something that's like Res Frameworks' view sets or anything else like that. We'll see it again a bit later on is a point of difference. It is all the way through in this style of design. We're using per-component configuration. So we're not using framework-level settings that are then action over distance picked up somewhere inside the web framework. Not quite sure where and have some kind of effect. Hard to see as a developer if you want to kind of plum into what's happening here, where is this getting used. And we're also not using application-wide settings where we're plugging all of the configuration directly into this one single application instance. So per-component configuration, which again, helps with better re-use shareable components across the ecosystem, helps with being able to test components in isolation, so on. So what I think is interesting about some of the design in this is, again, all stuff that I hope, as we're progressing on Django, can feed into some of the ASCII work that's going on there. And it's all about the overall complexity of the stack. You've got this one single interface style that runs all the way through. Very consistent. You can use a test client with lots of different components. It's very, very composable. It's also very performant because we're not introducing any extra abstractions on top of this interface style. Woo-ee! Limey. Oh, yeah. I said that I say that. We're not in the States, so nobody picks me up for it. But there we go. OK. Async. Great. What about the database? Blimey. Oh, god, there it is again. OK. Django, RRM, SQL Alchemy, are both thread synchronous APIs. And not just that, but if you go down to the lower levels, there's something that is very analogous to whiskey in a way is the Python interface that is used to separate the database driver from the higher level code that is working with that driver. And it's also a thread synchronous interface. And we've got lots and lots of folks who've been working in the async space developing async database drivers. But we don't have a standard interface onto them. So it's difficult to start writing tooling that works together with SQL Lite and Postgres and MySQL. So I've recently released a package called Databases, which aims to address this. It's not analogous to DBAPI exactly because it's a slightly different level of interface. It's aimed to be something that is very developer facing interface. You can use it to make raw SQL queries to any of those async database drivers. You can also use it to work together with SQL Alchemy Core, so the table definitions and the query builder, which is an absolutely stellar tool. It's not all the way to an ARM, but it's a really productive level to be working at nonetheless. It's also great because it allows you to use SQL Alchemy Core. If you write your table definitions using that, you then have support for migrations using their Alembic, which is analogous to Django's migrations. And it provides transaction support as well and dealing with database connections sensibly and all that sort of stuff for you. Great. So there's a low-level answer to what do we need to do in the async land in order to address the database. But there's still a component that's missing there, which is a fully-fledged ORM. So I've also released another package, independent of databases, but built on top of it, which is a Django-like or the start of a Django-like ORM, but asynchronous. And again, because it's built onto the databases, we've still got the migration support. It's got a very Django-like API. It's not all the way there yet, but we've got a bunch of stuff in, so we've got all of the different filter expressions and so on. We've got support for foreign key relationships, and we've got support for select-related. We don't have support for many-to-many yet, or reverse foreign keys, or prefetch-related. And if you're building an async ORM, because IO always needs to be explicit, there are a few things that we need to do a little bit differently. So for example, in the Django-ORM, if you've fetched a model instance, you haven't fetched a relationship on it, and you access that relationship on it, it will go off and generate some SQL and resolve that automatically for you. Can't do that with async. You have to either have called select-related on it in the first place, or explicitly load it and say, I want to resolve this thing now, and it will raise an error to you otherwise. Similar stuff with paging through query sets. If you don't want to fetch the entire set of results in a query set all at once, you need to do that explicitly. In the syntax, it's an async iterator. So it looks like async for instance in query set. But it's a bit different to being able to just do that implicitly in Django. As well as looking a bit differently, there are also benefits to this explicit style. So I think a very large number of people in this room will have hit the types of cases where maybe you're working with your own code base, or you're working with a code base that you've recently come into, and this view is running really, really slowly. Why is that? And you dig into it and dig into it, and you find that somewhere in the template code, it's iterating over a query set, and it's accessing some relationship or some field on there that's not there, and it's generating SQL queries again, and again, and again. Now, in async, in a standard style setup, you can't run database queries in a template at all. It stops you from doing that. It ensures that anything that you're accessing, you have loaded, and if you try to load something without explicitly doing that, it will raise you a great big error. Similarly, you can't do database lookups in str. And I've worked with clients who go, this thing is this bit in the admin is running incredibly slowly. Why is that? It's because when you're displaying your model instance, you're generating SQL queries. You don't want to do that. And having that tighter control, yeah, it's a bit more to think about, but it's also this huge benefit as well. People run into this with Django, and they go, Django slow. Well, it's not, no, but it's allowing you to not look after yourself. Now, this is a bit different, but I've been working with it for a while, and I really, really like it now. I really like it. What else? Yeah, there's even more to think about. So you've gone to all this trouble of building potentially very high concurrency services. You might want to be precise about how you think about database connections and database transactions. So a really good example of this is if you are building microservices, you've got a gateway API. And the job of the gateway API is to request comes in, make a database access in order to authenticate the user, great users authenticated, go and send an HTTP request out to some other service, wait for the response, send it back to the end user. And what you don't want to do if you want to be able to build very high throughput services like that is hold on to your database connection, or especially not a database transaction for the entire duration of that HTTP request. There's just no need. You want to acquire your connection, do your work, let go of it, then go and make your HTTP request, because that is a really, really slow operation. You don't want to be hanging on to this valuable system resource for the whole duration of it. So for example, databases is designed to be very liberal with acquiring and releasing connections to the connection pool unless you are explicitly within a transaction. And there's other places to think about as well. So if we want to have async HTTP requests, the requests library doesn't give that to us out of the box yet. I've just released a package recently, requests async, which is requests with a different adapter that makes asynchronous network requests. Email, really, you want to make non-blocking SNTP requests. There's a great library for that already. Caching, you're interacting with Redis or Memcache. Again, that's a network operation. Well, that was a back of the stage there. Even validation, right? So you're building a validation library, form validation, API validation. If you want to be able to perform any database queries within that, you're going to need to be able to make sure that your validation library provides support for async methods as well. And stuff like password hashing. So password hashing is interesting because it's deliberately designed to run slowly. And if you run something very, very, very, very slowly in a single async task, then what happens is all of that normal, very fast interleaving between all of your different tasks stops happening. And this one task is just hogging everything else up. And these other tasks are getting blocked. So two different ways you can resolve that. Either you can make sure that your password hashing is yielding to the other tasks, lots of the ways through. Or you can take a simple approach. You just dispatch it off to a thread. You say, OK, go and run this thing in a thread. Don't block the main event loop. So this is the stack of stuff that I've been working on recently and trying to lay a really healthy ground work for all of the async stuff that I think is about to be on the way at a very great rate. Let's take a look at how all of this stuff hangs together if you want to start building full stack web frameworks based on this. Don't worry if you can't read everything in the slide here. It's not so bad, right? But it's just to give a bit of a flavor of a few different points of difference between what we used to in Django and some of the design style here. Settings, we're still pulling all of our settings together in the same place. We're still using standard 12 factor configuration style. But we're then using those settings and pulling them into the components or the resources that we're using them within so they're less entangled in their design. Models kind of similar. Point here being the async landscape is really starting to mature. Type validation, type system is a library that handles both API validation and form rendering, kind of similar to REST framework serializers, but it's trimmed down a bit. The difference here being our validation libraries need to be able to support async methods as well. Star point in Starlet, we call them endpoints, or in REST framework, including in Django views. The interesting thing to kind of note here is it's very, very clear where our database accesses are happening or network requests. So we've got some views which are not async. There's no database accesses that are happening in there. They don't need to be async. We can see exactly where stuff's happening with the database, which is really great. Routing, pretty similar. We've talked about per component configuration and being able to mount ASCII apps. And pulling it all together, we've got a single app instance at the top, which ends up just being a bunch of middleware that you pulled into it, a couple of other default middle ways that it will always include, because you pretty much always want them to deal with server errors or exception handling. So the middleware and then the routing. And that's it. So we've got something that's quite Django-ish. This is very, very decoupled, low impact approach. Performs great in terms of throughput, as great as you could reasonably need. It's able to support web sockets or service center vents and various other stuff, background tasks. We mentioned briefly, and HTTP server, two server push. And it lets us do things that we just can't do with Django at the moment. So building API gateway services that can comfortably handle tens of thousands of requests per second, building proxy services, building GraphQL backends with real time subscription endpoints that can serve thousands of clients at the same time. All things that we would really like to start to be able to push Django into the realm of, and doing all the legwork for this. And the other thing that we have is this very, very composed style. So we can start right at the bottom here, a raw ASCII interface. We can work away gradually up using individual ASCII components, up to using these tools in a very micro frameworkish way, all the way up to using them in the same sort of way as a full stack web framework. So what does this mean for Django? So a few things in tandem. I suppose it should be two things in tandem, really. I don't know what a three-way bike's called. First of all, progressively adding ASCII into the stack. So Andrew Godwin has what I think is actually a very achievable proposal for how we would go about getting some aspects of this into Django, starting off by adding an ASCII interface onto Django, but running everything within thread pools beyond that. Then gradually you iterate, and you build that out so that the middleware stack is ASCII-based. And at that point, if you need to be able to drop into async in a view, then you can. Your Django components might not necessarily support it, but if you want to do a little bit, if you don't mind doing a little bit of extra work, you've got more power available to you. And then iterating on from there and looking at turning some of the components async along the way. And the other thing is, there's a lot of groundwork that's being done for this already. Things like the databases package could potentially be used in an async Django R&M. Maybe we don't need an async Django R&M. Maybe a one that just dispatches into the thread pool will work equally well. But we've got a lot of groundwork, ladies, the point. And at the same time as all of that, pushing really hard to keep maturing the async landscape. And in particular, pushing really hard to do that in a way, that is something that the whole community can share. So getting behind ASCII is a standard because it allows us to work together really efficiently. And I think that the Python community has a really important message here, which is, I don't think there's anything out there that beats Python for productivity. It's absolutely awesome. An async makes it competitive with the other big players in its zone with Node and with Go and brings support for real-time protocols and loads of other stuff. To me, if we can reach a great level of functionality with this stuff, Python's really hitting the sweet spot there. Yeah, I think it's a really simple message. And I think it's really powerful. And I think it's one that we need to start communicating to the rest of the world. OK, quickly, all of the Django Fellows work only ever happens because it's sponsored. My time only happens because it's sponsored. I think that the ask that we have when we say, hey, I want to work for your companies full time for 50 euros a month or 150 euros a month, if your businesses can see the potential of the impact that this sort of work plus the work on REST framework, the 3.10 that's coming out soon and the open API support that's going to be in there. And your companies are going to benefit from these things over the next year, the next two years, the next five years, and into the long term. I think the investment is just an absolute no-brainer. So whether that is through the DSF, through REST framework sponsorships, whether it's through community events like this, or Django Girls, or local events in your something, anything, it's so important. I also think in order for us to take things to the next level that we're going to need to find other monetization strategies as well and ones that benefit the community. And I think for this to happen with frameworks like Django and like Rails, they need to be truly succeeding in the product space as well, providing great products that are the fastest possible on-ramp for developers to get started with Django or with Rails. Give developers the ability to start mocking out your API, your API documentation, or give developers the ability to start sketching out their admin and start putting data in there. Yes, you might be limited to a certain number of rows in there. We've got some constraints that we're not going to do it. We're only going to be with you for the prototyping stage. But the goal of our product is to lose you as a user. We want you to end up taking complete control of the framework at the end of the day. We'll have a great big button there. Great, let's get started on my product. I'm happy with the prototyping stage. Our front-end team's been working against it. And at the end of the day, that is the most efficient thing for us to be doing for our developers at a certain point in the maturity of these frameworks. And if we can do it, if we can nail it, we could have a company that just works on Django, or Starlet, or the Python ecosystem full-time. And the more we can succeed with this, the more developers we can bring on board. I was chatting over the slides with Carlton late last night. And we went over these last three slides. And summed them up to him at the end of the set. First one is, give us your money. Next one is, here's how we make money. And the last one is, it's not about the money. And it's not. It's really not. The money's a tool. Why are we here? Why do we care about open source? Why have we all come together? Our guiding light in all of this, our thing that keeps us focused, has to be impact on society, betterment of society. As is great quote by an American trappist monk, Thomas Moore, which is, if we do not do this, we may find ourselves climbing to the very top of the ladder of success, only to find that the ladder has been leaning against the wrong wall. And if we focus on our values, rather than allowing them the raw power of the market to set the direction and the flow, then we change the rules of the game. And you help evolve the very currency with which the market actually trades in. Oh, hang on a minute. I didn't show you my lovely bit. That was the slide for that one. You know, it's all about taking our individual creative spark, our individual instinctive sense of empathy, and working together as a community, as a whole, and as a society. Thank you. Thank you.
 We're there.  OK.  Yeah, one last thing before I get started.  I think my kids might be watching on the live stream at home.  So hi, boys.  Yeah, hi, kids.  Thank you.  OK, great.  Hi.  Thank you very much.  My name is Tom Chrissy.  I've been a long time Python and Django user.  I'm the maintainer of several open source projects.  And I'm most well known for being the author of Django  Rest Framework.  Now, I'm fortunate enough to be in a position  where I work on open source full time now for my day job.  Django Rest Framework launched a sponsorship program  where lots and lots of different companies  can contribute a small amount per month.  And this is what pays for my day job at the moment.  So and I've been spending, as well as working on Django Rest  Framework and helping manage that,  spending a lot of time recently on async,  which is what I'm going to talk about today.  So Python is at a really big crossroads right now.  Python 3.5 introduced some new functionality  in the form of the async and await keywords.  The async and await keywords help  us use an entirely new concurrency model that  is much, much more efficient than the existing threading  concurrency model.  And this is going to let us do some really exciting things.  For one thing, it lets us build very, very high throughput  web services.  It allows us to make non-blocking HTTP requests  so we can build Python gateway APIs or proxy services that  are able to handle very high concurrency, high throughput,  which is a domain where Python has traditionally not  excelled in.  And it will also allow us to handle real time network  protocols, such as WebSockets.  So we'll be able to start building more and more real time  responsive web applications, chat services, games, real time  monitoring, all of these kinds of things.  Before we dive into this, a little bit of groundwork,  concurrency, what is concurrency?  Concurrency is all about the number of tasks  that your server is able to handle simultaneously.  In the web development land, this  maps on to how many HTTP connections is your server  able to hold open at the same time,  and then in turn also influences what  is the throughput that your server is able to achieve.  How many requests per second can each individual server  handle?  So what are some of the ways that we  have to increase the concurrency of these web services  that we're building?  So the bluntest hammer in our toolbox, our sledge hammer,  is multi-host, so horizontal scaling,  where you add more servers running the same code base,  and you're going to be able to handle a greater number of requests  per second.  Well, let's bring that down a level to a single server.  How can we increase the concurrency on a single server?  Within a single server, we'll have a number of CPU cores.  We want to make sure that all of those CPU cores are  fully utilized all the time, as much as possible,  so we can run multiple processes within each machine,  or all pretty much completely independent,  but all running at the same time.  OK, bring that down again another level.  How do we increase the concurrency  within a single process running on a single server?  So traditionally, what we've used is threaded concurrency.  With threaded concurrency, what happens  is we have a number of simultaneous flows of control  running through our program, and each individual flow of control  will have large chunks of time where it's not utilizing the CPU  because it's waiting on IO from the rest of the system.  So any time your program goes off and makes an HTTP request,  makes a database access, accesses some disk IO,  there's this relatively huge chunk of time  when that thread of control is not able to do anything else  until it gets a response back from the operating system.  So with multi-threading, what the operating system does for you  is handles interleaving several different flows of control  and switches between them very, very quickly,  so it appears as if all these flows of control  are happening at the same time.  Now, more recently, the async model has been introduced,  and the key point to take away at this point in time  is async is an alternative to multi-threading.  With async, you will have multiple tasks rather  than multiple threads, and your multiple tasks  will all be running within a single thread,  but you will still have multiple processes,  all running lots and lots of little tasks within it,  and you will still be running across multiple different hosts.  And async is, as we said, far more efficient.  So what are the differences between these two?  With threaded concurrency, everything's  managed by the operating system, and you don't get to see,  as a programmer, when am I going to switch between one  of my two different threads of control?  In async, it's a completely different model  in that it's managed by the runtime,  it's managed by Python itself, or by node, or whatever  runtime you happen to be using.  And the points of context switching  between these different flows of control  are still the points at which you're performing I.O,  so network requests, database accesses, disk accesses,  but they have to be explicitly marked in the program  so that the runtime knows, OK, here's  a point at which I can context switch.  So you have this entirely new syntax that is introduced,  the async and await keyword.  And what's problematic is that these two models  are largely incompatible.  If you're going to have explicit context switching,  then you need to have explicit context switching  all the way through.  So if you're low level, I'm making a network request  as being explicitly marked, then anything that calls into that  also needs to be marked up as being an async function.  And although there are ways that we can  mediate between these two styles, it's a bit fiddly.  So there's this huge challenge for the ecosystem.  It's a little bit of a fork in the road.  And it's important to knowledge that, as with any new technology,  there are costs as well.  And we need to talk about them upfront and recognize them  so that we're in a good position to judge  what the trade-offs are.  All right.  So there's this huge upfront cost  in terms of all of the new code that  needs to be written in order to work with async.  So for one thing, all of the low-level networking stuff,  all of the database drivers or making HTTP requests,  or exactly how do you go about making disk IO,  people need to write low-level async libraries  to interact with that because it doesn't exist  in the standard library because it wasn't a thing when  Python was not 3.5.  Not quite true, but pretty much.  What else?  OK, well, it's a different paradigm.  And there's a little bit more to think about as well  as a developer.  There's two different types of function call you've got now.  You've got async function calls, which  is anything that is making IO.  And you've got regular function calls,  which is only allowed to just be doing regular programming-type  stuff.  It's using the CPU, and it's performing  some kind of computation.  One of the other reasons that it's  worthwhile being cautious here as well is,  you might not care about throughput.  So when you're looking at the performance of your web service,  the thing that your users will most care about  is how long does it take once they've  made a request for them to get a page back in front of them?  What is the latency of the request?  And async has an influence on that, but it's more complicated.  And unless you are building a very high volume service,  the throughput that it's able to handle  might not matter that much.  Unless you have several server instances,  why do you care that it could handle high load?  Fine.  So that's the words that could, you know, things to bear in mind,  but the benefits.  Right.  Sometimes the performance really does matter.  And in the cases where it does, it's really, really important  that this should not be a blocker for businesses adopting  Python.  We want to be able to build hugely scalable web services  with Python.  And we want the mega companies who  are developing these flagship services  to be choosing Python right at the start,  building highly successful products with it,  and being able to go to the rest of the world, hey,  look at this awesome thing that company XYZ has built in Python.  And for them to go, yeah, this was a great development  experience for our team.  And we're very happy with the choice that we made.  What else?  Real time, because Async is much more resource efficient,  we're able to hold open lots and lots of network connections  without that having a high impact.  So we can hold open things like WebSockins,  and we could do real time communications,  non-blocking HTTP requests, being  able to perform parallelization within our codes  without that being a heavy weight thing  that branching lots of new threads would be.  The explicit IO is actually also a benefit as well,  but we'll come to that later in the talk.  And one other thing to say here is performance  can mean different things in different contexts.  So being able to build very highly concurrent web services,  well, a flip way round of looking at it, that is,  on very lightly resourced systems, on embedded systems,  these sorts of things will work really, really well.  Or suppose you don't require high throughput  on your site most of the time, but it's only  to get a huge traffic spike.  Your service is much, much more resilient to that.  So it doesn't have to always be about,  this is just about high throughput services all the time.  There are other reasons why it's important.  And there's this other thing.  It's something that has been said lots of times.  People say, oh, Python's slow.  We don't want to use Python for xyz because Python's slow.  Now, yeah, OK, benchmarks, nonsense, nonsense, nonsense.  The tech and power benchmarks are the least awful ones  that are around.  They've got a number of different test cases  that exercise various different bits of do some database stuff,  do reads, writes, whatever.  This particular test case, I think,  is the most representative of web applications  because it does a little bit of database reading.  It does a little bit of template rendering,  and it does a bit of exercising the web stack.  And yes, I've cut some Go results off of the top here.  And yes, I've got lots and lots of results  from several different frameworks off the bottom here.  But the important things to look at is one at the top.  This is Go written against the Go standard library.  The ones in yellow, those are all node based.  And these ones in blue are both Python.  And we've got a single very beefy server there  that on its own is servicing 70,000 requests per second,  which is probably more than you need.  So it's about making sure that there isn't  this blocker to business adoption.  Python is in the same ballpark as node and Go  for almost all intents and purposes  that we in this room would probably need.  So OK, great.  We'd like to start taking advantage of this.  What do we need to do?  The first thing in the stack that we run into is WISCII.  WISCII is the interface that exists between the server,  Gunicorn or UWISCII or something like that,  and the application framework, the web framework.  And WISCII has a couple of very big constraints  for what we'd like to be able to do, one of which  is inherently a thread concurrency interface.  It doesn't have that async definition on there,  so it's not allowed to do any async context switching  inside it.  And it's designed purely for handling HTTP request  and responses, so it's got no easy way  to adapt it for web sockets.  So hello, ASCII.  Along comes Django channels, which originally  was designed in order to keep SEP  to deal with web sockets in Django  by largely keeping separate the asynchronous nature  of handling the web socket connections  from the synchronous nature of the thread-based Django code  base.  And it has gradually evolved under Andrew Godwin's  wonderful guidance, wherever there we go,  into becoming a general purpose application interface,  an alternative to WISCII.  An async alternative to WISCII.  An async alternative to WISCII that also handles web sockets,  that is also able to deal with, and that is also  a more general purpose interface and that is more adaptable.  So this is how the interface for ASCII looks.  As of ASCII 3, which is we're done now.  I mean, not we're done, but we're kind of done for the big stuff,  anyway.  We've got these three variables that we call into the function  with scope, receive send.  Scope is a whole bunch of state information  about the incoming connection in a dictionary  with a bunch of keys in it.  And receive and send are two channels  on which the web application communicates with the web  server.  You use, for example, in the HTTP context,  you'll use the receive channel to do things  like pulling the body of the HTTP request.  You don't want it in the scope because then you'd  have to have the whole body arrive all at once,  so instead you'd like to be able to stream it in in case  you need to do that.  And also for sending out the outgoing HTTP response.  ASCII gives us a lot of nice things.  So obviously we've got the potential for the performance  characteristics of async that we've talked about.  Real-time communication.  So it's not just web sockets.  There's also server-sense events, which  are very similar to web sockets.  But they're over HTTP only.  And they are uni-directional, so just  sending from the server to the clients.  But they can be a nice, simple thing  to use without having to go to the web socket roots.  HTTP long polling.  HTTP's to server push, where when a request is made  to your web server, you've hit the home page.  The web server knows, OK, I haven't got any cookies  from this thing.  It probably is going to need all of these other assets  on the page.  And I don't want it to have to go and do a couple of different  round trips before it gets them.  So I'm going to preemptively start  pushing these assets over the connection.  I'll send back there.  Here's the home page.  And I'm also going to start sending down my cat  gifts at the same time.  Aski also has startup and shutdown events, which gives  a more nicely managed context for running tasks within that  domain than Whiskey has, which doesn't have any kind of way  of communicating, OK, I'm ready to go now.  Or, OK, can you please start shutting things down?  And actually, it's pretty powerful,  because it allows us to do things like build  clock or timer-driven events and know that the events that  we're scheduling, if they're running,  then when the server requests, when we're  requested to shut down, we can wait until those events are  finished and then we can send back to the server, say, OK,  I'm all finished now and we know that we're  going to terminate cleanly, which can potentially  allow us to build really nice task queues and so on  in a much more simple way.  And it's a more adaptable interface, right?  We can potentially extend this into other protocols as well.  So it's here for the long term.  Where's the Aski landscape at the moment?  OK, so we've got several different server  implementations already.  We've got Daphne, the original one.  Hypercorn, somebody, Phil Jones, been working on, and UVaCorn.  It's one that I've spent my time on.  We've also got lots and lots of Aski web frameworks  emerging.  Starlet is the one that I've been working on,  and I'm going to show you some of how that looks a bit different  to Django and why that's interesting in a moment.  Django channels is slightly the odd one out on this list,  because that's async on the front,  but threaded most of the rest of the way through.  And we've also got other stuff that's starting to be developed  in this area as well.  So what I want to do over the next few slides  is take a look at if we're building an async web  framework, what are some of the ways  that we could do things a little bit differently?  And I think a lot of this can feed into some of the work  that we're hoping to start doing on Django  over the coming months.  Now, Starlet, OK, up here, this example  looks like any old standard micro web framework,  but there are a few ways that it's put together  that I think are a little bit interesting.  Now, it's one thing that we're going to see a lot.  It's Aski all the way through.  We use the Aski interface as the primary thing  on which the stack is built all the way right up to the,  when you're working with a view, and then you're  in request response, and you're dealing with requests  and responses, but all the way through every other part  of the system, if you start to dig into what's happening,  it's based on Aski.  And here's a good example of that,  is a response in Starlet itself exposes the Aski interface.  So an instance of a response is a valid web framework.  It's a very small one that just does one thing, but it is.  Interesting.  OK.  Just to get a bit of an idea about how some of the components  look, this isn't the level that you'll be working at normally.  This is using requests and responses  within a raw Aski interface.  Normally, you'd be working within a proper request  response view, but you can see how requests are just  an interface that is instantiated over the Aski  state that you can then do stuff with.  OK.  Fine.  Let's have another look at ways in which we use Aski all  the way through.  The test client in Starlet is built upon requests.  It is requests.  It's requests, but with an adapter class, an adapter class  that instead of making raw network requests,  plugs directly into an A&E into an Aski framework.  And that's great because we can use our test client  to test any Aski web framework, not just Starlet,  but any of the other ones that we saw  were up on the screen earlier, or to test any Aski component  as well.  So here, we're just instantiating a response.  We're making a request out to it using the standard requests  library with all of the standard API and behaviors requests.  The next place, we said we're using it all the way through.  So of course, we're writing Aski middleware as well.  Now, in, say, Django and lots of other web frameworks,  what happens with the HTTP dispatching  is that the first thing that happens, the request comes in,  we create some kind of request instance.  And then we pass our request instance  all the way through a middleware stack that  gets request instances, calls into the next thing in the chain,  and returns response instances.  Why wouldn't we want to do that?  Sounds fine.  Sounds good.  Well, what's nice about using Aski as the middleware interface  is that your middleware implementations  are reusable across, again, any Aski web framework.  They're also independently testable using the same test  client that you use for testing everything else.  It's also important to design things in this way  because if you build your middleware as a request  response interface, what do you do when you come to WebSockets?  We're going to build another different type of interface.  If you're working against the Aski interface,  it's much clearer how to build, for example,  middleware that authenticates both HTTP and WebSocket  requests based on the headers in either.  OK, but as a developer, you don't necessarily  want to be working at that level all the time.  No problem, one of the things that Starlet provides  is a class that you can subclass that, to you,  the end developer provides a request response interface,  but to the outside world provides the Aski interface.  So when the request comes in, it creates a request instance,  sends it off to your dispatch method.  You call into call next, and it does the job  of inspecting any messages coming back down  and marshalling those into response instances.  And that's great because you're working  at the request response level, but again,  you're building reusable middleware  that we can share with the rest of the community  and that when the rest of the community building middleware,  they can share with us.  What else?  Mountable apps, top example here.  If you want to build a file server in Starlet,  that's what you do.  You can run that top example with Daphne, with HyperCorm,  with Uvacorm, and that'll just work.  If you want to put that within your web application  and serve static files within your web application,  you mount that to a particular endpoint.  And again, we're building more and more reusable components.  So a great example of this, talking half an hour ago  about Ariadne GraphQL server.  It has an ASCII interface.  You can just plug it straight in,  and it's not coupled to a particular web framework.  It's about ASCII.  Another example, class-based views.  Similar sorts of thing.  The class-based views in Starlet expose the ASCII interface.  You can do interesting things like it.  That allows you to build out more high-level variations on this.  So if you want to build something that's  like Res Frameworks' view sets or anything else like that.  We'll see it again a bit later on is a point of difference.  It is all the way through in this style of design.  We're using per-component configuration.  So we're not using framework-level settings that  are then action over distance picked up somewhere  inside the web framework.  Not quite sure where and have some kind of effect.  Hard to see as a developer if you want to kind of plum  into what's happening here, where is this getting used.  And we're also not using application-wide settings  where we're plugging all of the configuration directly  into this one single application instance.  So per-component configuration, which again,  helps with better re-use shareable components  across the ecosystem, helps with being able to test components  in isolation, so on.  So what I think is interesting about some of the design  in this is, again, all stuff that I hope,  as we're progressing on Django, can feed into some of the ASCII  work that's going on there.  And it's all about the overall complexity of the stack.  You've got this one single interface style that runs all  the way through.  Very consistent.  You can use a test client with lots of different components.  It's very, very composable.  It's also very performant because we're not  introducing any extra abstractions on top of this  interface style.  Woo-ee!  Limey.  Oh, yeah.  I said that I say that.  We're not in the States, so nobody picks me up for it.  But there we go.  OK.  Async.  Great.  What about the database?  Blimey.  Oh, god, there it is again.  OK.  Django, RRM, SQL Alchemy, are both thread synchronous APIs.  And not just that, but if you go down to the lower levels,  there's something that is very analogous to whiskey in a way  is the Python interface that is used  to separate the database driver from the higher level code  that is working with that driver.  And it's also a thread synchronous interface.  And we've got lots and lots of folks  who've been working in the async space developing async  database drivers.  But we don't have a standard interface onto them.  So it's difficult to start writing  tooling that works together with SQL Lite and Postgres  and MySQL.  So I've recently released a package called Databases,  which aims to address this.  It's not analogous to DBAPI exactly  because it's a slightly different level of interface.  It's aimed to be something that is very developer  facing interface.  You can use it to make raw SQL queries  to any of those async database drivers.  You can also use it to work together with SQL Alchemy Core,  so the table definitions and the query builder,  which is an absolutely stellar tool.  It's not all the way to an ARM, but it's a really productive  level to be working at nonetheless.  It's also great because it allows you to use SQL Alchemy Core.  If you write your table definitions using that,  you then have support for migrations  using their Alembic, which is analogous to Django's  migrations.  And it provides transaction support as well  and dealing with database connections sensibly  and all that sort of stuff for you.  Great.  So there's a low-level answer to what  do we need to do in the async land in order  to address the database.  But there's still a component that's  missing there, which is a fully-fledged ORM.  So I've also released another package,  independent of databases, but built on top of it,  which is a Django-like or the start of a Django-like ORM,  but asynchronous.  And again, because it's built onto the databases,  we've still got the migration support.  It's got a very Django-like API.  It's not all the way there yet, but we've got a bunch of stuff  in, so we've got all of the different filter expressions  and so on.  We've got support for foreign key relationships,  and we've got support for select-related.  We don't have support for many-to-many yet,  or reverse foreign keys, or prefetch-related.  And if you're building an async ORM,  because IO always needs to be explicit,  there are a few things that we need to do a little bit differently.  So for example, in the Django-ORM,  if you've fetched a model instance,  you haven't fetched a relationship on it,  and you access that relationship on it,  it will go off and generate some SQL  and resolve that automatically for you.  Can't do that with async.  You have to either have called select-related on it  in the first place, or explicitly load it and say,  I want to resolve this thing now,  and it will raise an error to you otherwise.  Similar stuff with paging through query sets.  If you don't want to fetch the entire set of results  in a query set all at once, you need to do that explicitly.  In the syntax, it's an async iterator.  So it looks like async for instance in query set.  But it's a bit different to being able to just do that  implicitly in Django.  As well as looking a bit differently,  there are also benefits to this explicit style.  So I think a very large number of people in this room  will have hit the types of cases where maybe you're  working with your own code base, or you're working with a code  base that you've recently come into,  and this view is running really, really slowly.  Why is that?  And you dig into it and dig into it,  and you find that somewhere in the template code,  it's iterating over a query set, and it's  accessing some relationship or some field on there that's not  there, and it's generating SQL queries again, and again,  and again.  Now, in async, in a standard style setup,  you can't run database queries in a template at all.  It stops you from doing that.  It ensures that anything that you're accessing,  you have loaded, and if you try to load something  without explicitly doing that, it will raise you  a great big error.  Similarly, you can't do database lookups in str.  And I've worked with clients who go,  this thing is this bit in the admin  is running incredibly slowly.  Why is that?  It's because when you're displaying your model instance,  you're generating SQL queries.  You don't want to do that.  And having that tighter control, yeah,  it's a bit more to think about, but it's also  this huge benefit as well.  People run into this with Django, and they go, Django slow.  Well, it's not, no, but it's allowing you to not  look after yourself.  Now, this is a bit different, but I've been working with it  for a while, and I really, really like it now.  I really like it.  What else?  Yeah, there's even more to think about.  So you've gone to all this trouble of building potentially  very high concurrency services.  You might want to be precise about how  you think about database connections  and database transactions.  So a really good example of this is if you are building  microservices, you've got a gateway API.  And the job of the gateway API is to request  comes in, make a database access in order to authenticate  the user, great users authenticated,  go and send an HTTP request out to some other service,  wait for the response, send it back to the end user.  And what you don't want to do if you  want to be able to build very high throughput services  like that is hold on to your database connection,  or especially not a database transaction  for the entire duration of that HTTP request.  There's just no need.  You want to acquire your connection, do your work,  let go of it, then go and make your HTTP request,  because that is a really, really slow operation.  You don't want to be hanging on to this valuable system  resource for the whole duration of it.  So for example, databases is designed  to be very liberal with acquiring and releasing  connections to the connection pool  unless you are explicitly within a transaction.  And there's other places to think about as well.  So if we want to have async HTTP requests,  the requests library doesn't give that to us out of the box  yet.  I've just released a package recently,  requests async, which is requests  with a different adapter that makes  asynchronous network requests.  Email, really, you want to make non-blocking SNTP requests.  There's a great library for that already.  Caching, you're interacting with Redis or Memcache.  Again, that's a network operation.  Well, that was a back of the stage there.  Even validation, right?  So you're building a validation library, form validation,  API validation.  If you want to be able to perform any database queries  within that, you're going to need  to be able to make sure that your validation library provides  support for async methods as well.  And stuff like password hashing.  So password hashing is interesting  because it's deliberately designed to run slowly.  And if you run something very, very, very, very slowly  in a single async task, then what happens  is all of that normal, very fast interleaving  between all of your different tasks stops happening.  And this one task is just hogging everything else up.  And these other tasks are getting blocked.  So two different ways you can resolve that.  Either you can make sure that your password hashing is  yielding to the other tasks, lots of the ways through.  Or you can take a simple approach.  You just dispatch it off to a thread.  You say, OK, go and run this thing in a thread.  Don't block the main event loop.  So this is the stack of stuff that I've been working on recently  and trying to lay a really healthy ground work for all  of the async stuff that I think is about to be on the way  at a very great rate.  Let's take a look at how all of this stuff hangs together  if you want to start building full stack web frameworks based  on this.  Don't worry if you can't read everything in the slide here.  It's not so bad, right?  But it's just to give a bit of a flavor  of a few different points of difference  between what we used to in Django and some  of the design style here.  Settings, we're still pulling all of our settings together  in the same place.  We're still using standard 12 factor configuration style.  But we're then using those settings  and pulling them into the components or the resources  that we're using them within so they're less entangled  in their design.  Models kind of similar.  Point here being the async landscape  is really starting to mature.  Type validation, type system is a library that handles  both API validation and form rendering,  kind of similar to REST framework serializers,  but it's trimmed down a bit.  The difference here being our validation libraries  need to be able to support async methods as well.  Star point in Starlet, we call them endpoints,  or in REST framework, including in Django views.  The interesting thing to kind of note here  is it's very, very clear where our database accesses are  happening or network requests.  So we've got some views which are not async.  There's no database accesses that are happening in there.  They don't need to be async.  We can see exactly where stuff's happening with the database,  which is really great.  Routing, pretty similar.  We've talked about per component configuration  and being able to mount ASCII apps.  And pulling it all together, we've got a single app instance  at the top, which ends up just being a bunch of middleware  that you pulled into it, a couple of other default middle  ways that it will always include,  because you pretty much always want them to deal with server  errors or exception handling.  So the middleware and then the routing.  And that's it.  So we've got something that's quite Django-ish.  This is very, very decoupled, low impact approach.  Performs great in terms of throughput,  as great as you could reasonably need.  It's able to support web sockets or service  center vents and various other stuff, background tasks.  We mentioned briefly, and HTTP server, two server push.  And it lets us do things that we just can't do with Django  at the moment.  So building API gateway services that can comfortably  handle tens of thousands of requests per second,  building proxy services, building GraphQL backends  with real time subscription endpoints  that can serve thousands of clients at the same time.  All things that we would really like  to start to be able to push Django into the realm of,  and doing all the legwork for this.  And the other thing that we have is this very, very composed  style.  So we can start right at the bottom here, a raw ASCII  interface.  We can work away gradually up using individual ASCII  components, up to using these tools  in a very micro frameworkish way,  all the way up to using them in the same sort of way  as a full stack web framework.  So what does this mean for Django?  So a few things in tandem.  I suppose it should be two things in tandem, really.  I don't know what a three-way bike's called.  First of all, progressively adding ASCII into the stack.  So Andrew Godwin has what I think is actually  a very achievable proposal for how  we would go about getting some aspects of this into Django,  starting off by adding an ASCII interface onto Django,  but running everything within thread pools beyond that.  Then gradually you iterate, and you build that out  so that the middleware stack is ASCII-based.  And at that point, if you need to be  able to drop into async in a view, then you can.  Your Django components might not necessarily support it,  but if you want to do a little bit,  if you don't mind doing a little bit of extra work,  you've got more power available to you.  And then iterating on from there and looking  at turning some of the components async along the way.  And the other thing is, there's a lot of groundwork  that's being done for this already.  Things like the databases package  could potentially be used in an async Django R&M.  Maybe we don't need an async Django R&M.  Maybe a one that just dispatches into the thread pool  will work equally well.  But we've got a lot of groundwork, ladies, the point.  And at the same time as all of that,  pushing really hard to keep maturing the async landscape.  And in particular, pushing really hard to do that in a way,  that is something that the whole community can share.  So getting behind ASCII is a standard  because it allows us to work together really efficiently.  And I think that the Python community has a really important  message here, which is, I don't think there's anything out there  that beats Python for productivity.  It's absolutely awesome.  An async makes it competitive with the other big players  in its zone with Node and with Go  and brings support for real-time protocols  and loads of other stuff.  To me, if we can reach a great level of functionality  with this stuff, Python's really hitting the sweet spot there.  Yeah, I think it's a really simple message.  And I think it's really powerful.  And I think it's one that we need to start communicating  to the rest of the world.  OK, quickly, all of the Django Fellows work  only ever happens because it's sponsored.  My time only happens because it's sponsored.  I think that the ask that we have when we say, hey,  I want to work for your companies full time for 50 euros a month  or 150 euros a month, if your businesses can see  the potential of the impact that this sort of work  plus the work on REST framework, the 3.10 that's  coming out soon and the open API support that's  going to be in there.  And your companies are going to benefit from these things  over the next year, the next two years, the next five years,  and into the long term.  I think the investment is just an absolute no-brainer.  So whether that is through the DSF,  through REST framework sponsorships,  whether it's through community events like this,  or Django Girls, or local events in your something, anything,  it's so important.  I also think in order for us to take things to the next level  that we're going to need to find other monetization  strategies as well and ones that benefit the community.  And I think for this to happen with frameworks like Django  and like Rails, they need to be truly succeeding  in the product space as well, providing great products that  are the fastest possible on-ramp for developers  to get started with Django or with Rails.  Give developers the ability to start mocking out your API,  your API documentation, or give developers  the ability to start sketching out their admin  and start putting data in there.  Yes, you might be limited to a certain number of rows in there.  We've got some constraints that we're not going to do it.  We're only going to be with you for the prototyping stage.  But the goal of our product is to lose you as a user.  We want you to end up taking complete control  of the framework at the end of the day.  We'll have a great big button there.  Great, let's get started on my product.  I'm happy with the prototyping stage.  Our front-end team's been working against it.  And at the end of the day, that is the most efficient thing  for us to be doing for our developers  at a certain point in the maturity of these frameworks.  And if we can do it, if we can nail it,  we could have a company that just works on Django,  or Starlet, or the Python ecosystem full-time.  And the more we can succeed with this,  the more developers we can bring on board.  I was chatting over the slides with Carlton late last night.  And we went over these last three slides.  And summed them up to him at the end of the set.  First one is, give us your money.  Next one is, here's how we make money.  And the last one is, it's not about the money.  And it's not.  It's really not.  The money's a tool.  Why are we here?  Why do we care about open source?  Why have we all come together?  Our guiding light in all of this,  our thing that keeps us focused, has  to be impact on society, betterment of society.  As is great quote by an American trappist monk,  Thomas Moore, which is, if we do not do this,  we may find ourselves climbing to the very top  of the ladder of success, only to find  that the ladder has been leaning against the wrong wall.  And if we focus on our values, rather than allowing  them the raw power of the market to set the direction  and the flow, then we change the rules of the game.  And you help evolve the very currency with which the market  actually trades in.  Oh, hang on a minute.  I didn't show you my lovely bit.  That was the slide for that one.  You know, it's all about taking our individual creative  spark, our individual instinctive sense of empathy,  and working together as a community, as a whole,  and as a society.  Thank you.  Thank you.
DjangoCon_2019_-_Sketching_out_a_Django_redesign_0_419.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_420_1751.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_1752_3983.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_3984_9383.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_9384_11231.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_11232_12179.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_12180_15899.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_15900_19655.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_19656_22463.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_22464_22643.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_22644_23219.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_23220_23447.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_23448_23987.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_23988_25487.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_25488_26255.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_26256_27179.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_27180_30803.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_30804_31919.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_31920_32795.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_32796_33971.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_33972_34103.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_34104_34271.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_34272_34859.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_34860_36623.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_36624_39299.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_39300_40631.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_40632_41915.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_41916_42623.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_42624_44195.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_44196_45131.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_45132_45671.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_45672_46439.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_46440_46727.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_46728_46859.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_46860_47003.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_47004_47675.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_47676_48587.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_48588_49127.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_49128_49895.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_49896_50471.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_50472_51395.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_51396_52427.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_52428_55391.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_55392_55895.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_55896_56963.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_56964_57647.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_57648_60383.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_60384_61127.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_61128_61367.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_61368_61499.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_61500_61871.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_61872_62027.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_62028_62147.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_62148_62435.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_62436_62891.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_62892_63755.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_63756_63971.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_63972_64691.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_64692_66359.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_66360_66803.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_66804_66947.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_66948_67151.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_67152_67319.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_67320_70271.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_70272_70511.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_70512_71843.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_71844_73607.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_73608_79247.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_79248_80135.png DjangoCon_2019_-_Sketching_out_a_Django_redesign_80136_80226.png