I have a InMemory Cache method which I want to implement using Redis Cache, it uses the concept of Semaphore in the InMemory Cache, hence I have gone through many documentation's like the Redis documentation on Distributed Locking, and tried using different locking mechanisms in Redis such as RedLock.Net , RedLock-cs etc, but I haven't been able to completely go through with the technique, as even though I am able to recreate the method, but the running time for large number of processes for it in Redis is coming out to be huge.
The code that I am trying to recreate is as follows:
private readonly ConcurrentDictionary<string, Lazy<SemaphoreSlim>> _semaphores = new ConcurrentDictionary<string, Lazy<SemaphoreSlim>>();
public async ValueTask<TValue> GetMethod<TValue>(string key,Func<Task<TValue>> get,int time)
{
return await SemaphoreMethod(key, async() =>
{
var value = await get();
if(value != null)
cache.set(key,value,time);
return value;
});
}
private async ValueTask<TValue> SemaphoreMethod<TValue>(string key,Func<Task<TValue>> get)
{
if(cache.TryGetValue(key,out TValue result))
return result;
var semaphore = _semaphores.GetOrAdd(key,k => new Lazy<SemaphoreSlim>(() => new SemaphoreSlim(1,1))).Value;
await semaphore.WaitAsync();
TValue answer = default;
if(cache.TryGetValue(key,out TValue result2))
return result2;
answer = await get();
_semaphores.TryRemove(key,out _);
semaphore.Release();
return answer;
}
And the method which I run against it to check it's implementation and efficiency in comparison to InMemory Cache is as follows:
public async Task GetValueAsync_Tasks()
{
var callCount = 0;
async Task<string> Get()
{
Interlocked.Increment(ref callCount);
return "value";
}
const int count = 1000;
var valueTasks = new List<ValueTask<string>>(count);
for(var i=0;i<count;i++)
valueTask.Add(GetMethod("key",Get));
var tasks = valueTasks.Select(vt => vt.AsTask()).ToArray();
Task.WaitAll(tasks);
callCount.Should().Be(1);
}
One other thing that I am not able to understand that in which part of the test method, would the code run all the tasks created, i.e. in this one
valueTasks.Add(sut.GetValueAsync("key", FetchValue));
or in this one
Task.WaitAll(tasks);
I am using the StackExchange.Redis library, and Azure Cache for Redis for storing the respective data.