Skip to content

Rate Limit for Kestrel - Design mechanism to apply back pressure to accepting connections #13295

Open
@mconnew

Description

@mconnew

Is your feature request related to a problem? Please describe.

To match functionality with WCF on .NET Framework, CoreWCF needs to be able to control the number of pending accepts. When a client connects to WCF using the NetTcp transport, there is a handshake that needs to be completed which includes authentication and security upgrade negotiation. After a connection has been established and while the handshake is ongoing, a connection is considered to be a pending connection. WCF controls the maximum number of connections which are in the pending connection state by controlling the maximum number of pending accepts to never be more than how many more pending connections are possible. For example, if the maximum number of pending connections is configured to be 100 and there are currently 95 pending connections, we limit the number of pending accepts to be no more than 5.

Describe the solution you'd like

I believe there should be a generic throttling interface that can be added by DI for use at any layer within an app, and a derived interface (which adds no extra methods) which is used at the transport layer. The two interfaces would be defined as:

public interface IThrottle
{
    ValueTask<bool> AcquireAsync(CancellationToken cancellationToken = default(CancellationToken));
    void Release();
}

public interface ITransportThrottle : IThrottle { }

When calling AcquireAsync, one of four things can happen.

  1. The returned ValueTask is completed with a value of true meaning the throttle was acquired.
  2. The returned ValueTask is not completed and the call goes async. When a throttle can be acquired, the ValueTask is completed with a value of true meaning the throttle was acquired.
  3. The returned ValueTask is completed with a value of false meaning the throttle can not be acquired immediately. This would be used for a throttle implementation which is used when you want to fail immediately such as returning a 503 throttled HTTP response.
  4. The returned ValueTak is not completed and the call goes async. If the cancellation token is cancelled, the ValueTask is completed with a value of false meaning the throttle was not acquired. This would be used for scenarios where waiting for a throttle can time out or when shutting down the application and you need to complete the pending AcquireAsync call for cleanup.

In the code which calls Accept on the listening socket, if an ITransportThrottleexists in DI, in a loop it acquires the throttle and adds another pending accept until ITransportThrottle.AcquireAsync returns a non-completed ValueTask. It would be up to the implementation of the ITransportThrottleto ensure there's a sensible number of pending accepts. Basically the number of pending accepts is completely controlled by the ITransportThrottleif it exists.

Edit: Changed IServiceThrottle to ITransportThrottle

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs: DesignThis issue requires design work before implementating.Priority:1Work that is critical for the release, but we could probably ship withoutaffected-very-fewThis issue impacts very few customersarea-middlewareIncludes: URL rewrite, redirect, response cache/compression, session, and other general middlewaresarea-networkingIncludes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractionsblockedThe work on this issue is blocked due to some dependencyenhancementThis issue represents an ask for new feature or an enhancement to an existing onefeature-rate-limitWork related to use of rate limit primitivesfeature-yarpThis issue is related to work on yarpseverity-nice-to-haveThis label is used by an internal tool🥌 Bedrock

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions