hypercorn
hypercorn copied to clipboard
Improved H3 for hypercorn.
I am a co-maintainer of aioquic, and am also using hypercorn w/ H3 in a project. I've noticed from testing related to an aioquic ticket that the way hypercorn's code is calling aioquic is suboptimal. It's creating a timer on every received packet, and this causes terrible slowdowns on large data transfers. This patch is the start of a revamp to handle timers much more efficiently, and to call aioquic in its preferred patterns. I've had good results interoperating with a trio-based server with hypercorn with Chrome and curl. I have written the asyncio port, which is just a mirror of the trio port, but I haven't tested it yet.
Also besides testing there are still some improvements needed for connection cleanup as the current method I'm using is inefficient. Finally we may want to add "retry mode" to the handshake, as this forces the client to prove they can roundtrip with you before you start serving them.
I'm making this PR now just to give you a heads up and show you what I'm thinking early.
Oh, and thanks for all the good stuff!
The asyncio support is now hand tested and works, and state management is cleaner and cleanup is now efficient. I still have to add retry mode.
Example performance difference:
Stock hypercorn serving a 20 MiB file to curl:
curl: (56) QUIC: recvmsg() unexpectedly returned -1 (errno=61; Connection refused)
curl --http3 https://localhost:9292/static/foo.bin -o foo 0.10s user 0.13s system 7% cpu 3.234 total
Note that it crashed after only about 8 MiB because all the spawned timers firing are happening so frequently that the loss detection code in aioquic (which isn't expecting to be called without a chance to progress) is backing off so much it exceeds the exponent representation size in a float. Aioquic should guard itself better here, but my point is we are at 3.2 seconds and haven't transferred much. Much of the time spent seems related to task scheduling.
Here's the code from this PR doing the same thing:
curl --http3 https://localhost:9292/static/foo.bin -o foo 0.11s user 0.18s system 21% cpu 1.328 total
This is a successful transfer of all 20 MiB. Granted it's still only around 1/9 the speed of transferring from nginx, but it finishes correctly and is faster than the one that fails.
Many thanks for doing this, I'm short of time to review it at the moment.
No worries, it will be there when you are ready for it :)
Thanks. I wanted to go in a slightly different direction and utilise a Hypercorn pattern for single tasks. I've done so in ab98383f521a8b7d4f173ffa1f28b443e85561d8. Thoughts very welcome.
I'm now looking at the retry code - why would this be a configuration option? Is there a reason to disable this?
I'm ok with going a different direction, assuming you reuse/adapt a few other parts of this patch:
- The session ticket support is not crucial but may be nice, as it allows TLS resumption
- Retry is good for anti-amplification and should probably be on by default.
- The features related to connection state and cid tracking.
I'll add the retry part in the next release, I've ran out of time today.