Search code examples
javascriptc#asp.netdotnet-httpclientpolly

Polly Bulkhead unexpected behavior when using in ASP.NET Core


This is my bulkhead policy:

BulkheadPolicy = Policy
    .BulkheadAsync(maxParallelization: 2,
        maxQueuingActions: 2, onBulkheadRejectedAsync: (context) =>
        {
            Console.WriteLine("Rejected");
            return Task.CompletedTask;
        });         

It should allow only 4 concurrent tasks to execute/wait for execution.
However, I end up getting 6 tasks being able to run through the bulkhead policy.

This is my controller action:

[HttpGet("bulkhead")]
public async Task<object> Bulkhead()
{
    Console.WriteLine(DateTime.UtcNow);

    var bhCount = _policyManager.BulkheadPolicy.BulkheadAvailableCount;
    var queueCount = _policyManager.BulkheadPolicy.QueueAvailableCount;

    // The policy is used here
    await _policyManager.BulkheadPolicy.ExecuteAsync(async (context) =>
    {
        await Task.Delay(10000);
    }, new Context());

    return new
    {
        bhCount,
        queueCount
    };
}

In my browser, I used this script to simulate 14 concurrent tasks:

for (var i = 0; i < 14; i++) {
    fetch('https://localhost:44313/api/Bulkheads/bulkhead')
        .then(result=>console.log(result));
}

which should produce only 4 200OK responses. However, I end up getting 6 200OK responses. I ran the test many times and got the same result. Does anybody have an idea why since the same behavior works well with a console application? Did I miss something here? Many thanks.

UPDATE 1: This is my IPolicyManager interface

    public interface IPolicyManager
    {
        AsyncBulkheadPolicy BulkheadPolicy { get; }
    }

    public class PolicyManager : IPolicyManager
    {
        public PolicyManager()
        {
             Init();
        }

        public AsyncBulkheadPolicy BulkheadPolicy { get; private set; }

        private void Init()
        {
            BulkheadPolicy = Policy
                .BulkheadAsync(maxParallelization: 2,
                    maxQueuingActions: 2, onBulkheadRejectedAsync: (context) =>
                    {
                        Console.WriteLine("Rejected");
                        return Task.CompletedTask;
                    });
        }
    }

UPDATE 2: When I send a fixed request (fixed URL), it failed quite slow, but when I append a random argument in the requests, the result is as expected (fail fast and only 4 requests can proceed).

for (var i = 0; i < 14; i++) {
    fetch('https://localhost:44313/api/Bulkheads/bulkhead?count=' + i)
        .then(result=>console.log(result));
}

Solution

  • It turned out that the browser's behavior was not as expected. I wrote a console application that does the same task and it works well: fail fast and only 4 requests can proceed at a time. So I can confirm this is not an issue from Polly or ASP.NET Core. Maybe it relates to caching mechanism of web browser (I'm using Edge): https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching