-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Description
Today SSLStream uses a 4K buffer for the TLS handshake and a 16K buffer for the reading TLS frames. These buffers come from the array pool today which makes it really easy to exhaust the buckets in the shared array pool for those commonly used sizes. This affects websocket and any other scenario that results in a large number of long lived concurrent connections.
Regression?
No.
Data
A demo of 5000 websocket connections connected to an ASP.NET Core server using Kestrel:
Here's a heap dump sorted by inclusive size. Most of what is being held onto is array pool buffers.

On top of that, I took a trace of the same set of connections and you can see the ArrayPool event source says the number of buffers allocated is 5000. The buffers are all 32K in size (because of how the SslSteam adds the FrameOverhead).
Allocation profile of 5000 websocket connections. You can see the array pool has the most byte[] allocations.
They're mostly in the handshake and reading (I'm not writing in this app), just sitting mostly idle with ping pongs:
Analysis
We shouldn't use the array pool buffers for reads that may take a long time. That means both for the handshake and the encryption/decryption:
runtime/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs
Line 1040 in 5dacf1e
| _internalBuffer = ArrayPool<byte>.Shared.Rent(ReadBufferSize); |
The handshake will end up doing the same for 8K buffers in the pool but potentially for a much shorter duration as clients negotiate quickly and there's a timeout to complete the handshake (in kestrel).


