Hangfire RecurringJob執行時間重疊

在.net的專案中常常會使用Hangfire來處理排程相關的功能,像一些需要定時執行的工作就會掛在RecurringJob執行。但這裡延伸出一個議題:若是工作執行時間大於排程間隔時間時會發生什麼事?

先建立一個RecurringJob,每分鐘執行一次

recurringJob.AddOrUpdate("", () => DoSomething(), Cron.Minutely);

在function中使用Thread.Sleep暫停一分半,製造出"執行時間大於排程間隔時間"的情境,並用logger紀錄開始、結束時間以及thread ID

public void DoSomething()
{
     _logger.Info($"Job start, tid={Thread.CurrentThread.ManagedThreadId}");
     Thread.Sleep(TimeSpan.FromSeconds(90));
     _logger.Info($"Job end, tid={Thread.CurrentThread.ManagedThreadId}");
}

執行一段時間,產生的log如下

由log中可以發現thread ID:17的工作尚未結束,thread ID:18的工作就開始執行了

若是做帳務處理之類的工作,執行時間重疊可能會造成資料異常


Hangfire中有個DisableConcurrentExecution Attribute可以讓同一時間只有一個工作執行

參數為timeout時間,單位是秒

[DisableConcurrentExecution(5)]
public void DoSomething()
{
    _logger.Info($"Job start, tid={Thread.CurrentThread.ManagedThreadId}");
    Thread.Sleep(TimeSpan.FromSeconds(90));
    _logger.Info($"Job end, tid={Thread.CurrentThread.ManagedThreadId}");
}

產生的log如下

可以發現工作執行時間不會相互重疊了

當排程觸發卻發現前一個工作仍在執行中時,便會拋出例外並加入重試的queue,預設會重試10次

若是希望自訂重試次數,或是乾脆不重試,可以使用AutomaticRetry Attribute

參數中除了能指定重試次數外,還能指定重試延遲時間、重試都失敗後要歸類在失敗工作還是刪除工作等等

[DisableConcurrentExecution(5)]
[AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
public void DoSomething()
{
    _logger.Info($"Job start, tid={Thread.CurrentThread.ManagedThreadId}");
    Thread.Sleep(TimeSpan.FromSeconds(90));
    _logger.Info($"Job end, tid={Thread.CurrentThread.ManagedThreadId}");
}

我在程式中設定完全不重試,並將工作移動到刪除工作中,在dashboard能看到執行時間重疊的工作都直接移到刪除工作中


最後在官方文件中有多次強調DisableConcurrentExecution只能降低工作執行時間重疊發生的機率,在高負載的狀態下還是有可能會發生工作執行時間重疊的狀況

因此在排程執行的工作中還是要考慮到工作執行時間重疊的情境,並做相關應對措施

參考資料: