Adjusted cache to allow for throttling and locking.

This commit is contained in:
2025-02-26 23:10:36 -05:00
parent 6835431d1e
commit c1a7d23096
5 changed files with 89 additions and 31 deletions

View File

@@ -1,29 +1,88 @@
namespace Harmonia.Core.Caching;
using System.Collections.Concurrent;
namespace Harmonia.Core.Caching;
public abstract class Cache<TKey, TValue> : ICache<TKey, TValue> where TKey : notnull
{
private readonly ConcurrentDictionary<object, SemaphoreSlim> _locks;
private readonly SemaphoreSlim _throttler;
protected abstract int MaxConcurrentRequests { get; }
protected abstract TValue? TryGetValue(object key);
protected abstract TValue? Fetch(TKey key);
protected abstract ValueTask<TValue?> FetchAsync(TKey key, CancellationToken cancellationToken);
protected abstract void AddToCache(object key, TValue value);
public TValue? Get(TKey key)
public Cache()
{
object? actualKey = GetKey(key);
if (actualKey == null)
return default;
return TryGetValue(actualKey) ?? Refresh(key);
_locks = new();
_throttler = new SemaphoreSlim(MaxConcurrentRequests, MaxConcurrentRequests);
}
public TValue? Refresh(TKey key)
public async Task<TValue?> GetAsync(TKey key, CancellationToken cancellationToken)
{
object? actualKey = GetKey(key);
if (actualKey == null)
return default;
TValue? value = Fetch(key);
TValue? value = TryGetValue(actualKey);
if (value != null)
return value;
SemaphoreSlim lockObject = _locks.GetOrAdd(actualKey, (key) => new SemaphoreSlim(1, 1));
try
{
await _throttler.WaitAsync(cancellationToken);
if (cancellationToken.IsCancellationRequested)
return default;
await lockObject.WaitAsync(cancellationToken);
if (cancellationToken.IsCancellationRequested)
return default;
return TryGetValue(actualKey) ?? await FetchAsync2(key, cancellationToken);
}
finally
{
lockObject.Release();
_locks.TryRemove(lockObject, out _);
_throttler.Release();
}
//return TryGetValue(actualKey) ?? Refresh(key);
}
private async Task<TValue?> FetchAsync2(TKey key, CancellationToken cancellationToken)
{
object? actualKey = GetKey(key);
if (actualKey == null)
return default;
TValue? value = await FetchAsync(key, cancellationToken);
if (value == null)
return default;
AddToCache(actualKey, value);
return value;
}
public async Task<TValue?> RefreshAsync(TKey key, CancellationToken cancellationToken)
{
object? actualKey = GetKey(key);
if (actualKey == null)
return default;
TValue? value = await FetchAsync(key, cancellationToken);
if (value == null)
return default;