2016-07-25 11 views
2

Я добавил атрибут 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 попытается запустить его несколько раз.

Я мог бы, конечно, поместить атрибут в Работу, гарантируя, что он не будет вообще Retry. Однако это не является допустимым решением, потому что я хочу, чтобы задания были повторены, за исключением случаев, когда они не работают из-за одновременного запуска.

+0

Можете уточнить два абзаца. Извините, но я немного смущен. – jtabuloc

ответ

3

Вы можете предотвратить повтор попыток, если вы установите проверку в OnPerformed в интерфейсе IServerFilter.

Реализация:

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 
     } 
    } 

Я надеюсь, что это поможет.

0

Я на самом деле закончил использование на основе ответа 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(); 
       } 
      } 
     } 
    } 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^