Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

C#: Interval Helper

0.00/5 (No votes)
31 Jul 2019 1  
Calling an API which has a limit of calls per second

 

Background

I was calling a private API from C#, which had an API limit of 2 calls per second for each user. So there was a chance of getting "Rate Limit Reached Exception" if the API was being called more then twice in a second. That's why before making an API call, we had to make sure that the time interval between two call start is at least 500 milliseconds.

We were not just making API calls. After each call, we had to process a few other things. To make things manageable, we used a simple interval helper, which provided us the expected waiting time. Later that waiting time had been passed to Thread.Sleep(), that's it. Let's check the time interval helper class and its usages.

Interval Helper Class

Here is the interface:

public interface IIntervalHelper<TInterval>
{
    /// <summary>
    /// Expected Interval
    /// </summary>
    TInterval Interval();

    /// <summary>
    /// Process start time, assigned at Begin method
    /// </summary>
    DateTime StartDateTime();

    /// <summary>
    /// Set interval
    /// </summary>
    /// <param name="interval">Time</param>
    void SetInterval(TInterval interval);

    /// <summary>
    /// Start process time count, will set startDateTime
    /// </summary>
    /// <param name="startDateTime">Default DateTime.Now</param>
    void Begin(DateTime? startDateTime = null);

    /// <summary>
    /// Get passed time
    /// </summary>
    /// <param name="endDateTime">Should be greater than startDateTime, 
    /// Default DateTime.Now</param>
    /// <returns>Time</returns>
    TInterval PassedTime(DateTime? endDateTime = null);

    /// <summary>
    /// Get remaining time
    /// </summary>
    /// <param name="endDateTime">Should be greater than startDateTime, 
    /// Default DateTime.Now</param>
    /// <returns>Time</returns>
    TInterval RemainingTime(DateTime? endDateTime = null);
}

Using the interface to create the main helper class:

/// <summary>
/// TimeSpan interval helper class
/// </summary>
public class TimeSpanIntervalHelper : IIntervalHelper<TimeSpan>
{
    private TimeSpan _interval;
    private DateTime _startDateTime;

    public TimeSpanIntervalHelper(TimeSpan interval)
    {
        SetInterval(interval);
    }

    public TimeSpan Interval()
    {
        return _interval;
    }

    public DateTime StartDateTime()
    {
        return _startDateTime;
    }

    public void SetInterval(TimeSpan interval)
    {
        _interval = interval;
    }

    public void Begin(DateTime? startDateTime = null)
    {
        _startDateTime = startDateTime ?? DateTime.Now;
    }

    public TimeSpan PassedTime(DateTime? endDateTime = null)
    {
        DateTime dateTime = endDateTime ?? DateTime.Now;
        if (dateTime < _startDateTime)
        {
            throw new ArgumentException("EndDateTime should n't be less then StartDateTime");
        }
        TimeSpan difference = dateTime - _startDateTime;
        return difference;
    }

    public TimeSpan RemainingTime(DateTime? endDateTime = null)
    {
        DateTime dateTime = endDateTime ?? DateTime.Now;
        TimeSpan passedTime = PassedTime(dateTime);
        TimeSpan remainingTime = _interval - passedTime;
        return remainingTime;
    }
}

Using the Helper Class

Here, we are setting the interval to 500 milliseconds.

TimeSpan interval = new TimeSpan(0, 0, 0, 0, 500);
var intervalHelper = new TimeSpanIntervalHelper(interval);

for (int i = 0; i < 10; i++)
{
    intervalHelper.Begin();

    /*
     * doing jobs or calling web api as needed for each item
     */

    /*at end of each process*/
    DateTime endDateTime = DateTime.Now;
    TimeSpan value = intervalHelper.PassedTime(endDateTime);
    value = intervalHelper.RemainingTime(endDateTime);

    /*we can also do
    value = intervalHelper.PassedTime();
    value = intervalHelper.RemainingTime();
    */

    /*wait and do job again*/
    Thread.Sleep(value);
}

For more, do check the TimeSpan_Use() test method.

Other Options

RateGate

As Sacha Barber suggested, Rate Limiting is a great one to be used. It is packed with a lot of options. It is also available on NuGet.

Nuget: https://www.nuget.org/packages/JackLeitch.RateGate/

using (var rateGate = new RateGate(2, TimeSpan.FromSeconds(1)))
{
    for (var i = 0; i < 10; i++)
    {
        /*
        * doing jobs as needed for each item
        */
        rateGate.WaitToProceed();
    }
}

Although I wasn't been able to do proper unit testing.

Do Check!!!

Inside the source code, we will find another helper class IntervalHelper which manages things only in milliseconds. The usages are the same as the TimeSpanIntervalHelper. Find usages code inside MilliSecond_Use() test method.

History

  • 31st July, 2019: Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here