Conversation
|
Hi @fantix, would you mind taking a look at this PR as well? It significantly improves SSL read performance in case of non-buffered user protocol |
|
Thanks @tarasko. Turns out this also fixes a memory-leak problem we've hit in a production. Here are our numbers: A FastAPI/uvicorn service of ours grew ~110 MiB RSS per pod per day and never leveled off. It talks to Postgres through asyncpg over TLS. asyncpg isn't a BufferedProtocol, so every DB read goes through We replayed our production traffic, 150k requests per run, with master and this PR side by side: With this PR, memory flattens after warm-up, the same as when Postgres runs over plain TCP. There were no errors and CPU per request didn't change. Setup: Python 3.13.15, glibc 2.41, Linux, uvicorn 0.42 + httptools 0.8, asyncpg 0.31, TLS 1.3.
Would be great to see this merged and released. |
@parikls But perhaps you would be interested to try out aiofastnet. aiofastnet still works on top of uvloop, but it replaces transport layer with its own implementation which is even more efficient than this PR. |
Calling SSLObject.read(SSL_READ_MAX_SIZE) has a big performance issue. Internally it first allocates bytes object of SSL_READ_MAX_SIZE bytes, then reads into it, then shrinks it down to the actual number of bytes read.
Given that SSL_READ_MAX_SIZE = 256 * 1024,
we allocate 256K every time we call SSLObject.read from SSLProtocol._do_read__copied.
I've fixed it by allocating our own buffer and passing it to SSLObject.read.
I have attached perf output before and after this change.
Other changes:
Before:
After: