Starts in:
2 DAYS
3 HRS
52 MIN
31 SEC
Starts in:
2 D
3 H
52 M
31 S
Open Closed

Hangfire background job does not work with unit of work properly #3685


User avatar
0
enes.koroglu created
  • ABP Framework version: v5.3.3
  • UI type: Angular
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): no
  • Exception message and stack trace:

We create appService methods with [UnitOfWork(true)] attribute. Call our business related codes/methods and then make await CurrentUnitOfWork.SaveChangesAsync(); Then call one or more background job creation calls and when exit from appService methods ABP calls CompleteAsnyc method. In this stiuation if any error occurs (like below), background jobs are created and our operations are (on business related tables) rollbacked. How can we make background job work in the same transaction with our business code; so it will not created or rolledback when we get any error on method completion (CompleAsync)

[ERR] The operation was canceled.
System.OperationCanceledException: The operation was canceled.
   at System.Threading.CancellationToken.ThrowOperationCanceledException()
   at Npgsql.Internal.NpgsqlConnector.<StartUserAction>g__DoStartUserAction|257_0(<>c__DisplayClass257_0& )
   at Npgsql.Internal.NpgsqlConnector.StartUserAction(ConnectorState newState, NpgsqlCommand command, CancellationToken cancellationToken, Boolean attemptPgCancellation)
   at Npgsql.NpgsqlTransaction.Commit(Boolean async, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalTransaction.CommitAsync(CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalTransaction.CommitAsync(CancellationToken cancellationToken)
   at Volo.Abp.Uow.EntityFrameworkCore.EfCoreTransactionApi.CommitAsync()
   at Volo.Abp.Uow.UnitOfWork.CommitTransactionsAsync()
   at Volo.Abp.Uow.UnitOfWork.CompleteAsync(CancellationToken cancellationToken)
   at Volo.Abp.AspNetCore.Mvc.Uow.AbpUowActionFilter.OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

  • Steps to reproduce the issue:"
[UnitOfWork(true)]
        public async Task CreateAsync()
        {
            await productRepository.InsertAsync(new Product("Product1"));
            await CurrentUnitOfWork.SaveChangesAsync();
            await backgroundJobManager.EnqueueAsync(ProductJobArgs);
        }

7 Answer(s)
  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    How can we make background job work in the same transaction with our business code; so it will not created or rolledback when we get any error on method completion (CompleAsync)

    Hi,

    I'm sorry to say, Background jobs are independent of application threads, so they cannot use the same transaction.

    You can try this:

    public async Task CreateAsync()
    {
        using (var uow = _unitOfWorkManager.Begin(requiresNew: true, isTransactional: true))
        {
            //...
            await productRepository.InsertAsync(new Product("Product1"));
            await uow.CompleteAsync();
        }
                
        await backgroundJobManager.EnqueueAsync(ProductJobArgs);
    }
    
  • User Avatar
    0
    ademaygun created

    @liangshiwei Could you implement inbox pattern like in eventbus

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi, @ademaygun

    You can just use the ABP's BackgroundJob system instead of Hangfire, it can work with the ABP Unitofwork system.

  • User Avatar
    0
    ademaygun created

    Hı @liangshiwei ,

    Yes, but it hasn't UI dashboard. if there was a UI we could use it.

  • User Avatar
    0
    enes.koroglu created

    Hi @liangshiwei

    In above message your suggestion (below code) does not solve our problem. If we got any error on backgroudjob creation, process will be broken. If it's a UI call, user will get an error and business will be completed. As @ademaygun said there is no UI dashboard to manage and see background jobs for ABP's BackgroundJob system and in the documents ABP suggest us hangfire as an integrated solution. But ABP does not work with Hangfire properly. There would be same problem in EventBus but luckly ABP solves that problem with implementing inbox pattern. There could be same solution for background jobs.

    Is it possible to discuss this problem with the team? This problem is really critical for us.
    
    public async Task CreateAsync()
    {
        using (var uow = _unitOfWorkManager.Begin(requiresNew: true, isTransactional: true))
        {
            //...
            await productRepository.InsertAsync(new Product("Product1"));
            await uow.CompleteAsync();
        }
                
        await backgroundJobManager.EnqueueAsync(ProductJobArgs);
    }
    
  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    I will try find a way

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    You can try this:

    Install the System.Data.SqlClient package version with 4.8.3

    using (var transaction = new TransactionScope())
    {
        await productRepository.InsertAsync(new Product("Product1"));
        await backgroundJobManager.EnqueueAsync(ProductJobArgs);
        transaction.Complete();
    }
    

    Here is my test image:

Made with ❤️ on ABP v9.1.0-preview. Updated on November 20, 2024, 13:06