47

Is there a way to set hangfire recurring jobs every few seconds? I do not seek a solution where fire and forget task creates another fire and forget task, and if not, what are suggested alternatives?

Robert
  • 3,353
  • 4
  • 32
  • 50
  • Regarding [this](http://docs.hangfire.io/en/latest/background-methods/performing-recurrent-tasks.html) documentation it does not seem possible to create recurring tasks on a second-base. The least minimum is [minutes](https://en.wikipedia.org/wiki/Cron#CRON_expression) – ckruczek Jul 14 '16 at 07:07

7 Answers7

65

Not sure when this became supported but tried this in ASP.NET Core 2.0 with Hangfire 1.7.0. The following code schedules a job every 20 seconds:

RecurringJob.AddOrUpdate<SomeJob>(
    x => x.DoWork(),
    "*/20 * * * * *");

If I am not mistaken 6 tokens (as opposed to standard 5 tokens) is supported due to Hangfire use of NCrontab which allows cron expressions with 6 tokens (second granularity instead of minute granularity).

Hangfire dashboard also nicely shows the small time interval between runs:

Hangfire dashboard

Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
31

I think anyone who is against allowing a recurring trigger of less than 1 min is short sighted. After all, is 55 secs any less efficient than 1 min ? It seems so arbitrary! As much as I love Hangfire, I've encountered situations where I've had to steer a client to Quartz.net simply because there was a business requirement for a job to run every 55 secs or alike.

Anyone who makes the counter argument that if it was configured to run every 1sec it would have a serious impact on performance is again taking a closed view on things. Of course a trigger with a 1 sec interval is probably not a good idea, but do we dissalow 55 sec or 45 sec for the unlikely situation where someone will choose 1 sec ?

In any case, performance is both subjective and dependent on the host platform and hardware. It really isn't up to the API to enforce opinion when it comes to performance. Just make the polling interval and trigger recurrence configurable. That way the user can determine the best result for themselves.

Although a background process which is orchestrating a job to run every 55 sec may be an option, it isn't very satisfactory. In this case, the process isn't visible via the Hangfire UI so it's hidden from the administrator. I feel this approach is circumventing one of the major benefits of Hangfire.

If Hangfire was a serious competitor to the likes of Quartz.net it would at least match their basic functionality. If Quartz can support triggers with an interval below 1 min than why can't Hangfire!

Yandry Pozo
  • 4,851
  • 3
  • 25
  • 27
PatrickNolan
  • 1,671
  • 2
  • 20
  • 40
16

Although Hangfire doesn't allow you to schedule tasks for less than a minute, you can actually achieve this by having the function recursively scheduling itself; i.e. let's say you want some method to be hit every 2s you can schedule a background job that calls the method on Startup;

BackgroundJob.Schedule(() => PublishMessage(), TimeSpan.FromMilliseconds(2000));

And then in your PublishMessage method do your stuff and then schedule a job to call the same method;

public void PublishMessage()
    {
        /* do your stuff */

        //then schedule a job to exec the same method
        BackgroundJob.Schedule(() => PublishMessage(), TimeSpan.FromMilliseconds(2000));
    }

The other thing you need to override is the default SchedulePollingInterval of 15s, otherwise your method will only be hit after every 15s. To do so just pass in an instance of BackgroundJobServerOptions to UseHangfireServer in your startup, like so;

var options = new BackgroundJobServerOptions
        {
            SchedulePollingInterval = TimeSpan.FromMilliseconds(2000)
        };

        app.UseHangfireServer(options);

I don't know how "foolproof" my solution is, but I managed to achieve my goal with it and everything is "happy" in production.

takaz
  • 201
  • 2
  • 6
  • It is already stated in the question that tasks which are creating another tasks is not desired answer. I guess this is not possible by design, and that is it – Robert Oct 04 '17 at 11:42
9

I had to do the same but with 5 seconds. The default schedule polling interval is set to 15s. So it requires 2 steps to achieve a 5s interval job.

in Startup.cs

var options = new BackgroundJobServerOptions
{
    SchedulePollingInterval = TimeSpan.FromMilliseconds(5000)
};

app.UseHangfireDashboard();
app.UseHangfireServer(options);

Your job

RecurringJob.AddOrUpdate(() => YourJob(), "*/5 * * * * *");
WtFudgE
  • 5,080
  • 7
  • 47
  • 59
4

Hangfire doesn't support intervals of less than a minute for recurring jobs.

Why? Imagine if they allowed less than a minute: let say 1 sec. How frequently would hangfire check recurring jobs in the database? This would cause a lot of database IO.

See this discussion on Hangfire for more information.

Jules
  • 1,677
  • 1
  • 19
  • 25
jtabuloc
  • 2,479
  • 2
  • 17
  • 33
0

I faced with the same problem, and here it is my solution:

private void TimingExecuteWrapper(Action action, int sleepSeconds, int intervalSeconds)
{
    DateTime beginTime = DateTime.UtcNow, endTime;
    var interval = TimeSpan.FromSeconds(intervalSeconds);
    while (true)
    {
        action();
        Thread.Sleep(TimeSpan.FromSeconds(sleepSeconds));
        endTime = DateTime.UtcNow;
        if (endTime - beginTime >= interval)
            break;
    }
}

intervalSeconds is minimal NCRON interval. It is 1 minute. action is our job code. Also I suggest to use DisableConcurrentExecution to avoid some collisions of concurrency.

daniil_
  • 707
  • 7
  • 26
0

I had a similar requirement, in that I had a recurring job that needs running every 15 seconds. What I did to try to get around this limitation was to just delay the creation of the scheduled jobs (set to 1 minute intervals), which seemed to do the trick. However what I found was that, taking into account the polling intervals (set the schedulepolling interval to my frequency) and delays in picking up the new jobs this isn't always as accurate as it should be, but is doing the trick for the moment. However a better/proper solution would be good.

feel a bit dirty having to resolve to the below approach, but it helped me out... so in essence I created 4 jobs doing the same thing, created 15 seconds apart. along the lines of:

...

new Thread(() =>
{
    //loop from {id=1 through 4}
    //   create job here with {id} in the name at interval of 1 minute
    //   sleep 15 seconds
    //end loop
}).Start();    
...
Francois
  • 92
  • 9