You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
HttpConnectionPool should aggressively open multiple HTTP/2 connections under load if EnableMultipleHttp2Connections is set #66944
On HTTP/1.1, the HttpConnectionPool will attempt to open as many connections as there are pending requests (within the limits of MaxConnectionsPerServer).
On HTTP/2, we will only open one connection at a time, even if we have a lot of requests pending and EnableMultipleHttp2Connections is set.
We should consider attempting to open up to Number of requests / InitialMaxConcurrentStreams (100) connections if EnableMultipleHttp2Connections is set and existing connections aren't able to support more streams.
Clients SHOULD NOT open more than one HTTP/2 connection to a given
host and port pair, where the host is derived from a URI, a selected
alternative service [[ALT-SVC](https://datatracker.ietf.org/doc/html/rfc7540#ref-ALT-SVC)], or a configured proxy.
changed the title [-]HttpConnectionPool should aggressively open multiple HTTP/2 connections under load[/-][+]HttpConnectionPool should aggressively open multiple HTTP/2 connections under load if EnableMultipleHttp2Connections is set[/+]on Mar 21, 2022
A client can create additional connections as replacements, either to
replace connections that are near to exhausting the available stream
identifier space (Section 5.1.1)
which is about running out of integer values for stream id and not running out of available streams on a connection.
For completeness, the original issue: #35088 which references 2 prior arts:
And only WinHTTP docs mention opening a new connection after stream limit is hit:
Allows new requests to open an additional HTTP/2 connection when the maximum concurrent stream limit is reached, rather than waiting for the next available stream on an existing connection.
So I'm not against opening connections more aggressively if we take into account the max concurrent streams count.
I think attempting to open Number of requests / SETTINGS_MAX_CONCURRENT_STREAMS might make sense after some initial delay to ensure the number of concurrent requests doesn't quickly decrease after a burst. EnableMultipleHttp2Connections being set probably is an indication that the caller expects many requests to be long running and not bandwidth constrained therefore requiring a lot of concurrency.
I'm less sure about Number of requests / InitialMaxConcurrentStreams (100). I think HttpClient should probably wait for at least one SETTINGS frame before opening too many connections.
Clients SHOULD NOT open more than one HTTP/2 connection to a given
host and port pair, where the host is derived from a URI, a selected
alternative service [[ALT-SVC](https://datatracker.ietf.org/doc/html/rfc7540#ref-ALT-SVC)], or a configured proxy.
I remember this discussion from when EnableMultipleHttp2Connections was added. I'm in the firm belief that the spec is worded from a browser perspective.
The requirement makes sense when there are many clients, e.g. browsers, making requests from a server. It doesn't make sense when there is a single client, e.g. a microservice app or proxy, making requests from a server.
My 2 cents.
Reacted by Miha Zupan, Vuk Vasić, Yngvar Johnsen, Sander Saares and Ryan Erdmann
Why does the proposed InitialMaxConcurrentStreams start with Initial? I remember there used to be a bug in SocketsHttpHandler where it wouldn't limit the number of streams it started on a connection until it got SETTINGS_MAX_CONCURRENT_STREAMS from the server. A sudden burst of requests would cause more than 100 streams which caused the server to start rejecting them. The fix was to have an artificial limit of 100 until the server settings were received. Is this property value for this time? And then it gets overridden by the server's SETTINGS_MAX_CONCURRENT_STREAMS.
I think the property should drop Initial and just be MaxConcurrentStreams. If the goal is to distribute streams over more connections then a client setting that specifies a limit to the number of streams on a connection is fine. A client can't exceed SETTINGS_MAX_CONCURRENT_STREAMS but there is nothing stopping it from sending fewer streams on a connection before opening a new one.
So a server specifies SETTINGS_MAX_CONCURRENT_STREAMS = 100, but SocketsHttpHandler.MaxConcurrentStreams = 20, so the client sends up to a maximum of 20 requests on a connection.
InitialMaxConcurrentStreams is the already existing limit of 100 streams until we receive the settings. It's an internal constant and not configurable at the moment.
If the goal is to distribute streams over more connections
This issue specifically isn't about allowing more/less streams per connection.
It's only about allowing having more than 1 pending connection attempt in flight at a time.
If your connections are established quickly enough, there would be no real visible difference in behavior.
Having such a property may be interesting, but that's a separate issue.
Ah ok. I thought part of the issue was to add a new property called InitialMaxConcurrentStreams.
re: "Current proposal has risk to have more connections than strictly needed" - I think this is fine. The initial burst may create more connections than are needed, but the logic SocketsHttpHandler uses to assign new requests favors the first connections created. Any extra connections created during the initial burst will run out of work and then be closed for inactivity.
Triage: Looks like we are ok with current proposal. RFC is not a problem anymore. More connections is alright in this case and can be capped if needed later.
For server-side cases definitely it makes sense to have a configurable number of connections open. This way the load will be spread if you have multiple instances of some microservice or scaled app.
What about having Min and Max pool sizes as options (MinConnectionsPerServer, MaxConnectionsPerServer)?
Then at the start, it will open MinConnectionsPerServer and round-robin through these connections, if some connection reaches MaxConcurrentStreams then it could open another one up until MaxConnectionsPerServer are open.
Activity
wfurt commentedon Mar 21, 2022
This would effectively violate H/2 RFC recommendation
https://datatracker.ietf.org/doc/html/rfc7540#section-9.1
MihaZupan commentedon Mar 21, 2022
Right, but isn't that the point behind
EnableMultipleHttp2Connections's existence?wfurt commentedon Mar 21, 2022
I assumed we would but that contradicts the "is set and existing connections aren't able to support more streams."
[-]HttpConnectionPool should aggressively open multiple HTTP/2 connections under load[/-][+]HttpConnectionPool should aggressively open multiple HTTP/2 connections under load if EnableMultipleHttp2Connections is set[/+]karelz commentedon Mar 22, 2022
Triage:
EnableMultipleHttp2ConnectionsManickaP commentedon Mar 22, 2022
I misread this part:
which is about running out of integer values for stream id and not running out of available streams on a connection.
For completeness, the original issue: #35088 which references 2 prior arts:
And only WinHTTP docs mention opening a new connection after stream limit is hit:
So I'm not against opening connections more aggressively if we take into account the max concurrent streams count.
cc: @Tratcher @JamesNK
halter73 commentedon Mar 23, 2022
I think attempting to open
Number of requests / SETTINGS_MAX_CONCURRENT_STREAMSmight make sense after some initial delay to ensure the number of concurrent requests doesn't quickly decrease after a burst.EnableMultipleHttp2Connectionsbeing set probably is an indication that the caller expects many requests to be long running and not bandwidth constrained therefore requiring a lot of concurrency.I'm less sure about
Number of requests / InitialMaxConcurrentStreams (100). I think HttpClient should probably wait for at least one SETTINGS frame before opening too many connections.MihaZupan commentedon Mar 23, 2022
I suggested
InitialMaxConcurrentStreamsas some starting point, but we could go with some factor of that.The logic could be:
MaxStreamsPerPendingConnection = 5 * InitialMaxConcurrentStreams(500)queueCount > MaxStreamsPerPendingConnection * pendingConnectionsMaxStreamsPerPendingConnectionis updated toSETTINGS_MAX_CONCURRENT_STREAMS * 5Alternatively, we could start with
MaxStreamsPerPendingConnection = int.MaxValueand force waiting for at least one established connection.This would still be rather conservative in avoiding opening too many connections while doing something if the queue grows.
JamesNK commentedon Mar 23, 2022
What is the goal here? Improve performance by spreading HTTP/2 requests across more HTTP connections?
JamesNK commentedon Mar 23, 2022
I remember this discussion from when
EnableMultipleHttp2Connectionswas added. I'm in the firm belief that the spec is worded from a browser perspective.The requirement makes sense when there are many clients, e.g. browsers, making requests from a server. It doesn't make sense when there is a single client, e.g. a microservice app or proxy, making requests from a server.
My 2 cents.
MihaZupan commentedon Mar 23, 2022
Increase the speed with which we ramp up the number of connections given a burst of requests.
It also helps in the case where opening a connection may be slow / sometimes fail, by having multiple pending attempts at a time.
JamesNK commentedon Mar 23, 2022
Ok.
Why does the proposed
InitialMaxConcurrentStreamsstart withInitial? I remember there used to be a bug inSocketsHttpHandlerwhere it wouldn't limit the number of streams it started on a connection until it got SETTINGS_MAX_CONCURRENT_STREAMS from the server. A sudden burst of requests would cause more than 100 streams which caused the server to start rejecting them. The fix was to have an artificial limit of 100 until the server settings were received. Is this property value for this time? And then it gets overridden by the server'sSETTINGS_MAX_CONCURRENT_STREAMS.I think the property should drop
Initialand just beMaxConcurrentStreams. If the goal is to distribute streams over more connections then a client setting that specifies a limit to the number of streams on a connection is fine. A client can't exceedSETTINGS_MAX_CONCURRENT_STREAMSbut there is nothing stopping it from sending fewer streams on a connection before opening a new one.So a server specifies
SETTINGS_MAX_CONCURRENT_STREAMS= 100, butSocketsHttpHandler.MaxConcurrentStreams= 20, so the client sends up to a maximum of 20 requests on a connection.MihaZupan commentedon Mar 23, 2022
InitialMaxConcurrentStreamsis the already existing limit of 100 streams until we receive the settings. It's an internal constant and not configurable at the moment.This issue specifically isn't about allowing more/less streams per connection.
It's only about allowing having more than 1 pending connection attempt in flight at a time.
If your connections are established quickly enough, there would be no real visible difference in behavior.
Having such a property may be interesting, but that's a separate issue.
JamesNK commentedon Mar 23, 2022
Ah ok. I thought part of the issue was to add a new property called
InitialMaxConcurrentStreams.re: "Current proposal has risk to have more connections than strictly needed" - I think this is fine. The initial burst may create more connections than are needed, but the logic SocketsHttpHandler uses to assign new requests favors the first connections created. Any extra connections created during the initial burst will run out of work and then be closed for inactivity.
karelz commentedon Mar 29, 2022
Triage: Looks like we are ok with current proposal. RFC is not a problem anymore. More connections is alright in this case and can be capped if needed later.
Not critical -> Future
vasicvuk commentedon May 24, 2022
For server-side cases definitely it makes sense to have a configurable number of connections open. This way the load will be spread if you have multiple instances of some microservice or scaled app.
What about having Min and Max pool sizes as options (MinConnectionsPerServer, MaxConnectionsPerServer)?
Then at the start, it will open
MinConnectionsPerServerand round-robin through these connections, if some connection reachesMaxConcurrentStreamsthen it could open another one up untilMaxConnectionsPerServerare open.