1

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.

0 Answers0