Last active
February 20, 2019 15:52
-
-
Save edamtoft/9a72cc535156b9955e00233083b27750 to your computer and use it in GitHub Desktop.
Rate Limiting for X-Requests-per-Y-Timespan APIs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface IRateLimiter | |
{ | |
Task<T> RunRateLimited<T>(Func<Task<T>> func); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public sealed class SemaphoreRateLimiter : IRateLimiter, IDisposable | |
{ | |
private readonly SemaphoreSlim _semaphore; | |
private readonly TimeSpan _span; | |
public SemaphoreRateLimiter(int count, TimeSpan timespan) | |
{ | |
_semaphore = new SemaphoreSlim(count); | |
_span = timespan; | |
} | |
public async Task<T> RunRateLimited<T>(Func<Task<T>> func) | |
{ | |
// Conceptually, this has a semaphore with a lock with the configured | |
// number of "slots". Once a lock is aquired, that "slot" enters a | |
// refactory period for the allotted period of time, allowing no more than | |
// the confiured number of requests within a moving time window. | |
await AquireLock(); | |
ScheduleRelese(); | |
return await func(); | |
} | |
Task AquireLock() => _semaphore.WaitAsync(); | |
async void ScheduleRelese() | |
{ | |
// Normally async voids functions are a bad pattern because you cannot wait for the work to complete | |
// and any expections basically dissapear into the ether, but in this case, we are basically using it | |
// as a lazy timer and deliberately firing this off with no expectation of waiting for completion. | |
await Task.Delay(_span); | |
_semaphore.Release(); | |
} | |
public void Dispose() => _semaphore.Dispose(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment