MagicStack / uvloop

Ultra fast asyncio event loop.
Apache License 2.0
10.45k stars 548 forks source link

Improve SSL performance by avoiding SSLWantReadError exception and using much faster checks whenever possible #629

Open tarasko opened 2 months ago

tarasko commented 2 months ago

SSLWantReadError is expensive.

https://github.com/python/cpython/issues/123954

This PR tries to predict that there will be SSLWantReadError by checking incoming.pending and SSLObject.pending() first. This check works in 99% of cases. For the rest 1% we still rely on SSLWantReadError

andersfylling commented 4 days ago

Can you expend on how you figured out it handles 99% of cases, and what are the last 1%?

tarasko commented 3 days ago

99% - 1% is figurative. I don't know the real numbers and obviously it depends on a particular use case.

Checking incoming.pending > 0 or SSLObject.pending() > 0 may not help if we've received only a part of SSL frame. I think it may happen if system TCP stack broke it up or some receiving buffer was too small. In such case incoming.pending > 0 or SSLObject.pending() > 0 will be True but when we call SSLObject.read there will be SSLWantRead exception anyway.

In my use case client and server exchange a lot of small messages and every message (every SSL frame) can fit into a single TCP packet. Incoming data can also fit into all receiving buffers. So it never happens that uvloop reads only a part of SSL frame, it is always a complete frame, and SSLObject.read is able to process all incoming data.

Checking incoming.pending > 0 or SSLObject.pending() is very cheap comparing to raising and catching SSLWantRead. I think it would be always good if we can prevent SSLWantRead.