using Microsoft.Extensions.Caching.Memory;
using System.Runtime.CompilerServices;
namespace Remotely.Shared.Helpers;
public static class RateLimiter
{
private static readonly MemoryCache _cache = new(new MemoryCacheOptions());
private static readonly SemaphoreSlim _cacheLock = new(1, 1);
///
/// Clears the RateLimiter cache so it is reset to a fresh state.
///
///
///
public static async Task Reset(CancellationToken cancellationToken = default)
{
await _cacheLock.WaitAsync(cancellationToken);
try
{
_cache.Clear();
}
finally
{
_cacheLock.Release();
}
}
///
///
/// Throttles a given func so it is only called once every duration.
///
///
/// The key used to identify and debounce the action will be derived from
/// the caller information. Those parameters will be populated automatically
/// and can be left empty.
///
///
public static async Task Throttle(
Func func,
TimeSpan duration,
[CallerMemberName] string callerMemberName = "",
[CallerFilePath] string callerFilePath = "",
[CallerLineNumber] int callerLineNumber = -1,
CancellationToken cancellationToken = default)
{
var key = $"{callerMemberName}-{callerFilePath}-{callerLineNumber}";
await Throttle(func, duration, key, cancellationToken);
}
///
///
/// Throttles a given func so it is only called once every duration.
///
///
/// The provided key will be used to identify and throttle the func.
///
///
public static async Task Throttle(
Func func,
TimeSpan duration,
string key,
CancellationToken cancellationToken = default)
{
await _cacheLock.WaitAsync(cancellationToken);
try
{
if (_cache.TryGetValue(key, out _))
{
return;
}
await func.Invoke();
_cache.Set(key, string.Empty, duration);
}
finally
{
_cacheLock.Release();
}
}
}