Click here to Skip to main content
15,885,914 members
Articles / Programming Languages / C#
Tip/Trick

C#: Interval Helper

Rate me:
Please Sign up or sign in to vote.
2.64/5 (4 votes)
31 Jul 2019CPOL1 min read 7.4K   88   3   4
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:

C#
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:

C#
/// <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.

C#
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/

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Bangladesh Bangladesh
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionCheck this one out for RateLimits its very good Pin
Sacha Barber2-Aug-19 3:12
Sacha Barber2-Aug-19 3:12 
AnswerRe: Check this one out for RateLimits its very good Pin
DiponRoy5-Aug-19 18:16
DiponRoy5-Aug-19 18:16 
Nice, Thank you!
QuestionApologies, my spelling-OCD has kicked in Pin
Member 145399882-Aug-19 0:51
Member 145399882-Aug-19 0:51 
AnswerRe: Apologies, my spelling-OCD has kicked in Pin
DiponRoy13-Aug-19 21:20
DiponRoy13-Aug-19 21:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.