当重复任务同时运行两次时,如何取消重复任务?

3
我已经在作业上添加了一个属性DisableConcurrentExecution(1),但这只是延迟第二个实例的执行,直到第一个实例完成。我希望能够检测到并取消同时运行的作业。

我想,如果DisableConcurrentExecution(1)可以防止同时运行相同的循环作业,它会将第二个作业放置在“重试”状态下,从而改变其状态。因此,我在作业上添加了其他自定义属性,用于检测失败状态,如下所示:

public class StopConcurrentTask : JobFilterAttribute, IElectStateFilter
{
    public void OnStateElection(ElectStateContext context)
    {
        var failedState = context.CandidateState as FailedState;
        if(failedState != null && failedState.Exception != null)
        {
            if(!string.IsNullOrEmpty(failedState.Exception.Message) && failedState.Exception.Message.Contains("Timeout expired. The timeout elapsed prior to obtaining a distributed lock on"))
            {

            }
        }
    }
}

这使我能够检测作业是否因为与同一作业的另一个实例同时运行而失败。问题是,我找不到一种方法来取消这个特定的失败作业并将其从重新运行中删除。现在,该作业将被放入重试计划,并且Hangfire将尝试运行它多次。
当然,我可以在作业上放置一个属性,确保它根本不重试。然而,这不是一个有效的解决方案,因为我希望作业重试,除非它们由于并发运行而失败。

你能澄清一下你的两段话吗?抱歉,我有点困惑。 - jtabuloc
2个回答

3

如果您在 IServerFilter 接口的 OnPerformed 方法中进行验证,就可以防止重试的发生。

实现:

public class StopConcurrentTask : JobFilterAttribute, IElectStateFilter, IServerFilter
    {
        // All failed after retry will be catched here and I don't know if you still need this
        // but it is up to you
        public void OnStateElection(ElectStateContext context)
        {
            var failedState = context.CandidateState as FailedState;
            if (failedState != null && failedState.Exception != null)
            {
                if (!string.IsNullOrEmpty(failedState.Exception.Message) && failedState.Exception.Message.Contains("Timeout expired. The timeout elapsed prior to obtaining a distributed lock on"))
                {

                }
            }
        }

        public void OnPerformed(PerformedContext filterContext)
        {
            // Do your exception handling or validation here
            if (filterContext.Exception == null) return;

            using (var connection = _jobStorage.GetConnection())
            {
                var storageConnection = connection as JobStorageConnection;

                if (storageConnection == null)
                    return;

                var jobId = filterContext.BackgroundJob.Id
                // var job = storageConnection.GetJobData(jobId); -- If you want job detail

                var failedState = new FailedState(filterContext.Exception)
                {
                    Reason = "Your Exception Message or filterContext.Exception.Message"
                };

                using (var transaction = connection.GetConnection().CreateWriteTransaction())
                {
                    transaction.RemoveFromSet("retries", jobId);  // Remove from retry state
                    transaction.RemoveFromSet("schedule", jobId); // Remove from schedule state
                    transaction.SetJobState(jobId, failedState);  // update status with failed state
                    transaction.Commit();
                }
            }
        }

        public void OnPerforming(PerformingContext filterContext)
        {
           // Do nothing
        }
    }

我希望这能对您有所帮助。


1

实际上,我最终采用了Jr Tabuloc的答案——如果作业在15秒前最后执行,则会删除该作业——我注意到服务器唤醒和作业执行之间的时间变化。通常情况下,这是以毫秒为单位的,但由于我的作业每天只执行一次,所以我想15秒不会有影响。

public class StopWakeUpExecution : JobFilterAttribute, IServerFilter
{
    public void OnPerformed(PerformedContext filterContext)
    {

    }

    public void OnPerforming(PerformingContext filterContext)
    {
        using (var connection = JobStorage.Current.GetConnection())
        {
            var recurring = connection.GetRecurringJobs().FirstOrDefault(p => p.Job.ToString() == filterContext.BackgroundJob.Job.ToString());
            TimeSpan difference = DateTime.UtcNow.Subtract(recurring.LastExecution.Value);
            if (recurring != null && difference.Seconds < 15)
            {
                // Execution was due in the past. We don't want to automaticly execute jobs after server crash though.

                var storageConnection = connection as JobStorageConnection;

                if (storageConnection == null)
                    return;

                var jobId = filterContext.BackgroundJob.Id;

                var deletedState = new DeletedState()
                {
                    Reason = "Task was due in the past. Please Execute manually if required."
                };

                using (var transaction = connection.CreateWriteTransaction())
                {
                    transaction.RemoveFromSet("retries", jobId);  // Remove from retry state
                    transaction.RemoveFromSet("schedule", jobId); // Remove from schedule state
                    transaction.SetJobState(jobId, deletedState);  // update status with failed state
                    transaction.Commit();
                }
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接