59 lines
2 KiB
C#
59 lines
2 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
|
|
namespace Mag.Shared
|
|
{
|
|
public class RateLimiter
|
|
{
|
|
public readonly int MaxNumberOfEvents;
|
|
private readonly double overPeriodInSeconds;
|
|
private readonly double targetEventSpacingInSeconds;
|
|
|
|
private readonly Stopwatch stopwatch = Stopwatch.StartNew();
|
|
|
|
public RateLimiter(int maxNumberOfEvents, TimeSpan overPeriod)
|
|
{
|
|
if (maxNumberOfEvents <= 0)
|
|
throw new ArgumentOutOfRangeException(nameof(maxNumberOfEvents), $"{nameof(maxNumberOfEvents)} must be greater than 0");
|
|
|
|
if (overPeriod <= TimeSpan.Zero)
|
|
throw new ArgumentOutOfRangeException(nameof(overPeriod), $"{nameof(overPeriod)} must be greater than TimeSpan.Zero");
|
|
|
|
MaxNumberOfEvents = maxNumberOfEvents;
|
|
overPeriodInSeconds = overPeriod.TotalSeconds;
|
|
|
|
targetEventSpacingInSeconds = overPeriodInSeconds / maxNumberOfEvents;
|
|
}
|
|
|
|
|
|
private int numberOfEventsRegistered;
|
|
|
|
/// <summary>
|
|
/// Result > 0 : We're able to meet our target rate and must pause between events.<para />
|
|
/// Result = 0 : We're running right no time. A new event should be registered without delay.<para />
|
|
/// Result < 0 : We're running behind. A new event should be registered without delay. We're failing to meet our target rate.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public double GetSecondsToWaitBeforeNextEvent()
|
|
{
|
|
var elapsedSeconds = stopwatch.Elapsed.TotalSeconds;
|
|
|
|
return ((targetEventSpacingInSeconds * numberOfEventsRegistered) - elapsedSeconds);
|
|
}
|
|
|
|
public void RegisterEvent()
|
|
{
|
|
numberOfEventsRegistered++;
|
|
|
|
var elapsedSeconds = stopwatch.Elapsed.TotalSeconds;
|
|
|
|
if (numberOfEventsRegistered > MaxNumberOfEvents || elapsedSeconds > overPeriodInSeconds)
|
|
{
|
|
numberOfEventsRegistered = 1;
|
|
|
|
stopwatch.Reset();
|
|
stopwatch.Start();
|
|
}
|
|
}
|
|
}
|
|
}
|