Following is my problem.
I have an API controller with an API endpoint inside of it (the resource).
api/myResource/{id}/do-something
I'm working on a middleware, that will limit access to this resource based upon some business rules. Inside this middleware, I'm matching the incoming request, I'm parsing the URI and I want to allow access to it (let the pipeline flow) or simply return with a 412 status code in case the limit of allowed threads is reached FOR THE GIVEN RESOURCE (e.g)
api/myResource/1/do-something /// should allow 2 concurrent accesses.
api/myResource/2/do-something /// should allow 10 concurrent accesses.
api/myResource/3/do-something /// should allow 1 concurrent accesses.
For that I've started implementing a solution which I will attach.
internal class AsyncLock<TKey>
{
private static readonly ConcurrentDictionary<TKey, SemaphoreSlim> _safeSemaphores
= new ConcurrentDictionary<TKey, SemaphoreSlim>();
internal async Task<SemaphoreSlim> TryLockAsync(TKey key, int maxConcurrentCount)
{
if (!_safeSemaphores.TryGetValue(key, out SemaphoreSlim semaphore))
{
semaphore = new SemaphoreSlim(maxConcurrentCount, maxConcurrentCount);
_safeSemaphores.TryAdd(key, semaphore);
}
await semaphore.WaitAsync();
return semaphore;
}
internal SemaphoreSlim TryLock(TKey key, int maxConcurrentCount)
{
if (!_safeSemaphores.TryGetValue(key, out SemaphoreSlim semaphore))
{
semaphore = new SemaphoreSlim(maxConcurrentCount, maxConcurrentCount);
_safeSemaphores.TryAdd(key, semaphore);
}
semaphore.Wait();
return semaphore;
}
}
This is how it is used (it allows for 2 concurrent accesses, of course this is the subject of this question, it is not going to be a hardcoded 2 but determined earlier in the pipeline)
AsyncLock<string> _lock = new AsyncLock<string>();
SemaphoreSlim semaphore = await _lock .TryLockAsync(key, 2);
if (semaphore.CurrentCount != 0)
{
context.Response.StatusCode = 200;
//await _next(context);
semaphore.Release();
}
else
{
context.Response.StatusCode = 412;
}
How it behaves is unpredictable. I'm testing using 4 threads, sometimes it works as expected, sometimes they all return 200, other times they all get stuck, I mean it is a combination every time.
I would really appreciate some help figuring this one out.