How to start my Worker Service on the hour
Asked Answered
T

5

6

I have this worker service kicking off jobs but hourly it checks for jobs. How can I get it to check on the hour rather at the run time hourly?

public class WorkerService : BackgroundService
{
    private const int generalDelay = 20;  //minutes

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            await Task.Delay(generalDelay * 60000, stoppingToken);
            await DoBackupAsync();
        }
    }

    private static Task DoBackupAsync()
    {
        DoWork d = new DoWork();
        return Task.FromResult("Done");
    }
}

Mainly for this I know when jobs will run and can predict as well as scheduling my updates in between the run times and if all jobs complete.

Toandfro answered 15/4, 2022 at 12:18 Comment(1)
Use a scheduler? Like quartz.net?Pazia
P
10

For this kind of requirement, a full-blown scheduler may be overkill. I do recommend using a cron expression, but I prefer Cronos over NCrontab. Cronos has better tests and well-defined behavior regarding UTC and daylight saving time transitions.

This is what it would look like using Cronos:

public class WorkerService : BackgroundService
{
  private const string schedule = "0 * * * *"; // every hour
  private readonly CronExpression _cron;

  public WorkerService()
  {
    _cron = CronExpression.Parse(schedule);
  }

  protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  {
    while (!stoppingToken.IsCancellationRequested)
    {
      var utcNow = DateTime.UtcNow;
      var nextUtc = _cron.GetNextOccurrence(utcNow);
      await Task.Delay(nextUtc.Value - utcNow, stoppingToken);
      await DoBackupAsync();
    }
  }
}
Pasteboard answered 15/4, 2022 at 15:23 Comment(2)
I use UTC and got this in there no problem. Both good libraries it seems but implementing and UTC/DST bonus. ThanksToandfro
Hi Stephen! FYI the maximum year supported by the Cronos library is the 2099. I got this information yesterday, by the owner of the library Sergey Odinokov. Link here.Ademption
B
2

You might look for a scheduler as @Paweł Łukasik say, which is very suitable to use the crontab format.

quartz was a powerful library which good to control the job time, but quartz need to cooperate with its library architecture

If your code is huge and difficult to modify, I would suggest you use a light way library NCrontab that can only get the time by crontab.

0 * * * *

Then you can calculate next the hour by GetNextOccurrence method.

public class WorkerService : BackgroundService
{
    const string EveryHours = "0 * * * *";
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var schedule = CrontabSchedule.Parse(EveryHours);
        while (!stoppingToken.IsCancellationRequested)
        {
            var now = DateTime.Now;
            DateTime nextExecutionTime = schedule.GetNextOccurrence(now);
            await Task.Delay((nextExecutionTime - now).Milliseconds, stoppingToken);
            await DoBackupAsync();
        }
    }
    private static Task DoBackupAsync()
    {
        DoWork d = new DoWork();
        return Task.FromResult("Done");
    }
}
Bronwyn answered 15/4, 2022 at 12:46 Comment(2)
I like this approach. This is great. ThanksToandfro
@Bronwyn is a error in your delay. Should be Total Milliseconds used await Task.Delay((nextExecutionTime - now).TotalMilliseconds, stoppingToken);Gobioid
W
0

If you want to check the current hour you can use var datetime = DateTime.UtcNow which will take the current DateTime, and then you can use datetime.Hour to get the current hour value so you can use it to check.

Whoremaster answered 15/4, 2022 at 12:37 Comment(0)
A
0

You could use the WaitUntilNextHourAsync method below:

public static Task WaitUntilNextHourAsync(
    CancellationToken cancellationToken = default)
{
    DateTime now = DateTime.Now;
    DateTime next = now.Date.AddHours(now.Hour + 1);
    Debug.Assert(next > now);
    TimeSpan delay = next - now;
    Debug.Assert(delay > TimeSpan.Zero && delay.TotalHours <= 1);
    if (delay.TotalSeconds < 1) delay += TimeSpan.FromHours(1);
    return Task.Delay(delay, cancellationToken);
}

Usage example:

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    while (true)
    {
        await WaitUntilNextHourAsync(stoppingToken);
        await DoBackupAsync();
    }
}
Ademption answered 15/4, 2022 at 14:6 Comment(0)
U
0

I was starting off with something similar to the accepted answer - you probably don't need anything more. However, I was ending up sharing my code as a github/nuget project. It's by far more basic (and easy to use) than Quartz.NET and other scheduling frameworks. Give it a try and let me know via github issues if something doesn't work as expected.

The source code repository: https://github.com/thomasgalliker/NCrontab.Scheduler

The nuget download: https://www.nuget.org/packages/NCrontab.Scheduler

Enjoy.

Unworldly answered 23/4, 2022 at 13:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.