HangfireIO / Hangfire

An easy way to perform background job processing in .NET and .NET Core applications. No Windows Service or separate process required
https://www.hangfire.io
Other
9.44k stars 1.71k forks source link

Batches not registering when continuations are deleted due to conditions not being met. #2348

Closed cavallialexander closed 10 months ago

cavallialexander commented 10 months ago

I have a batch that contains another batch (batch2); batch2 has 2 continuations, one for if all the children succeed and the other if it completes or is deleted.

The on-success continuation runs fine and goes into the succeeded state of batch2's children, but the on-failure task gets deleted but stays in the Pending state of batch2.

image

This prevents both batch and batch2 from being moved to the completed state and they will stay in the started state, presumably until the jobs time out.

Manually deleting the on-failure continuation (even though it is already in the deleted state) will result in the batch finishing as it should.

image

This issue is present in Hangfire:1.8.6 and Hangfire.Pro 3.0.1 but is not present in Hangfire 1.8.3 and Hangfire.Pro 2.3.2

odinserj commented 10 months ago

Thanks for reporting this. Can you please send the sequence of StartNew and ContinueBatchWith method invocations that result into this?

cavallialexander commented 10 months ago

No problem. Here is the minimal code I was using to test.

public class TestJobs
   [JobDisplayName("Start Sync")]
    public async Task DoSync(PerformContext jobContext = default)
    {
        var thisJobId = jobContext.BackgroundJob.Id;

        await StartSimulation(thisJobId);
    }

    private Task StartSimulation(string thisJobId)
    {
        BatchJob.ContinueJobWith(thisJobId, simBatch => SimulationStandIn(simBatch));

        return Task.CompletedTask;
    }

    private void SimulationStandIn(IBatchAction batchAction)
    {
        batchAction.StartNew(simulation =>
        {
            var pId = simulation.StartNew(parent =>
            {
                foreach (var rep in Enumerable.Range(1, 5))
                {
                    parent.StartNew(middle =>
                    {
                        string? lastId = null;
                        foreach (var rep2 in Enumerable.Range(1, 5))
                        {
                            lastId = lastId is null
                                ? middle.Enqueue(() => WriteWithDelay($"Mid: {rep} - Child: {rep2}"))
                                : middle.ContinueJobWith(lastId, () => WriteWithDelay($"Mid: {rep} - Child: {rep2}"));
                        }
                    }, $"middle {rep}");
                }
            }, "parent");

            simulation.ContinueBatchWith(pId, () => WriteWithDelay("Success"),
                options: BatchContinuationOptions.OnlyOnSucceededState);
            simulation.ContinueBatchWith(pId, () => WriteWithDelay("Failure"),
                options: BatchContinuationOptions.OnlyOnDeletedState | BatchContinuationOptions.OnlyOnCompletedState);
        }, "simulation");
    }

  [JobDisplayName("Extra Addition: {0}")]
  public async Task WriteWithDelay(string val)
  {
      Console.WriteLine("Wait for it...");
      await Task.Delay(1000);
      Console.WriteLine(val);
  }
}
odinserj commented 10 months ago

Thanks a lot for the update! I just released 3.0.2 and 2.3.4 versions that should fix this bug. Please feel free to reopen this issue if it persists after the upgrade.