Skip to content

Instantly share code, notes, and snippets.

@Brom95
Created August 1, 2022 10:13
Show Gist options
  • Save Brom95/c4736aae78f870785ed19c098d3f86ca to your computer and use it in GitHub Desktop.
Save Brom95/c4736aae78f870785ed19c098d3f86ca to your computer and use it in GitHub Desktop.
using Microsoft.Extensions.Logging;
namespace RateLimiterCore;
public class RateLimiter<T> : IRateLimiter<T>, IDisposable
{
private int count = 0;
private Timer windowTimer;
private readonly TimeSpan windowSize;
private static readonly RateLimiterOptions DefaultOptions = new();
private uint maxConcurrentRequests;
private readonly CancellationTokenSource cancellationTokenSource;
private readonly ILogger<RateLimiter<T>> logger;
public RateLimiter(IRateLimiterOptions? options = null, ILogger<RateLimiter<T>> logger = null)
{
options ??= DefaultOptions;
windowSize = options.WindowSize;
maxConcurrentRequests = options.ConcurrentRequests;
cancellationTokenSource = new CancellationTokenSource();
this.logger = logger;
}
public async Task<Result<T>> Invoke(Func<Task<T>> action, CancellationToken cancellationToken)
{
windowTimer ??= new Timer((_) => Interlocked.Exchange(ref count, 0), null, windowSize, windowSize);
int initialValue, computedValue;
do
{
initialValue = count;
if (initialValue < maxConcurrentRequests && !cancellationTokenSource.IsCancellationRequested)
{
computedValue = initialValue + 1;
}
else
{
// RateLimiter<T>.Log($"Fail");
logger?.LogDebug("Fail");
return Result<T>.Fail();
}
}
while (initialValue != Interlocked.CompareExchange(ref count, computedValue, initialValue));
var task = Task.Run(action, cancellationToken);
logger?.LogInformation("Success {task} Count: {count}", task.Id, count);
return Result<T>.Success(await task);
}
public void Dispose()
{
maxConcurrentRequests = 0;
cancellationTokenSource.Cancel();
windowTimer.Dispose();
}
}
public interface IRateLimiterOptions
{
uint ConcurrentRequests { get; set; }
TimeSpan WindowSize { get; set; }
}
public class RateLimiterOptions : IRateLimiterOptions
{
public uint ConcurrentRequests { get; set; } = 5;
public TimeSpan WindowSize { get; set; } = TimeSpan.FromSeconds(5);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment