-
Notifications
You must be signed in to change notification settings - Fork 10.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ASP.NET core GRPC server can terminate connections when MAX_CONCURRENT_STREAMS
is breached
#17484
Comments
From the logs the extra streams do appear to be handled correctly (REFUSED_STREAM), it's only the data frames that cause an issue. This looks like a duplicate of #13405 RE: gracefully draining data from refused or canceled streams.
How readily does this repro with the default value of 100? The spec discourages anything less: |
Yes, I thought of that issue as soon as I saw this. I wasn't aware the server terminated the connection when this happened. Killing the connection and all other streams on it in progress is a pretty big side-effect. |
Hey @Tratcher, your summary is correct- it is only the data frames that cause the issue. However, as the client seems to transmit the header + data frames almost simultaneously, it has no ability to react to the
This has been causing issues consistently in a production environment which uses the default value of 100- it was just easier to analyze the logs with the lower setting of |
On a single connection? @JamesNK Yeah, it's ugly. So far it's always been triggered by another bug so the triggers were higher priority to fix. This new scenario looks like the first trigger that isn't a bug itself. The client is aggressive but still technically within the spec. |
@Tratcher correct. To add some further details about our production setup:
|
How could that happen? The settings are negotiated at the beginning of the connection. Is the client not honoring the limit if there's a burst of requests? |
@Tratcher I did modify my above comment but I'll expand upon it here. When I say "idle" connections, I'm talking about TCP connections that have been terminated after a period of inactivity. In our production setup, our primary source of "idle" terminations come from the load balancer we use (it ignores TCP keepalives). When idle connections are closed, After some quick testing |
Ah, idle connections aren't really special then, they're just closed so a new one gets automatically created. I'm not aware of any provision in the spec for a client to cache settings across connections. It makes sense that both clients start with a clean slate on each connection. |
Is this 3.1 LTS worthy? It has real world impact. On the other hand I would guess the fix is non-trivial. What is the migration here? Increasing MAX_CONCURRENT_STREAMS to a very high number would do it, alternatively on the client you would need to count and balance calls between gRPC channels (each channel represents a connection). |
You'd have to throttle the client's concurrent requests. That should be pretty easy with a DelegatingHandler. We'll have to come up with a fix before we can evaluate the risk for backporting it. |
For what it's worth my workaround for the time being has been to establish a connection and then send periodic health check requests to keep the connection alive. It's not perfect but it's worked for us so far. @Tratcher is DelegatingHandler specific to |
Also keep in mind that 100 is the default stream limit for several other servers so this is not going to be an isolated issue. |
Originally created at grpc/grpc-dotnet#673 by @djluck
What version of gRPC and what language are you using?
grpc=v2.24.0
lang=C#
What operating system (Linux, Windows,...) and version?
Windows, v10.0.18362 Build 18362
What runtime / compiler are you using (e.g. .NET Core SDK version
dotnet --info
)v3.0.100
What did you do?
When issuing many requests on a fresh/ idle connection, it's possible to exceed the
MAX_CONCURRENT_STREAMS
setting of a server, causing the connection to fault and failing successfully processed streams.Exceeding the
MAX_CONCURRENT_STREAMS
on a fresh/ idle connection should be supported as the initial value is assumed to be unlimited until a peer has communicated it's prefered limit.This psuedo-code highlights how to re-create the issue on the client-side:
MAX_CONCURRENT_STREAMS
should be set to a low value in the ASP.NET grpc server (inappsettings.json
):What did you expect to see?
The streams that exceed the
MAX_CONCURRENT_STREAMS
limit should be closed and anyDATA
frames received by the server for these streams should result in aSTREAM_CLOSED
error as per the HTTP/2 RFC:What did you see instead?
Streams that exceed the
MAX_CONCURRENT_STREAMS
hadDATA
frames in-flight which resulted in the entire TCP connection being torn down:The impact of faulting the entire connection is that some requests may have successfully processed and their
CancellationToken
may not trigger- this means it's impossible to recover from this kind of error gracefully.Anything else we should know about your project / environment?
The text was updated successfully, but these errors were encountered: