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. |
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: