Skip to content

Instantly share code, notes, and snippets.

@arxae
Created January 10, 2019 00:17
Show Gist options
  • Save arxae/7741bae2c742d8fb3e64abfc89028dd8 to your computer and use it in GitHub Desktop.
Save arxae/7741bae2c742d8fb3e64abfc89028dd8 to your computer and use it in GitHub Desktop.
Apply to class that executes a job in Hangfire. If a recurring job with this attribute is already running, a new one won't start
using System;
using System.Collections.Generic;
using Hangfire.Client;
using Hangfire.Common;
using Hangfire.States;
using Hangfire.Storage;
/// <summary>
/// Makes sure that 2 (or more) same jobs will not be run twice at the same time.
/// If a job is already running, the new job will be skipped
/// </summary>
[AttributeUsage(AttributeTargets.All)]
public class SkipWhenPreviousJobIsRunningAttribute : JobFilterAttribute, IClientFilter, IApplyStateFilter
{
public void OnCreating(CreatingContext filterContext)
{
var connection = (JobStorageConnection)filterContext.Connection;
// We can't handle old storages
if (connection == null) return;
// We should run this filter only for background jobs based on
// recurring ones
if (filterContext.Parameters.ContainsKey("RecurringJobId") == false) return;
var recurringJobId = filterContext.Parameters["RecurringJobId"] as string;
// RecurringJobId is malformed. This should not happen, but anyway.
if (string.IsNullOrWhiteSpace(recurringJobId)) return;
var running = connection.GetValueFromHash($"recurring-job:{recurringJobId}", "Running");
if ("yes".Equals(running, StringComparison.OrdinalIgnoreCase))
{
filterContext.Canceled = true;
}
}
public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
if (context.NewState is EnqueuedState)
{
var recurringJobId = JobHelper.FromJson<string>(context.Connection.GetJobParameter(context.BackgroundJob.Id, "RecurringJobId"));
if (string.IsNullOrWhiteSpace(recurringJobId)) return;
transaction.SetRangeInHash(
$"recurring-job:{recurringJobId}",
new[] { new KeyValuePair<string, string>("Running", "yes") });
}
else if (context.NewState.IsFinal)
{
var recurringJobId = JobHelper.FromJson<string>(context.Connection.GetJobParameter(context.BackgroundJob.Id, "RecurringJobId"));
if (string.IsNullOrWhiteSpace(recurringJobId)) return;
transaction.SetRangeInHash(
$"recurring-job:{recurringJobId}",
new[] { new KeyValuePair<string, string>("Running", "no") });
}
}
public void OnCreated(CreatedContext filterContext)
{
// Method intentionally left empty.
}
public void OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
// Method intentionally left empty.
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment