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;
///
/// Result > 0 : We're able to meet our target rate and must pause between events.
/// Result = 0 : We're running right no time. A new event should be registered without delay.
/// Result < 0 : We're running behind. A new event should be registered without delay. We're failing to meet our target rate.
///
///
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();
}
}
}
}