Lessons learned with asyncio ("Look ma, I wrote a distributed hash table!")
Tollervey, Nicholas
Nicholas Tollervey - Lessons learned with asyncio ("Look ma, I wrote a distributed hash table!") This talk introduces the asyncio module. I'll cover what it's for, how it works and describe how I used it to write a real-world networked application (a distributed hash table). We'll explore the event loop, coroutines, futures and networking with examples from my code. This won't be an exhaustive exposition. Rather, attendees will grasp enough of asyncio to continue with their own studies. By the end of this introductory talk I hope attendees will want to learn more about asyncio and perhaps give it a try in their own projects.
 Thank you very much. Thank you. Thank you. So, yes, my name is Nicholas. I'm a freelance Python developer from the UK. And this is an introductory talk, really, about async.io through the medium of distributed hash tables. So async.io, as I'm sure you all know, arrived in Python 3.4. And my only expectation of you is that you know a little bit of Python. So what we'll do is we'll explore some of the course of the course. concepts of async.io. And I'll tell you the story of how I used async.io to create a fun little personal project which was to build a distributed hash table which Holger very usefully referenced in his keynote this morning, which is perhaps why there's rather a lot of you in this room. This isn't an exhaustive exposition of async.io. There's an awful lot I've had to leave out and it simplifies a lot as well. But my aim is to arm you with just enough information so that you can continue exploring the module and the concept of async.io. It's also a personal pedagogical exercise. If I can explain async.io in a simple sort of a way, it demonstrates my own clarity of thought about async.io and understanding about async.io. As you can hear, I'm talking very quickly because I have rather a lot of information to give to you. So yes, perhaps like Eric Gumbie, your brain will hurt by the end of this talk. So the first thing I want to do is to give you a little bit of information about what you can do. So I'm going to start with the first thing that needs to be answered is what does async.io do? Well, the Python documents are very clear about this. They state that this module provides infrastructure for writing single-threaded concurrent code using co-routines multiplexing IO access over sockets and other resources, running network clients and servers and other related primitives. You got all that? I've actually got that written down. I didn't do that from memory. Now, while I understand all the terminology from the documentation, it doesn't give me a sense or a feel of how I might use this module. And such documentation can make the module appear a little bit intimidating and sort of the realm of esoteric leaked Uber hackers like Trinity who's so leachy talks in courier fixed width. So you can figure out what Neo talks in. Anyway, what does async.io do? Well, we can do a lot better than that. We can keep the answer simple. Stupid. What does async.io do? As Trinity says, it lets you write code that concurrently handles asynchronous network based I.O. So let's be clear about what I mean by those terms. So concurrency is when several things appear to happen at once. Asynchronous literally means not synchronized. There's no way to tell when something may happen. The network, of course, as you know, is a medium for communicating with another device usually via the internet. I.O. is, as I'm sure you all know, input output when a program communicates with the outside world. So, the problem clearly stated is that messages arrive and depart via the network at unpredictable times and async.io lets you deal with such interactions simultaneously. So given this simple purpose, I want to place async.io into a practical context. So let's talk about distributed hash tables. So, again, let's start with the obvious question, what is a distributed hash table? Well, I'm assuming you know what a hash table is because it's more or less the same thing as a dict in Python. We are at your Python after all, and I think it's reasonable that I can expect you to all understand what a dict is. It's a simple key value data store. A distributed hash table is distributed because the whole is built from several independent yet related parts. So it's a little bit like this abstract encyclopedia. The whole encyclopedia is made up of volumes that are independent yet related to each other. And in our case, the distributed hash table is a structure that consists of many independent nodes that collaborate with each other over the network. DHT is also decentralized. So no node is more important than any of the others. So there are no client-serve relationship in the DHT. It's a loose peer-to-peer network of nodes. So, a distributed hash table, a DHT is a peer-to-peer key value data store. Why would I want to implement one of these? Well, it's a really interesting programming problem with fascinating properties. As I mentioned, there's no single point of failure. The DHT algorithm that I use, which is called Cademlia, efficiently scales to a huge number of nodes. It has good handling of fluid network membership as nodes leave and join the network. It's a solid foundation for more complex services, such as the ones that Holger referenced this morning. It's tested in the real world, so BitTorrent uses the Cademlia algorithm, FreeNet does as well. And obviously, if you're like Holger, it's an example of decentralized platforms which are fascinating on more than just a technical level, on a political level as well, perhaps. So, guess what? Distributed hash table nodes have to concurrently handle lots of asynchronous network-based I.O., which is the sweet spot for Async I.O. So, we have a context. So, how does Async I.O. Or how do I use Async I.O. to make all this work? So, let's introduce some core concepts. The first one being the event loop. And quite simply, this is some code that just keeps looping. Each iteration of the loop basically does two things. First thing is that it pulls for I.O. events that occurred during the time it took to complete the previous iteration of the loop. And the other thing that it does is that it runs any callbacks that need to be run during this current iteration of the loop. The loop also carries out various housekeeping needed for callbacks that have yet to be executed. But that's something that we can ignore. So, it's important, again, this is sort of an introductory talk, to define what polling and callbacks are. So, polling is discovering the states of something external to the program. And in Async I.O. this is network-related I.O. events. And a callback is some code that's to be executed when some event has occurred that's being detected via polling. And this metaphor, obviously the kids are polling. Are we there yet? Are we there yet? Are we there yet? And the mother is saying, I'll let you know when we get there. And this creates a sort of a callback in that she is promising to do something when some condition is met. So, that's why they get to the end of their journey and they get to Grandma's house. So, it's important to note that polling takes place once during each iteration of the loop. I.O. events are discovered by polling to determine which callbacks to execute during the current iteration of the loop. All pending callbacks executed one after the other. As it says so in PEPPY 15. And the loop can't continue while this is happening. It is blocked. So, the next iteration cannot start until all the sequentially executed callbacks finish in some sense. So, you're probably asking there's something wrong here. This doesn't sound very concurrent. Well, unfortunately, concurrency is a hard problem and there's actually more than one way to do it. So, it's worth looking at. So, it's worth taking some time to examine why async.io works in the way that it does. So, in the traditional threading model, several problems can happen. Task A reads the record, task B reads the record, both A and B change the retrieved data in different ways. Task B writes its changes, then task A writes its changes. What's the problem here? Well, task A has overwritten the record containing task B's changes. So, we have loss of data, which is the problem that we can avoid. So, why not wait until one task is finished before continuing? So, first do A, then B, followed by C, and so on. This is easy to understand and sort of deterministic. But what happens if A needs to wait for something such as a call to another machine on the network? Well, the program has to wait until A's network call completes. And in that situation, we can't get on with other stuff because we're still hanging around for A. So, given the situation in this slide, the program is described as blocked, and this is unacceptable if we're writing code that needs to react quickly to network-based events, which is precisely the sort of program that A SyncIDo has been designed to help it. And so, you're probably asking yourself, well, why not just get on with tasks B and C while we wait for the result from the network call for A? Well, if you're thinking that, then actually, you've described quite succinctly what A SyncIO does. So, welcome to the most important slide of the talk. I hope you're all paying attention. So, A SyncIO is event-driven. And this means that network-based I.O. is non-blocking. So, how does this work? Well, the program does not wait for a reply from network calls before continuing. Programmers, us, define call back to be run when the result of the network call is known. And in the meantime, the program continues to poll for and respond to other network-related I.O. events. Callbacks execute drawing the iteration of the event loop immediately after the expected network I.O. event is detected. Confused? Well, you shouldn't be, because actually, this is how we as humans think about concurrency. So, in the real world, we make plans all the time. The washing machine finishes is an expected event. And hanging the clothes out to dry is a callback for when this expected event happens. So, how hard can this be? Says the stock photo dude with the washing basket. Also, we multitask in the same way A SyncIO does. We skip between the things we need to do while we wait for other things to happen. We know we will have time to squeeze the orange juice while the toast and the eggs are cooking, while we make breakfast. We don't just stick some bridge in the microwave. Put it on for five minutes and then watch it. Well, I don't. You probably get on with other things. This is exactly the sort of thing that I'm talking about. We multitask. There's only one of us. There's only one A SyncIO event loop, and it's this is how we react to the world around us. The Microsoft, the Microsoft, the microwave it's a long night last night. The microwave goes ping and that is the expected event and then I take the porridge out and eat it. So a SyncIO avoids potentially confusing and complicated threaded concurrency while retaining the benefits of strictly sequential code. This is the fundamental advantage of a SyncIO. We plan ahead for expected events by defining callbacks to be called when such events eventually occur. In the meantime, we sequentially handle the callbacks related to other events that may have happened in the interming time. So questions you're probably asking yourself are how are asynchronous concurrent tasks created? How do such tasks pause while waiting for non-blocking network-based IO? And how are callbacks defined to handle the eventual results? To answer these questions, you need to understand code routines, futures, and tasks. So I'm going to attempt to explain code routines in about three slides. And a code routine essentially is an object representing an activity that eventually completes. Or it's a decorated function that returns such an object. And the important thing to know about these code routines are that they may be suspended, you use yield from syntax. And when a code routine is suspended, this allows the event loop to get on with the other things. Code routines are sorts of generators. So they lazily generate results. So calling a code routine doesn't actually start its execution. They yield from other objects. And when the yielded from object has a result, the code routine continues from the yield from statement that suspended it. And this is called re-entry. And at the end of the chain of yield froms is an object that returns the result or raises an exception. Rather than yielding from some other code routine. So this, oh dear, look at that. I think you can see it all. Yeah, that's just Chrome being a bit daft. So this is a decorated code routine method that handles an incoming HTTP request as part of my DHT. Upstream, something is yielding from the code routine created by this function in order to do something with the response that it generates. And this code will pause by yielding from the code routine created by the payload.read call. Which is a method that reads the raw data posted as part of the request. You can see that just after the try. Okay? When all that data has arrived, the code pauses again while waiting for the code routine created by the self.process data method itself. Waiting on other things such as perhaps a call to the database. And when the task encapsulated by this code routine is complete, i.e. the response, the upstream code routine gets the return result and resumes execution from where it yielded from in its code. So we kind of know how asynchronous activity happens, but how do we handle the results? How do I handle the result of a code routine? What about callbacks? Well, for this you need to understand futures and tasks. Which I'll try and explain in just two slides. So a future, if you've done twisted this will all be very familiar to you. A future represents the result that may not yet be available yet. Callback functions are added to a sort of a to-do list to be executed when the result is known. And a task is simply a future that wraps a code routine and the resulting object is realized when the code routine completes. We say the future has resolved, which sounds all very dot-two. So here's some example code, which I hope makes this clear. So the first thing I do is I create a callback function, handle resolved tasks, and the only thing that it does is record, it logs the result. The next thing I do is I create a task associated with a certain code routine that will pause. And then I add to that task the callback that I defined above. And the task object is automatically resolved when the code routine that's associated with it completes. And at the end I execute the code, because remember code routines are generated by setting up and running the event loop itself. So here's another perspective on this, a little bit mind-bending. So my buddy Terry Jones likes to point out that we're used to working with first-class functions in Python. We pass them in functions and we return them as values. We just might not know what the result is yet, but we can start to add callbacks to them. So let's do a quick recap before we come to the sort of end of the talk. I see him waving the five-minute flag. So an event loop is looping code that pulls for Io and manages event handling callback. So I can do that. And then I can do the same thing with the first-class function. So I can do that. Io and manages event handling callbacks. Coroutines an object that represents activity that eventually completes, or is a decorated function that returns such an object. Future represents a result that may not be available yet. And the associated callbacks are executed when it resolves. Like I said the future resolves in a kind of mysterious galafrey and sort of a way. And tasks are boiler-paid future classes that wrap coroutines. And these are realised in the same way that we do code-routine callbacks. So a given-all theory is very quickly look at how ASENC Io works in practice with my DHT project. For us to be able to do that, I need to very quickly explain how a DHT works. So as I mentioned before, a DHT is made with nodes. And the classic way to visualise this is with a clock face. And you'll see why in a moment. Each node has a unique ID that is within a set of all possible values of a certain hashing function. You all know what hashing functions are because you're all in Holders' Excellent Talk this morning. Okay? In my case, with my DHT, I use SHA 512. The ID's value indicates the node's position in the clock face. And in this way we can tell where the node is located in the abstract network, as it were. So we can tell who is close to who and how far away nodes are from each other. So there is some sort of notion of distance in the DHT. And data is a key value pair. And the key is turned into a hash, again using the same hashing algorithm, SHA 512 in this case. And the value is stored at nodes whose IDs are close to the hash of the key. So this is similar to understanding where to look things up in a multi-volume encyclopedia, going back to the example at the beginning. Articles are words, or keys in our case, and associated definitions are values that are stored in volumes that cover some alphabetical range within the global encyclopedia space, as it were. So ARDVARK belongs under A, whereas Zebra belongs under Z. Okay? So how do nodes know where to look? Well, each node maintains something called a routing table that tracks the state of its peers. And each interaction with its peers results in exchange of information about the nodes that it's been talking to. And that's how the routing table is populated initially and kept up to date, because nodes keep bumping into each other like ants in an ants nest. The routing table splits the clock face of nodes into buckets. Now, buckets contain the same number of peers, but buckets cover a smaller range, closer to the local node they are. Therefore, the local node knows more closer nodes. As you can see, me, I read about half past one, and the nodes that I know are in the different buckets, and they are the blue squares. Okay? Each node has some very simple rules. They behave according to some very simple rules. It doesn't really matter what those rules are, I'm just putting a few up there to illustrate what I mean. But the important thing about a hash table is that you need to be able to put values and you need to be able to get values. You need to get and set. And each one of those fundamental actions requires a lookup, okay? So that you know which nodes you want to interact with. And all these interactions are asynchronous, and lookups are also parallel concurrent, because you can ask several peers at the same time about the information that you need. So, how does the lookup work? A recursive lookup is the 6° separation gain, which always features Kevin Bacon if you're talking about how they would act. So, say I want to put a value with a key whose hash is in a position at 6 o'clock, okay? There's the target at 6 o'clock. And the first thing I do is I ask the nodes in my routing table that are closest to that hash and they reply with nodes from their routing table that are closer to the peers. And this keeps going recursively until I find nodes that I actually... that are actually close to the target key until I can't find any nodes that are close to the target key. So, get and set requires a lookup. And how is this handled in the realm of a syncIO? Well, a lookup is a future because it's something whose result we can't yet know until we finish looking up. We need to interrogate all our peers on the network, okay? And the state of the lookup, the progress in finding nodes that are close to the target is held within the lookup instance. And the lookup resolves because it's a type of future when the result is known. And the result is either going to be a value, or not a well-found exception in the case of a get operation, or it's going to be a little bit of the 20 closest known nodes in the DHT in the case of a put. And what I will do then is contact each of those 20 nodes and say store this information for me please. So what about networking? How does a syncIO handle this networking protocols? How do nodes in the DHT handle down the wire aspect of input output? So core concepts 5, 6 and 7 are streams, transports and protocols. Streams are high level abstractions that allow you to send and receive traffic down the network, or from the network using reader and writer objects. Transports are provided by a syncIO to handle low level networking activities such as TCP and UDP. And they handle the low level IO layer and the buffering and the eventually sort these out for you. So you shouldn't really have to interact with them. And protocols handle network protocols at the application layer. So for example HTTP or net string and so on and so forth. They're also a lot of fun. So streams are flows of data that can be read from or written to and they are built upon the transports and protocols. So what is a transport? A transport is concerned about how stuff moves over the network. Whereas a protocol works out what to do with the stuff that arrives from the network. It turns out it works out how to turn the raw bytes into something meaningful such as a net string message or an HTTP call. And really you only need to work with streams and perhaps protocols if you're doing something a bit fun. So my final thoughts with my DHT I've managed to get 100% unit test coverage and this is because a syncIO is part of Python and testing is normal. I'm using the built-in unit test library and mock. And so how you organize your code is a key factor for this. But this is in contrast with for example Twisty which has its own test drive and its own unit test class doesn't work quite the same way as the one in the standard library which can be a bit confusing. My DHT implementation is small and simple. So there are only 871 lines of code as of this morning in the DHT itself and this is because they syncIO and makes it easy. I've got about one minute left. It makes it easy to think about concurrent problems. So I believe the abstractions make it easy to write very simple code that's short and comprehensible. And finally I need to mention the difference between IO and CPU bound. Don't use asyncIO if you need to do something with lots of CPU overhead because of course that's going to block the event loop. So if you have lots of networking however that's the sweet spot. So the future in all kind of changes for better in Python 3.5 coroutines with the async and await syntax which Guido mentioned yesterday. His favorite feature so it must be good. So if you remember here's the coroutine that I showed you earlier on. Simply all I've done is taken out the yield from and put the correct keywords in Python 3.5. These are proper native coroutines and they're not to be actually confused with generators and it works slightly differently. The new async syntax outside the code block defy the coroutine not some internal yield from so it actually makes it easier to read in my opinion. And also the new features it comes with an await context manager and iteration protocols as well so you can do some really cool things though that's not shown here. So PEP 492 is Guido's favorite feature. Go read that. I've not tested this code as I put it for illustrative purposes only. That's it. Finished. Going to yellow. Smile. APPLAUSE The DHT project is called the drugulous. That's the GitHub account. Any questions? Does anybody have a short question? A short question like my DHT code. That's short. No questions at all. OK. Thank you very much.
 Thank you very much.  Thank you.  Thank you.  So, yes, my name is Nicholas.  I'm a freelance Python developer from the UK.  And this is an introductory talk, really,  about async.io through the  medium of distributed hash tables.  So async.io, as I'm sure you all know,  arrived in Python 3.4.  And my only expectation of you  is that you know a little bit of Python.  So what we'll do is we'll explore some of the  course of the course.  concepts of async.io.  And I'll tell you the story of how I used async.io  to create a fun little personal project  which was to build a distributed hash table  which Holger very  usefully referenced in his keynote this morning,  which is perhaps why there's rather a lot of you in this room.  This isn't an exhaustive  exposition of async.io.  There's an awful lot I've had to leave out  and it simplifies a lot as well.  But my aim is to arm you with just enough information  so that you can continue exploring the module  and the concept of async.io.  It's also a personal pedagogical exercise.  If I can explain async.io  in a simple sort of a way,  it demonstrates my own clarity of thought  about async.io and understanding about async.io.  As you can hear,  I'm talking very quickly because I have rather a lot of information  to give to you.  So yes, perhaps like Eric Gumbie,  your brain will hurt by the end of this talk.  So the first thing I want to do  is to give you a little bit of information  about what you can do.  So I'm going to start with  the first thing that needs to be answered  is what does async.io do?  Well, the Python documents are very clear about this.  They state that this module provides  infrastructure for writing single-threaded  concurrent code using co-routines  multiplexing IO access over sockets  and other resources, running  network clients and servers and other related primitives.  You got all that?  I've actually got that written down. I didn't do that from memory.  Now, while I understand  all the terminology  from the documentation,  it doesn't give me a sense or a feel  of how I might use this module.  And such documentation  can make the module appear a little bit intimidating  and sort of the realm  of esoteric leaked Uber hackers like Trinity  who's so leachy talks in courier fixed width.  So you can figure out what Neo talks in.  Anyway,  what does async.io do?  Well, we can do a lot better than that.  We can keep the answer simple.  Stupid. What does async.io do?  As Trinity says, it lets you write code  that concurrently handles  asynchronous network based I.O.  So let's be clear  about what I mean by those terms.  So concurrency is when  several things appear to happen at once.  Asynchronous  literally means not  synchronized. There's no way to tell  when something may happen.  The network, of course, as you know,  is a medium for communicating with  another device usually via the internet.  I.O. is, as I'm sure you all know,  input output  when a program communicates with the outside world.  So, the problem clearly stated  is that messages  arrive and depart via the network  at unpredictable times  and async.io lets you deal with such  interactions simultaneously.  So given this simple purpose,  I want to place async.io  into a practical context.  So let's talk about distributed hash tables.  So, again,  let's start with the obvious question,  what is a distributed hash table?  Well, I'm assuming you know what a hash table  is because it's more or less the same  thing as a dict in Python.  We are at your Python after all,  and I think it's reasonable that I can expect you  to all understand what a dict is.  It's a simple key value data store.  A distributed hash table is distributed  because the whole is built  from several independent yet related  parts. So it's a little bit like  this abstract encyclopedia.  The whole encyclopedia is made up of volumes  that are independent yet related  to each other.  And in our case, the distributed hash table  is a structure that consists  of many independent nodes  that collaborate  with each other over the network.  DHT is also decentralized.  So no node is more  important than any of the others.  So there are no client-serve relationship  in the DHT. It's a loose peer-to-peer  network of nodes.  So, a distributed hash table,  a DHT is a peer-to-peer  key value data store.  Why would I want to implement one of these?  Well, it's a really interesting programming  problem with fascinating properties.  As I mentioned, there's no single  point of failure. The DHT algorithm  that I use, which is called Cademlia,  efficiently scales to a huge number  of nodes. It has good handling  of fluid network membership  as nodes leave and join the network.  It's a solid foundation  for more complex services, such as the ones  that Holger referenced this morning.  It's tested in the real world, so BitTorrent  uses the Cademlia algorithm, FreeNet does as well.  And  obviously, if you're like Holger,  it's an example of decentralized platforms  which are fascinating  on more than just a technical level, on a political level as well, perhaps.  So, guess what?  Distributed hash table nodes  have to concurrently handle lots of asynchronous  network-based I.O., which is the sweet spot  for Async I.O.  So, we have a context.  So, how does Async I.O.  Or how do I use Async I.O. to make all this work?  So, let's introduce  some core concepts. The first one being  the event loop.  And quite simply, this is some code that just keeps looping.  Each iteration of the loop  basically does two things.  First thing is that it pulls for I.O. events  that occurred during the time it took  to complete the previous iteration  of the loop.  And the other thing that it does is that it runs  any callbacks that need  to be run during this current  iteration of the loop.  The loop also carries out various housekeeping  needed for callbacks that have yet to be executed.  But that's something  that we can ignore. So, it's important,  again, this is sort of an introductory talk,  to define what polling  and callbacks are.  So,  polling is discovering the states of something  external to the program.  And in Async I.O.  this is network-related I.O. events.  And a callback is  some code that's to be executed when some  event has occurred that's  being detected via polling.  And this metaphor, obviously  the kids are polling. Are we there yet?  Are we there yet? Are we there yet?  And the mother is saying, I'll let you know  when we get there. And this creates a sort of a callback  in that she is promising to do  something when some condition is met.  So, that's why they get to the end of their journey  and they get to Grandma's house.  So,  it's important to note that polling takes  place once during each iteration  of the loop.  I.O. events  are discovered by  polling to determine which callbacks to  execute during the current  iteration of the loop.  All pending callbacks  executed one after the other.  As it says so  in PEPPY 15.  And the loop can't continue  while this is happening. It is blocked.  So, the next iteration  cannot start until all the  sequentially executed callbacks  finish in some  sense. So, you're probably asking  there's something wrong here. This doesn't sound very  concurrent.  Well, unfortunately, concurrency is a hard  problem and there's actually  more than one way to do it. So, it's worth looking  at.  So, it's worth taking some time to examine  why async.io works in the way that it does.  So, in the traditional  threading model, several problems can  happen.  Task A reads the record, task B  reads the record, both A and B change the  retrieved data in different ways.  Task B writes its changes, then task A  writes its changes. What's the problem here?  Well, task A has overwritten  the record containing task B's  changes. So, we have loss of data, which is  the problem that we can avoid.  So, why not wait until  one task is finished  before continuing?  So, first do A, then B,  followed by C, and so on.  This is easy to understand and sort of  deterministic. But what happens  if A needs to wait for something such as  a call to another machine on the network?  Well, the program has to wait until  A's network call completes.  And in that situation,  we can't get on with other stuff because  we're still hanging around for A.  So, given the situation in this slide, the  program is described as blocked, and this is  unacceptable if we're writing code that needs  to react quickly to network-based  events, which is precisely the sort of  program that A SyncIDo has  been designed to help it.  And so, you're probably asking yourself,  well, why not just get on with tasks B  and C while we wait for  the result from the network call for A?  Well, if you're thinking that, then  actually, you've  described quite succinctly what  A SyncIO does.  So, welcome to the most important slide of the talk.  I hope you're all paying attention.  So, A SyncIO is event-driven.  And this means that  network-based I.O. is non-blocking.  So, how does this work?  Well, the program does not wait for a reply  from network calls before continuing.  Programmers,  us, define call back to be run  when the result of the network call is known.  And in the meantime, the program continues  to poll for and respond to other  network-related I.O. events.  Callbacks execute  drawing the iteration of the event loop  immediately after the expected network  I.O. event is detected.  Confused?  Well, you shouldn't be, because  actually, this is how we as humans  think about concurrency.  So, in the real world, we make plans  all the time. The washing machine  finishes is an expected event.  And hanging the clothes out to dry  is a callback for when this expected event  happens. So, how hard  can this be? Says the stock photo  dude with the washing basket.  Also,  we multitask in the same way  A SyncIO does.  We skip between the things we need to do  while we wait for other things to happen.  We know we will have time to squeeze  the orange juice  while the toast and the eggs are cooking, while  we make breakfast. We don't just stick  some bridge in the microwave.  Put it on for five minutes and then watch it.  Well,  I don't. You probably get on with other things.  This is exactly the sort of thing that I'm  talking about. We multitask. There's only one  of us. There's only one A SyncIO  event loop, and it's  this is how we react to the world  around us. The Microsoft, the  Microsoft, the microwave  it's a long night last night. The microwave  goes ping and that is the expected event  and then I take the porridge out and eat it.  So a SyncIO avoids  potentially confusing and complicated  threaded concurrency while retaining the  benefits of strictly sequential  code.  This is the fundamental advantage of a SyncIO.  We plan ahead for expected events  by defining callbacks to be called  when such events eventually occur.  In the meantime, we sequentially handle  the callbacks related to other events that may  have happened in the interming time.  So questions you're probably  asking yourself are how are asynchronous  concurrent tasks created?  How do such tasks pause while  waiting for non-blocking network-based IO?  And how are callbacks defined to handle  the eventual results?  To answer these questions, you need to  understand code routines, futures, and  tasks. So I'm going to  attempt to explain code routines  in about three slides.  And a code routine essentially  is an object representing  an activity that eventually  completes.  Or it's a decorated function that returns  such an object.  And the important thing to know about  these code routines are that they may be  suspended, you use yield from  syntax. And when  a code routine is suspended, this  allows the event loop to get on with the  other things.  Code routines are sorts of generators.  So they lazily generate results.  So calling a code routine doesn't actually  start its execution.  They yield from other  objects. And when the yielded from  object has a result, the code routine  continues from the yield from  statement that suspended it.  And this is called re-entry.  And at the end of the chain  of yield froms is an object that  returns the result or raises an exception.  Rather than yielding from some other  code routine.  So this, oh dear, look at that.  I think you can see it all.  Yeah, that's just Chrome being a bit  daft.  So this is a decorated code routine  method that handles an incoming HTTP  request as part of my DHT.  Upstream, something is yielding  from the code routine created by this  function in order to do something with the response  that it generates.  And this code will pause by yielding  from the code routine created by the  payload.read call.  Which is a method that reads  the raw data posted as part of the request.  You can see that just after the try.  Okay?  When all that data has arrived, the code  pauses again while waiting for the code  routine created by the self.process  data method itself.  Waiting on other things such as perhaps a call  to the database.  And when the task encapsulated by this  code routine is complete, i.e.  the response, the upstream  code routine gets the return result  and resumes execution from where it  yielded from in its  code.  So we kind of  know how asynchronous activity happens, but  how do we handle the  results? How do I handle the result of a  code routine? What about callbacks?  Well, for this you need to understand futures and tasks.  Which I'll try and explain in just two slides.  So a future, if you've done twisted  this will all be very familiar to you.  A future  represents the result that may  not yet be available yet.  Callback functions are added  to a sort of a to-do list to be executed  when the result is known.  And a task is simply  a future that wraps a code  routine and the resulting object  is realized  when  the code routine completes.  We say the future has resolved,  which sounds all very  dot-two.  So here's some example code, which I hope  makes this clear. So the first thing I do is  I create a callback function, handle  resolved tasks, and the only thing that it does  is record, it logs the result.  The next thing I do is I create  a task associated  with a certain code routine that will  pause.  And then I add to that  task the  callback that I  defined above.  And the task object is automatically resolved  when the code routine that's associated with it  completes. And at the end  I execute the code, because remember  code routines are generated by setting up  and running the event loop itself.  So here's another perspective  on this, a little bit mind-bending.  So my buddy Terry Jones likes to point out  that we're used to working with first-class  functions in Python. We pass them in  functions and we return them as values.  We just might not know what the result  is yet, but we can start to add  callbacks to them.  So let's do a quick recap  before we come to the sort of end  of the talk.  I see him waving the five-minute flag.  So an event loop is looping  code that pulls for Io and manages  event handling callback.  So I can do that.  And then I can do the same thing  with the first-class function.  So I can do that.  Io and manages event handling callbacks.  Coroutines  an object that represents activity that  eventually completes, or is a decorated  function that returns such an object.  Future represents  a result that may not be available  yet. And the associated callbacks  are executed when it resolves. Like I said  the future resolves in a kind of mysterious  galafrey and sort of a way.  And tasks are boiler-paid  future classes that  wrap coroutines. And these are realised  in the same way that we do  code-routine callbacks.  So a given-all theory  is very quickly look at how ASENC Io  works in practice with my DHT project.  For us to be able to do that, I need to  very quickly explain how a DHT works.  So  as I mentioned before, a DHT is made  with nodes. And the classic way to visualise  this is with a clock face. And you'll see  why in a moment.  Each node has a unique ID  that is within a set of all possible values  of a certain hashing function.  You all know what hashing functions are because  you're all in Holders' Excellent Talk this morning.  Okay?  In my case, with my DHT, I use SHA 512.  The ID's value  indicates the node's position  in the clock face. And in this way  we can tell where the node is located  in the abstract network, as it were.  So we can tell who is close to who  and how far away nodes are from each other.  So there is some sort of notion of distance  in the DHT.  And data is a key value pair.  And the key  is turned into a hash, again using the same  hashing algorithm, SHA 512 in this case.  And the value is stored  at nodes whose IDs are  close to the hash of the key.  So  this is similar to understanding where to look  things up in a multi-volume encyclopedia, going  back to the example at the beginning.  Articles are words, or keys  in our case, and associated  definitions are values  that are stored in volumes that cover  some alphabetical range  within the global encyclopedia space,  as it were. So  ARDVARK belongs under A,  whereas Zebra belongs under Z.  Okay? So how do nodes  know where to look? Well, each node maintains  something called a routing table  that tracks the state of its peers.  And each interaction with  its peers results in exchange  of information about the nodes  that it's been talking to. And that's how  the routing table is populated initially  and kept up to date, because nodes keep  bumping into each other like ants in an ants nest.  The routing table  splits the clock face of nodes  into buckets. Now, buckets  contain the same number of peers,  but buckets cover  a smaller range, closer  to the local node they are. Therefore, the local  node knows more closer  nodes. As you can see, me,  I read about half past one,  and the nodes that I know  are in the different buckets, and they are the blue squares.  Okay? Each node  has  some very simple rules. They behave  according to some very simple rules. It doesn't really matter  what those rules are, I'm just putting a few up there  to illustrate what I mean.  But the important thing about  a hash table is that you need to be able to put values  and you need to be able to get values. You need to get  and set. And  each one of those fundamental actions  requires a lookup, okay?  So that you know which nodes  you want to interact with. And all these interactions  are asynchronous, and lookups  are also parallel concurrent, because  you can ask several peers at the same time  about the information that you need.  So, how does the lookup work?  A recursive lookup is the 6°  separation gain, which always features  Kevin Bacon if you're talking about how they would act.  So, say I want  to put a value with a key whose hash  is in a position at 6 o'clock,  okay? There's the target  at 6 o'clock.  And the first thing I do is I ask  the nodes in my routing table  that are closest to that hash  and they reply  with nodes from their routing table that are closer  to the peers. And this keeps going  recursively until  I find nodes that  I actually...  that are actually close to the target key  until I can't find any nodes  that are close to the target key.  So, get and set requires a lookup.  And how is this handled in the realm  of a syncIO? Well,  a lookup is a future  because it's something whose result  we can't yet know until we finish looking up.  We need to interrogate all our peers  on the network, okay?  And the state of the lookup,  the progress in finding nodes that are  close to the target is held within the lookup instance.  And the lookup resolves  because it's a type of future  when the result is known. And the result  is either going to be a value, or not  a well-found exception in the case of a get  operation, or it's going to be a  little bit of the 20 closest  known nodes in the DHT  in the case of a put. And what I will do then  is contact each of those 20 nodes and say  store this information for me please.  So what about networking?  How does a syncIO handle this networking  protocols? How do nodes  in the DHT handle down the wire  aspect of input output?  So core concepts 5, 6  and 7 are streams, transports and protocols.  Streams are high level abstractions  that allow you to send and receive traffic  down the network, or from the network using  reader and writer objects.  Transports are provided by a  syncIO to handle low level  networking activities such as TCP  and UDP. And they handle the low level  IO layer and the buffering and the  eventually sort these out for you.  So you shouldn't really have to interact with them.  And protocols handle network  protocols at the application layer.  So for example HTTP or  net string and so on and so forth.  They're also a lot of fun. So streams  are flows of data that can be read from  or written to and they are built upon  the transports and protocols.  So what is a transport? A transport  is concerned about how stuff moves  over the network. Whereas a protocol  works out what to do  with the stuff that arrives from the network.  It turns out  it works out how to turn the raw bytes into  something meaningful such as a net string  message or an HTTP call.  And really  you only need to work with streams  and perhaps protocols if you're doing something a bit fun.  So my final thoughts  with my DHT  I've managed to get 100%  unit test coverage  and this is because  a syncIO is part of Python and  testing is normal. I'm using  the built-in unit test library and mock.  And so  how you organize your code is a key  factor for this. But  this is in contrast with for example  Twisty which has its own test drive  and its own  unit test class  doesn't work quite the same way as  the one in the standard library  which can be a bit confusing.  My DHT implementation is  small and simple. So there are only  871 lines of code  as of this morning  in the DHT itself  and this is because they syncIO and makes it  easy. I've got about one minute  left.  It makes it easy to think about concurrent  problems. So  I believe the abstractions make it easy  to write very simple code that's short  and comprehensible.  And finally  I need to mention  the difference between IO  and CPU bound. Don't use  asyncIO if you need to do something  with lots of CPU overhead because  of course that's going to block the event loop.  So  if you have lots of networking however  that's the sweet spot. So the future  in all kind of changes for better  in Python 3.5  coroutines with the async and await syntax  which Guido mentioned yesterday. His favorite  feature so it must be good.  So if you remember here's the  coroutine that  I  showed you earlier on. Simply  all I've done is taken out the yield from  and put the correct keywords in  Python 3.5. These are proper native  coroutines and they're not to be  actually confused with generators and it works  slightly differently. The new async syntax  outside the code block  defy the coroutine not some internal  yield from so it actually makes it easier  to read in my opinion.  And also the new features it comes  with an await context manager and iteration  protocols as well so you can do some really cool  things though that's not shown here. So  PEP 492 is  Guido's favorite feature. Go read that.  I've not tested this code  as I put it for illustrative purposes only.  That's it. Finished.  Going to yellow.  Smile.  APPLAUSE  The DHT project  is called the drugulous. That's the GitHub account.  Any questions?  Does anybody have a short  question?  A short question like my DHT  code. That's short.  No questions at all.  OK. Thank you very much.
Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____0_635.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____636_3215.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____3216_4259.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____4260_5315.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____5316_8615.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____8616_8879.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____8880_9791.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____9792_11195.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____11196_11807.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____11808_13319.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____13320_17579.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____17580_22295.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____22296_22583.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____22584_24335.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____24336_24887.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____24888_26075.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____26076_26651.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____26652_26879.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____26880_27875.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____27876_28751.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____28752_29159.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____29160_29399.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____29400_29567.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____29568_30239.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____30240_32051.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____32052_32231.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____32232_35135.png Nicholas_Tollervey_-_Lessons_learned_with_asyncio___Look_ma__I_wrote_a_distributed_hash_table____35136_36999.png
Thank you very much. Thank you. Thank you. So, yes, my name is Nicholas. I'm a freelance Python developer from the UK. And this is an introductory talk, really, about async.io through the medium of distributed hash tables. So async.io, as I'm sure you all know, arrived in Python 3.4. And my only expectation of you is that you know a little bit of Python. So what we'll do is we'll explore some of the course of the course. concepts of async.io. And I'll tell you the story of how I used async.io to create a fun little personal project which was to build a distributed hash table which Holger very usefully referenced in his keynote this morning, which is perhaps why there's rather a lot of you in this room. This isn't an exhaustive exposition of async.io. There's an awful lot I've had to leave out and it simplifies a lot as well. But my aim is to arm you with just enough information so that you can continue exploring the module and the concept of async.io. It's also a personal pedagogical exercise. If I can explain async.io in a simple sort of a way, it demonstrates my own clarity of thought about async.io and understanding about async.io. As you can hear, I'm talking very quickly because I have rather a lot of information to give to you. So yes, perhaps like Eric Gumbie, your brain will hurt by the end of this talk. So the first thing I want to do is to give you a little bit of information about what you can do. So I'm going to start with the first thing that needs to be answered is what does async.io do? Well, the Python documents are very clear about this. They state that this module provides infrastructure for writing single-threaded concurrent code using co-routines multiplexing IO access over sockets and other resources, running network clients and servers and other related primitives. You got all that? I've actually got that written down. I didn't do that from memory. Now, while I understand all the terminology from the documentation, it doesn't give me a sense or a feel of how I might use this module. And such documentation can make the module appear a little bit intimidating and sort of the realm of esoteric leaked Uber hackers like Trinity who's so leachy talks in courier fixed width. So you can figure out what Neo talks in. Anyway, what does async.io do? Well, we can do a lot better than that. We can keep the answer simple. Stupid. What does async.io do? As Trinity says, it lets you write code that concurrently handles asynchronous network based I.O. So let's be clear about what I mean by those terms. So concurrency is when several things appear to happen at once. Asynchronous literally means not synchronized. There's no way to tell when something may happen. The network, of course, as you know, is a medium for communicating with another device usually via the internet. I.O. is, as I'm sure you all know, input output when a program communicates with the outside world. So, the problem clearly stated is that messages arrive and depart via the network at unpredictable times and async.io lets you deal with such interactions simultaneously. So given this simple purpose, I want to place async.io into a practical context. So let's talk about distributed hash tables. So, again, let's start with the obvious question, what is a distributed hash table? Well, I'm assuming you know what a hash table is because it's more or less the same thing as a dict in Python. We are at your Python after all, and I think it's reasonable that I can expect you to all understand what a dict is. It's a simple key value data store. A distributed hash table is distributed because the whole is built from several independent yet related parts. So it's a little bit like this abstract encyclopedia. The whole encyclopedia is made up of volumes that are independent yet related to each other. And in our case, the distributed hash table is a structure that consists of many independent nodes that collaborate with each other over the network. DHT is also decentralized. So no node is more important than any of the others. So there are no client-serve relationship in the DHT. It's a loose peer-to-peer network of nodes. So, a distributed hash table, a DHT is a peer-to-peer key value data store. Why would I want to implement one of these? Well, it's a really interesting programming problem with fascinating properties. As I mentioned, there's no single point of failure. The DHT algorithm that I use, which is called Cademlia, efficiently scales to a huge number of nodes. It has good handling of fluid network membership as nodes leave and join the network. It's a solid foundation for more complex services, such as the ones that Holger referenced this morning. It's tested in the real world, so BitTorrent uses the Cademlia algorithm, FreeNet does as well. And obviously, if you're like Holger, it's an example of decentralized platforms which are fascinating on more than just a technical level, on a political level as well, perhaps. So, guess what? Distributed hash table nodes have to concurrently handle lots of asynchronous network-based I.O., which is the sweet spot for Async I.O. So, we have a context. So, how does Async I.O. Or how do I use Async I.O. to make all this work? So, let's introduce some core concepts. The first one being the event loop. And quite simply, this is some code that just keeps looping. Each iteration of the loop basically does two things. First thing is that it pulls for I.O. events that occurred during the time it took to complete the previous iteration of the loop. And the other thing that it does is that it runs any callbacks that need to be run during this current iteration of the loop. The loop also carries out various housekeeping needed for callbacks that have yet to be executed. But that's something that we can ignore. So, it's important, again, this is sort of an introductory talk, to define what polling and callbacks are. So, polling is discovering the states of something external to the program. And in Async I.O. this is network-related I.O. events. And a callback is some code that's to be executed when some event has occurred that's being detected via polling. And this metaphor, obviously the kids are polling. Are we there yet? Are we there yet? Are we there yet? And the mother is saying, I'll let you know when we get there. And this creates a sort of a callback in that she is promising to do something when some condition is met. So, that's why they get to the end of their journey and they get to Grandma's house. So, it's important to note that polling takes place once during each iteration of the loop. I.O. events are discovered by polling to determine which callbacks to execute during the current iteration of the loop. All pending callbacks executed one after the other. As it says so in PEPPY 15. And the loop can't continue while this is happening. It is blocked. So, the next iteration cannot start until all the sequentially executed callbacks finish in some sense. So, you're probably asking there's something wrong here. This doesn't sound very concurrent. Well, unfortunately, concurrency is a hard problem and there's actually more than one way to do it. So, it's worth looking at. So, it's worth taking some time to examine why async.io works in the way that it does. So, in the traditional threading model, several problems can happen. Task A reads the record, task B reads the record, both A and B change the retrieved data in different ways. Task B writes its changes, then task A writes its changes. What's the problem here? Well, task A has overwritten the record containing task B's changes. So, we have loss of data, which is the problem that we can avoid. So, why not wait until one task is finished before continuing? So, first do A, then B, followed by C, and so on. This is easy to understand and sort of deterministic. But what happens if A needs to wait for something such as a call to another machine on the network? Well, the program has to wait until A's network call completes. And in that situation, we can't get on with other stuff because we're still hanging around for A. So, given the situation in this slide, the program is described as blocked, and this is unacceptable if we're writing code that needs to react quickly to network-based events, which is precisely the sort of program that A SyncIDo has been designed to help it. And so, you're probably asking yourself, well, why not just get on with tasks B and C while we wait for the result from the network call for A? Well, if you're thinking that, then actually, you've described quite succinctly what A SyncIO does. So, welcome to the most important slide of the talk. I hope you're all paying attention. So, A SyncIO is event-driven. And this means that network-based I.O. is non-blocking. So, how does this work? Well, the program does not wait for a reply from network calls before continuing. Programmers, us, define call back to be run when the result of the network call is known. And in the meantime, the program continues to poll for and respond to other network-related I.O. events. Callbacks execute drawing the iteration of the event loop immediately after the expected network I.O. event is detected. Confused? Well, you shouldn't be, because actually, this is how we as humans think about concurrency. So, in the real world, we make plans all the time. The washing machine finishes is an expected event. And hanging the clothes out to dry is a callback for when this expected event happens. So, how hard can this be? Says the stock photo dude with the washing basket. Also, we multitask in the same way A SyncIO does. We skip between the things we need to do while we wait for other things to happen. We know we will have time to squeeze the orange juice while the toast and the eggs are cooking, while we make breakfast. We don't just stick some bridge in the microwave. Put it on for five minutes and then watch it. Well, I don't. You probably get on with other things. This is exactly the sort of thing that I'm talking about. We multitask. There's only one of us. There's only one A SyncIO event loop, and it's this is how we react to the world around us. The Microsoft, the Microsoft, the microwave it's a long night last night. The microwave goes ping and that is the expected event and then I take the porridge out and eat it. So a SyncIO avoids potentially confusing and complicated threaded concurrency while retaining the benefits of strictly sequential code. This is the fundamental advantage of a SyncIO. We plan ahead for expected events by defining callbacks to be called when such events eventually occur. In the meantime, we sequentially handle the callbacks related to other events that may have happened in the interming time. So questions you're probably asking yourself are how are asynchronous concurrent tasks created? How do such tasks pause while waiting for non-blocking network-based IO? And how are callbacks defined to handle the eventual results? To answer these questions, you need to understand code routines, futures, and tasks. So I'm going to attempt to explain code routines in about three slides. And a code routine essentially is an object representing an activity that eventually completes. Or it's a decorated function that returns such an object. And the important thing to know about these code routines are that they may be suspended, you use yield from syntax. And when a code routine is suspended, this allows the event loop to get on with the other things. Code routines are sorts of generators. So they lazily generate results. So calling a code routine doesn't actually start its execution. They yield from other objects. And when the yielded from object has a result, the code routine continues from the yield from statement that suspended it. And this is called re-entry. And at the end of the chain of yield froms is an object that returns the result or raises an exception. Rather than yielding from some other code routine. So this, oh dear, look at that. I think you can see it all. Yeah, that's just Chrome being a bit daft. So this is a decorated code routine method that handles an incoming HTTP request as part of my DHT. Upstream, something is yielding from the code routine created by this function in order to do something with the response that it generates. And this code will pause by yielding from the code routine created by the payload.read call. Which is a method that reads the raw data posted as part of the request. You can see that just after the try. Okay? When all that data has arrived, the code pauses again while waiting for the code routine created by the self.process data method itself. Waiting on other things such as perhaps a call to the database. And when the task encapsulated by this code routine is complete, i.e. the response, the upstream code routine gets the return result and resumes execution from where it yielded from in its code. So we kind of know how asynchronous activity happens, but how do we handle the results? How do I handle the result of a code routine? What about callbacks? Well, for this you need to understand futures and tasks. Which I'll try and explain in just two slides. So a future, if you've done twisted this will all be very familiar to you. A future represents the result that may not yet be available yet. Callback functions are added to a sort of a to-do list to be executed when the result is known. And a task is simply a future that wraps a code routine and the resulting object is realized when the code routine completes. We say the future has resolved, which sounds all very dot-two. So here's some example code, which I hope makes this clear. So the first thing I do is I create a callback function, handle resolved tasks, and the only thing that it does is record, it logs the result. The next thing I do is I create a task associated with a certain code routine that will pause. And then I add to that task the callback that I defined above. And the task object is automatically resolved when the code routine that's associated with it completes. And at the end I execute the code, because remember code routines are generated by setting up and running the event loop itself. So here's another perspective on this, a little bit mind-bending. So my buddy Terry Jones likes to point out that we're used to working with first-class functions in Python. We pass them in functions and we return them as values. We just might not know what the result is yet, but we can start to add callbacks to them. So let's do a quick recap before we come to the sort of end of the talk. I see him waving the five-minute flag. So an event loop is looping code that pulls for Io and manages event handling callback. So I can do that. And then I can do the same thing with the first-class function. So I can do that. Io and manages event handling callbacks. Coroutines an object that represents activity that eventually completes, or is a decorated function that returns such an object. Future represents a result that may not be available yet. And the associated callbacks are executed when it resolves. Like I said the future resolves in a kind of mysterious galafrey and sort of a way. And tasks are boiler-paid future classes that wrap coroutines. And these are realised in the same way that we do code-routine callbacks. So a given-all theory is very quickly look at how ASENC Io works in practice with my DHT project. For us to be able to do that, I need to very quickly explain how a DHT works. So as I mentioned before, a DHT is made with nodes. And the classic way to visualise this is with a clock face. And you'll see why in a moment. Each node has a unique ID that is within a set of all possible values of a certain hashing function. You all know what hashing functions are because you're all in Holders' Excellent Talk this morning. Okay? In my case, with my DHT, I use SHA 512. The ID's value indicates the node's position in the clock face. And in this way we can tell where the node is located in the abstract network, as it were. So we can tell who is close to who and how far away nodes are from each other. So there is some sort of notion of distance in the DHT. And data is a key value pair. And the key is turned into a hash, again using the same hashing algorithm, SHA 512 in this case. And the value is stored at nodes whose IDs are close to the hash of the key. So this is similar to understanding where to look things up in a multi-volume encyclopedia, going back to the example at the beginning. Articles are words, or keys in our case, and associated definitions are values that are stored in volumes that cover some alphabetical range within the global encyclopedia space, as it were. So ARDVARK belongs under A, whereas Zebra belongs under Z. Okay? So how do nodes know where to look? Well, each node maintains something called a routing table that tracks the state of its peers. And each interaction with its peers results in exchange of information about the nodes that it's been talking to. And that's how the routing table is populated initially and kept up to date, because nodes keep bumping into each other like ants in an ants nest. The routing table splits the clock face of nodes into buckets. Now, buckets contain the same number of peers, but buckets cover a smaller range, closer to the local node they are. Therefore, the local node knows more closer nodes. As you can see, me, I read about half past one, and the nodes that I know are in the different buckets, and they are the blue squares. Okay? Each node has some very simple rules. They behave according to some very simple rules. It doesn't really matter what those rules are, I'm just putting a few up there to illustrate what I mean. But the important thing about a hash table is that you need to be able to put values and you need to be able to get values. You need to get and set. And each one of those fundamental actions requires a lookup, okay? So that you know which nodes you want to interact with. And all these interactions are asynchronous, and lookups are also parallel concurrent, because you can ask several peers at the same time about the information that you need. So, how does the lookup work? A recursive lookup is the 6° separation gain, which always features Kevin Bacon if you're talking about how they would act. So, say I want to put a value with a key whose hash is in a position at 6 o'clock, okay? There's the target at 6 o'clock. And the first thing I do is I ask the nodes in my routing table that are closest to that hash and they reply with nodes from their routing table that are closer to the peers. And this keeps going recursively until I find nodes that I actually... that are actually close to the target key until I can't find any nodes that are close to the target key. So, get and set requires a lookup. And how is this handled in the realm of a syncIO? Well, a lookup is a future because it's something whose result we can't yet know until we finish looking up. We need to interrogate all our peers on the network, okay? And the state of the lookup, the progress in finding nodes that are close to the target is held within the lookup instance. And the lookup resolves because it's a type of future when the result is known. And the result is either going to be a value, or not a well-found exception in the case of a get operation, or it's going to be a little bit of the 20 closest known nodes in the DHT in the case of a put. And what I will do then is contact each of those 20 nodes and say store this information for me please. So what about networking? How does a syncIO handle this networking protocols? How do nodes in the DHT handle down the wire aspect of input output? So core concepts 5, 6 and 7 are streams, transports and protocols. Streams are high level abstractions that allow you to send and receive traffic down the network, or from the network using reader and writer objects. Transports are provided by a syncIO to handle low level networking activities such as TCP and UDP. And they handle the low level IO layer and the buffering and the eventually sort these out for you. So you shouldn't really have to interact with them. And protocols handle network protocols at the application layer. So for example HTTP or net string and so on and so forth. They're also a lot of fun. So streams are flows of data that can be read from or written to and they are built upon the transports and protocols. So what is a transport? A transport is concerned about how stuff moves over the network. Whereas a protocol works out what to do with the stuff that arrives from the network. It turns out it works out how to turn the raw bytes into something meaningful such as a net string message or an HTTP call. And really you only need to work with streams and perhaps protocols if you're doing something a bit fun. So my final thoughts with my DHT I've managed to get 100% unit test coverage and this is because a syncIO is part of Python and testing is normal. I'm using the built-in unit test library and mock. And so how you organize your code is a key factor for this. But this is in contrast with for example Twisty which has its own test drive and its own unit test class doesn't work quite the same way as the one in the standard library which can be a bit confusing. My DHT implementation is small and simple. So there are only 871 lines of code as of this morning in the DHT itself and this is because they syncIO and makes it easy. I've got about one minute left. It makes it easy to think about concurrent problems. So I believe the abstractions make it easy to write very simple code that's short and comprehensible. And finally I need to mention the difference between IO and CPU bound. Don't use asyncIO if you need to do something with lots of CPU overhead because of course that's going to block the event loop. So if you have lots of networking however that's the sweet spot. So the future in all kind of changes for better in Python 3.5 coroutines with the async and await syntax which Guido mentioned yesterday. His favorite feature so it must be good. So if you remember here's the coroutine that I showed you earlier on. Simply all I've done is taken out the yield from and put the correct keywords in Python 3.5. These are proper native coroutines and they're not to be actually confused with generators and it works slightly differently. The new async syntax outside the code block defy the coroutine not some internal yield from so it actually makes it easier to read in my opinion. And also the new features it comes with an await context manager and iteration protocols as well so you can do some really cool things though that's not shown here. So PEP 492 is Guido's favorite feature. Go read that. I've not tested this code as I put it for illustrative purposes only. That's it. Finished. Going to yellow. Smile. APPLAUSE The DHT project is called the drugulous. That's the GitHub account. Any questions? Does anybody have a short question? A short question like my DHT code. That's short. No questions at all. OK. Thank you very much.
introductory talk, really, about async.io through you the story of how I used async.io to create a room. This isn't an exhaustive exposition of that you can continue exploring the module and in a simple sort of a way, it demonstrates my own the end of this talk. So the first thing I want with the obvious question, what is a distributed perhaps. So, guess what? Distributed hash table suspended, this allows the event loop to get on Well, for this you need to understand futures and code, which I hope makes this clear. So the first from the network. It turns out it works out how