 I would like to introduce Andrew Svetloff, and yeah, I think you know what he's talking about better than me because you are here, I'm just here for the next talk, but I'm very interested. Okay, thank you. Okay, guys. I'll try to make a simple enough introduction for IRGDP. I am Andrew Svetloff, I'm a Python Core Developer, use Python for 16 years and Core Developer for last four months. I took part in ICN-CIO development as a committer, and now I'm a maintainer for IRGDP, and after all, there are other libraries under IRGDP, and they are like Postgres driver, several IRGDP extensions, Redis driver, well, Redis is not 100% Python, Kafka, whatever. So, trust me, I know something how to work with ICN-CIO code. Why do we need a synchronous code at all? That's very easy. Now is the age of microservices, right? And let's imagine a classic situation. We have a client, it's a browser or client which goes through API, we have a front side and front side communicates with a lot of microservices, internal microservices and external services like Twitter, GitHub, Facebook, whatever. And this architecture, we usually should perform many parallel HTTP requests from our front side to microservices, collect data back, transform it and return to user. And we can use threads and we can use asynchronous code. These threads, casual machine can support about 1,000 threads, but with ICN-CIO, with lightweight threads, amount increases to 1,000,000. The server may support more than 1,000 threads, maybe 10,000, 100,000, that's top limit. But it's still low limitation. With ICN-CIO and any asynchronous network, it allows to scale more, it allows to do more parallel work and only if your parallel threads are your bound, it does help with CPU bound, CPU heavy tasks, but it helps very well. So HTTP is a library dedicated to work with web, HTTP from both side, server and client. It supports persistent connection, web circuits out of the box and many, many other things. I'll describe later. Zalabry has three years long story. At very, very beginning it was part of ICN-CIO, in the last time it was called Tulip. But we were asked to rip it out, we instructed it into a new project, HTTP, and it was a good decision because HTTP was very young on those days, it changes quickly. And we released 22 releases so far, it's much faster than IO-HTTP release cycle. It has 150 contributors, good coverage, so it's much enough library. Client API. Client API, some hard looks like request. It's not a total API copy, but it's part of it. Well, I believe everybody knows how requests work, maybe the most popular third party library for Python. We make requests, get call, get response back, ask for response status code, get response body, as text, as JSON, as binary files, streaming and so on. I'll translate it in IO-HTTP, no way, sorry. That's because in IO-HTTP we don't want to encourage bad usage practice. We had, you know, it's still work, I can write it but it's deprecated. Let's go further. Request has a session concept. Session is a container for cookie storage and for connection pool. And you can perform, get request session, return response back and ask for response text. Session supports cookie, keep alive for free, may make your system, I don't know how, three, five, seven times faster, depends on your networking and how long your server is from client. I highly recommend to use session requests every time. Unfortunately, the first page which user, documentation page, which user look when request documentation, don't encourage session usage. So the most part of request code, which operates with request, is not optimal from my experience. Now we have HTTP client code, we have a session, HTTP session, we have a get response but you see, we use async with, async contacts manager for handling resources, for aggressively closing all open resources, open connection, open response, everything. And it's async because it works in async. And we have a response here. For reading response body, you should use await syntax. Again because usually you have header, response header, immediately. And reading the whole response time or response body or reading by chunk takes time. It requires input out communication with server. So we require await here. Now I want to say couple words about coroutines. Relative complex concept, but for casual user, it can be divided into very simple rules. How to use it? So coroutine is function which is not def but async def. If you see async def, it means you have deal with coroutine. If you have to call coroutine, put await before the call, like await sleep or await function. And if your function contains await inside, it should be coroutine itself. So the function itself should be async def also. That's it. Next thing, the power of asynchronous approach, asynchronous way for making programs. If we need to fetch several resources in parallel, we can create several tasks. Task is a lightweight thread and execute it in parallel, wait for all tasks together, for all results, or use another async IO API for getting results as they are complete, completion order. And our fetcher will try to fetch both Google Chrome and Python or home pages in parallel. What does it mean in time? Point of view. Synchronous code, execute three fetches one by one in sync, in sequence. And obviously it's long. If we start three real threads in Python, thanks to Gil and other applications, the threads executed in parallel, but gray part of this graph is a waiting state, program does work, but waiting for switch, for Gil releasing and other things. Async code, execute everything in the same thread, usually it's the main thread. But switch, micro thread, switch between tasks, and it does it very quickly without need for operation system context switch, and thanks to it, it supports million, billion tasks easily. Next thing, what you should know when you work with client API is time out. Why? Because every request, every try to acquire data from server may hands for very long period, 10 minutes, 30 minutes, without disconnection, exception, without any information, any suggestion what target server is done. Very often you get exception quickly, but sometimes you can, why? Because internet works in this way, we cannot so change internet protocol. But we can wrap our calls under time out, context manager, and it's safe. I recommend to use time outs everywhere, and context manager is very convenient for this. Web sockets, this is very simple example how to use web sockets, instead of saying session gate or session post, you use session web socket connect, pointer, web socket endpoint, web socket object, iterate our messages in web socket, I think for is asynchronous iteration. It may wait for next message, if still not available, and on waiting, I think your loop will switch to other tasks, pending tasks. When message is ready, we check for message content, if it's query for closing, we close web socket, and also we do something like ping-porn communication. This is very basic pattern, and all web sockets client code looks like this. That's it for client side, now server, it's about server. I believe everybody knows how to write hello world in Django, in Django, you have a view, it's a function which accepts request and return response. You file URL map, which maps for URL to view function, and execute it while Django manage command. In HTTP, we have almost the same, when we develop HTTP web server, high-level web API, we try to be very close to Django, Flask, or Battle to classic VSI frameworks, only with one exception. Our code is asynchronous, so we have also view, it's called web handler in HTTP documentation, which accepts request, return response, we have an application, register, route for view, run everything, but see, our index is not a function, it's corkin, which means we can do asynchronous work inside corkin, and the most obvious way is we can request another resources via client API, we can do web socket communications, and we, in opposite to classic Vizgi framework, we shouldn't return answer as quick as possible, we can wait. Lead user, client, obviously, will not see an answer quickly, but it does have a server, waiting inside our web handler, does stop other handlers if they have something to do, it's the main and the biggest difference between IOHCP and all other Vizgi frameworks. I don't know how many Vizgi frameworks there are, but many dozens, at least. Tornado, Tornado is also a synchronous framework, as well as Vizgi, but Tornado built on another concept, and Tornado, you have a request handler, and user should derive from it and override that method, or post method, it works. I don't prefer this way, but can live with it, but we found that utilizing a view web handler concept is much easier for understanding for end users. That's why we built asynchronous IO code in this way, not in Tornado. Web sockets for server looks almost the same as client looks like. The only difference, we should create web socket response, prepare it from request, and iterate our messages from web socket response, almost the same. The only thing that you should keep in mind is, like all other network communications, you have to have some mechanism for handling timeouts, because web sockets can stop working without any notification for long time, and programmatic ping and ping-pong communication can prevent these hands, inform server quickly that client is connected. What I suggest for how to develop asynchronous IO programs effectively, at first, run test or create your development environment as a single process. After creating your code, after debugging, testing, you can deploy it in different containers, in different process, on different nodes, hidden all under, say, NGINX in reverse proxy mode. But for development, it's much, much easier to put everything into the same process. Wow. Sorry. I didn't expect it. It's a big fail. No, it's not suspended. Sorry for this. So HTTP code does require tools like Celery. You just can create a long-running task for new thread inside your program. It's much more easier. Sometimes you need persistent pending tasks, which will be restarted if task execution has failed, but it's a red situation. In the most case, you can just create new tasks in the server and that's it. And you can do very long communication inside WebHandler. It's very convenient. Next simple thing. If you forgot to close gracefully your response or do something with stuff, you will get an exception like, it's not exception, it's actually warning. Future exception was never tried. It's wrong. It's programming error. You see runtime error as what exception was pushed into future before getting result. But I have no idea where in code it was happening. So run our program with Python, I think your debug environment flag and you will see trace back to the point where bad code was created, where object was created. Also, I highly recommend to you pass loop everywhere or don't pass it at all. Why it's important? It's important because easy to write test with explicit loop. I told about client session, use it for sharing state and for keeping open connection for supporting keep alive network pattern. We have one session. We have several tasks for features. We wait for all these tasks completion. That's it. How to write tests? Easy. But create new loop for every test in setup. Disable default loop and enter down close the loop. Also, test function in test framework should be regular function, not corking. So this trick can help very well. In HTTP, we have a decorator which does this call run until complete. But for understanding, we should have coroutine inside regular function and run this coroutine. Why it's important to disable global loop? Because with global loop enabled, we can start a task in one test, finish test successfully. Execute more tests 2 or 10. And on 12 tests execution, your task from the first test may fail. Because they share the same loop. Disabling default loop makes tests really separate. And that's why important to pass loop everywhere. Don't clash between two different tests. This is example how it works with PyTest framework. Honestly, I prefer PyTest nowadays. I tried unit test for long, for about decade. But switch to PyTest finally. We have PyTest HTTP plugin and everything looks easy and short. We have application, we have test client with get, post method and other. And you see, we create application, pass and handler in it. And perform test request to our test server. Analyze result back. It's very, very easy and very, very convenient to have tested and testing coroutines in the same trade. You don't have to start separate trade for starting application or separate process. You have all together. You can easily insert import PDB, PDB set trace or whatever you prefer for debugging your failure. I found it's very convenient. And the last one, what I want to mention. It's from Stackartflow question asked yesterday. A guy created code like this. This is Mongo client, MongoDB client. And it works manual run. But when he started to write unit test for this, test hands. I was handing. What, why? Just because I think I'm a motor client. Accept loop. And it was copied to event loop, default event loop. First test finished. Second test started. Created another loop. But asking for finding data using motor client. Completed to old, not used anymore loop. Bad idea. It has. But what to do? Request has app. It's an application. Application is a deep like object. You can push everything into this name space. And it's safe. When create an application, we create Mongo client. Push it into our application name space. Register clean up for graceful shutdown. Connection to Mongo. This is the same you can do with databases, with everything. It's very good. Register clean up on clean up signal. And that's it. So, let's give this section. It's not very interesting actually. Questions? Are there any questions or comments? I can react that disabling global event loop, main event loop, makes execution not a sync anymore for testing. So, so that test platform will be executed one by one and not sharing the same thread, not sharing the main thread, just to simplify debugging. There are ways to use another approach. You can create event loop and register the new event loop as default every time for every test. Default event loop will be new instance. But I found it's error problem. Sorry. Maybe it's the heaviest way, but the most safe way to pass loop everywhere. Maybe error, sorry. But it's my opinion and my experience. Okay, we have another question here. So, I was doing benchmarking of a service on a synchronous framework and on AO HTTP. And what I've noticed is that I can have a 16 millisecond latency on each request for, like, say, 20,000 requests a second. And when I double the load, so 40,000 requests a second, it's still the same latency. So, what happened? Well, it's a piece that, you know, it was still free CPU earlier, but, you know, I've doubled the load, but it's still the same latency. Did you observe something like that, or no? I don't know. I need to take a look on this benchmark more carefully. Sorry. Okay, so I will talk to you later. I hope. Thanks. More questions? Here. Yeah, I mean, just a question. Had a few other questions. Just a question from a previous question, though. Was the latency actually, was it in async.io, or was it a network latency or in the network stack? Just a question. Very interesting talk. I do quite a bit of web apps. Python has a great story in terms of writing. Web apps are really easy to write a web app, and an absolute payment act to deploy them. So, you can write an app very simply in Flask and Django in any other of the frameworks, and then once you deploy it, you have to start configuring engine X, you have to start, you know, celery for your long-running tasks, and you have Redis for your temporary, your caching, and you have lots of different components with fragile connections between them, so the whole thing becomes a bit of a nightmare and very fragile and hard to debug. So, async.io looks like it can simplify the whole thing a great deal. You mentioned getting rid of celery because you can do the long-running tasks using the async framework. That's great, but I'm just wondering why you suggested deploying async.io in the same way that you're doing behind engine X. Can't we just use the async HTTP server as a production server? So, that's one question. Another question is HTTP2 support. Okay. HTTP can be used as just a web server as much proxy, but for production, I recommend to push it behind engine X. Why? First, you usually have to have static files. If it's a requirement, engine X do it much better than Python. We have static file support, so it's a real performance use engine X. And another point, much more important, engine X has a very long story for preventing malvirus, for preventing attacking code. It's done by limiting buffer size and it has very good experience in this area. We tried the best, but I don't have evidence, but I'm not 100% sure we have no holes for attackers. About HTTP2, we have plans, but have nothing ready yet. There is a pull request, but it's still not ready. We have one more question here. Hi. Thank you for the talk. So, my question is a bit more like, do you have any big features in mind to add to the library, meaning that if there is a roadmap or these kinds of things? Well, we are not so formalized to have roadmaps, but HTTP is hosted on GitHub. We have issues and that's it. Any other questions? In our targets are HTTP2 support, performance and nested applications and fixing our bugs. You'll find it, of course. Any other question? If this is not the case, thank you again, Andrew.