Hangfire выполняет задание дважды

#asp.net-core #hangfire

Вопрос:

Я использую Hangfire.AspNetCore 1.7.17 и Hangfire.MySqlStorage 2.0.3 для программного обеспечения, которое в настоящее время находится в производстве.

Время от времени мы получаем отчет о выполнении заданий дважды, несмотря на использование атрибута [DisableConcurrentExecution] с таймаутом в 30 секунд.

Кажется, что как только эти 30 секунд пройдут, другой работник снова возьмет ту же самую работу.

Код довольно прост:

 public async Task ProcessPicking(HttpRequest incomingRequest)
{
    var filePath = await StoreStreamAsync(incomingRequest, TriggerTypes.Picking);
    var picking = await XmlHelper.DeserializeFileAsync<Picking>(filePath);
    // delay with 20 minutes so outbound-out gets the chance to be send first
    BackgroundJob.Schedule(() => StartPicking(picking), TimeSpan.FromMinutes(20));
}

[TriggerAlarming("[IMPORTANT] Failed to parse picking message to **** object.")]
[DisableConcurrentExecution(30)]
public void StartPicking(Picking picking)
{
    var orderlinePickModels = picking.ToSalesOrderlinePickQuantityRequests().ToList();
    var orderlineStatusModels = orderlinePickModels.ToSalesOrderlineStatusRequests().ToList();

    var isParsed = DateTime.TryParse(picking.Order.UnloadingDate, out var unloadingDate);

    for (var i = 0; i < orderlinePickModels.Count; i  )
    {
        // prevents bugs with usage of i in the background jobs
        var index = i;
        var id = BackgroundJob.Enqueue(() => SendSalesOrderlinePickQuantityRequest(orderlinePickModels[index], picking.EdiReference));
        BackgroundJob.ContinueJobWith(id, () => SendSalesOrderlineStatusRequest(
        orderlineStatusModels.First(x=>x.SalesOrderlineId== orderlinePickModels[index].OrderlineId), 
        picking.EdiReference, picking.Order.PrimaryReference, isParsed ? unloadingDate : DateTime.MinValue));
    }
}

[TriggerAlarming("[IMPORTANT] Failed to send order line pick quantity request to ****.")]
[AutomaticRetry(Attempts = 2)]
[DisableConcurrentExecution(30)]
public void SendSalesOrderlinePickQuantityRequest(SalesOrderlinePickQuantityRequest request, string ediReference)
{
    var audit = new AuditPostModel
    {
        Description = $"Finished job to send order line pick quantity request for item {request.Itemcode}, part of ediReference {ediReference}.",
        Object = request,
        Type = AuditTypes.SalesOrderlinePickQuantity
    };

    try
    {
        _logger.LogInformation($"Started job to send order line pick quantity request for item {request.Itemcode}.");
                
        var response = _service.SendSalesOrderLinePickQuantity(request).GetAwaiter().GetResult();
        audit.StatusCode = (int)response.StatusCode;
        if (!response.IsSuccessStatusCode) throw new TriggerRequestFailedException();
                
        audit.IsSuccessful = true;
        _logger.LogInformation("Successfully posted sales order line pick quantity request to ***** endpoint.");
    }
    finally
    {
        Audit(audit);
    }
}
 

Он планирует основную задачу (выбор начала), которая создает объекты, необходимые для двух подзадач:

  1. Отправьте детали комплектации клиенту
  2. Отправить обновление статуса клиенту

Первое задание дублируется. Возможно, и вторая работа тоже, но это не настолько важно, чтобы беспокоиться об этом, так как это касается только обновления статуса. Однако первое задание заставляет клиента думать, что было выбрано больше товаров, чем на самом деле.

Я бы предположил, что Hangfire обновляет состояние задания, например, выполняется, и проверяет это состояние перед началом работы. Не слишком ли мал мой тайм-аут при отключенном параллельном выполнении? Возможно ли в этом сценарии, что подключение к базе данных для обновления состояния занимает около 30 секунд (честно говоря, оно выполняется на медленном сервере с ~8 ГБ оперативной памяти, 6 vCores), из-за чего второй работник уже снова выполняет задание?

Или это специфическая проблема с зависанием, которую необходимо решить?