Hangfire single instance recurring job
Asked Answered
P

2

19

I am trying to use Hangfire to run a recurring job in the background that polls data from another website, the issue is that I don't want the recurring job to run if the previous job is still running.

I've read through the documentation but can't seem to find the answer. Is there a way to have a recurring job that runs every 10 minutes but skips if the previous task is not done yet?

public void Configuration(IAppBuilder app)
{
    app.MapSignalR();

    // Hangfire
    GlobalConfiguration.Configuration
        .UseSqlServerStorage("DatabaseContext");

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

    RecurringJob.AddOrUpdate("site-parser", () => SiteParserService.RunAll(), Cron.Minutely, TimeZoneInfo.Utc);

    ConfigureAuth(app);
}
Peirsen answered 11/6, 2015 at 17:31 Comment(2)
Here is a suggestion: Simply queue the job as a single instance. Then, if the job completes successfully, you execute a final step in the job. The final step of your job will queue the job to execute again in 10 minutes. Won't that provide you the functionality you need?Tyr
Do you mean the last execution of the same recurring task?Pericardium
S
10

You can use; [DisableConcurrentExecution(10 * 60)] attribute on the job method.

Here you can find information about this attribute: http://odinserj.net/2014/05/21/hangfire-0.8.2-released/

Smoothbore answered 18/8, 2016 at 16:29 Comment(0)
W
1

My solution:

namespace MyNameSpace
{
    public delegate void LockWrapperDelegateVoid();

    /* Job Locker. One job can work at current moment. Different jobs can work parallel */
    public static class JobLocker
    {
        private static readonly ConcurrentDictionary<string, bool> _locks = new ConcurrentDictionary<string, bool>();

        private static string LocksTryAdd(string lockerName)
        {
            if (string.IsNullOrEmpty(lockerName))  // lock by procedure's name (in this example = "JobProcedure")
            {
                lockerName = new StackFrame(2).GetMethod().Name;
            }
            if (!_locks.ContainsKey(lockerName))
            {
                _locks.TryAdd(lockerName, false);
            }
            return lockerName;
        }

        public static void LockWrapperVoid(string lockerName, LockWrapperDelegateVoid lockWrapperDelegateVoid)
        {
            lockerName = LocksTryAdd(lockerName);
            if (!_locks[lockerName])
            {
                _locks[lockerName] = true;
                try
                {
                    lockWrapperDelegateVoid();
                }
                finally
                {
                    _locks[lockerName] = false;
                }
            }
        }
    }
}

// USING

// JOB description
TryAddOrUpdateJob("JOB TITLE", () => JobManager.JobProcedure(), "* * * * *", TimeZoneInfo.Utc, "queueDefault");  // every one minute


public static void JobProcedure()
{
    // very important. You can use "BlockArea" instead null and use one area if several jobs depend each other
    // if null - area will have name like as the procedure ("JobProcedure")
    JobLocker.LockWrapperVoid(null, () =>  
    {
        //your code here
        System.Threading.Thread.Sleep(2 * 1000 * 60); // timeout - 2 minutes
    });
}
Wilkerson answered 20/3, 2018 at 10:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.