-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Experimenting with Trio-based Sanic server #1662
Conversation
…nt instead of serve_multiple
Status update:
I've encountered a connection glitch causing failure to read request, followed by connection timeout 20 seconds later, on asyncio and uvloop. The glitch occurs on fresh connections (maybe one in thousand), never in keepalive requests, and it never happens in trio implementation. Haven't yet figured out how to track it down. Next steps:
|
- New implementation is one line of code and twice faster than the old one. - Whole header block encoded to UTF-8 in one pass. - No longer supports custom encode method on header values. - Cookie objects now have __str__ in addition to encode, to work with this.
into a combined class protocol.H1Stream; added error handling.
Today I managed to clean up most of the big mess I had created while sketching various features. Still much cleanup and refactoring to be done, too. Most of my recent work on normal Sanic, such as the header optimizations, have also sprung off this branch. |
(Sorry this is a bit of an aside, but I think it is worth considering). As Sanic is an ASGI framework this PR should run with a Hypercorn ASGI server utilizing the Trio worker. I think it would be worth testing this when considering this PR. |
Benchmarked 5500 requests per second on Windows, single-threaded, with a These are keep-alive requests, so connection handshake and such are negligible. A request is processed in 180 µs, and cProfile shows us where most time is
|
Fixing receive_body bumped it from 5500 req/s to 5800 req/s... But disabling timeout updates gives 7300 req/s. |
Sanic has nine sites where it calls handlers or middleware functions and then Another alternative would be to wrap all non-async functions in async wrappers that can be awaited without this check, as they are registered to the app, avoiding the checks every time the functions are called. This would have roughly equivalent performance to the hasattr approach but allows for cleaner calls without such checks. OTOH, the wrapping itself would be somewhat complicated. In any case, it would be good to refactor middleware and routing handling so that no code is duplicated between asgi.py, app.py and server.py, reducing the number of these call sites, and largely avoiding the inconvenience while also better preserving identical semantics for native and asgi hosting. Comments & help regarding these issues would be appreciated. |
I would love it if this can work. |
I expect to resume working on this in December. |
@Tronic I think you are hitting on a couple of big items that need to be addressed probably outside of this PR. Namely the streamlining of the multiple calls to the router/middleware/listeners. I like the Personally, I think we need to resolve these other items before attempting a third server option. |
Sure, feel free to use it. I already forked a few PRs (most already merged) off things I found while experimenting on this. As stated in the beginning, this is more of a hacking ground than something meant to be merged as is. |
I'm closing this as the work was never meant to be merged and I don't have current plans to keep working on this branch. At this point it seems easier to copy&paste usable parts of code rather than try merging with the last six months of changes in master, in case one wishes to pursue with Sanic native Trio server. Also worth noting is the simplicity of implementing Trio support over ASGI, which suggests that a native server might possibly plug into the ASGI API instead. There are also various bits in this that should still be picked up and implemented on asyncio server as well, like SSL SNI support. Expect to see separate PRs on that. As for now, anyone wishing to do Trio webapps on Sanic is encouraged to use |
Attempting to leave the AsyncIO horrors behind, I spent the last two days porting Sanic to Trio. It turned out to be less tedious to rewrite most of server.py from scratch, so here you have a HTTP server that can serve requests but that still lacks a lot of normal Sanic functionality. Also, I wanted to keep changes to other parts of Sanic minimal, such that this might be at some point be available as a switchable mode, e.g.
SANIC_FRAMEWORK=asyncio|uvloop|trio
.Interesting highlights:
listeners
is a Python list of sockets)There is a lot more to do, many things are missing or broken, but I am posting this here for discussion. Should there be a Trio-mode in Sanic at all? Trio may never match uvloop's performance in microbenchmarks, and two modes is added maintenance burden. Anyone else willing to participate?