Open Closed

HANGFIRE WORKER/JOB REGISTRATION PROBLEMS ON ABP 8.2.0 #7551


User avatar
0
andrei.pislaru created

I wanted to upgrade our project to the new version (8.2.0 from 8.1) but then I encountered this problem when starting the application, the problem seems to be that the way jobs/workers id are registered is not correct anymore since of a limit to hangfire Ids i guess. The workers that causes the problem are the ones generated by ABP, specifically HangfirePeriodicBackgroundWorkerAdapter<BackgroundJobWorker>.DoWorkAsync and HangfirePeriodicBackgroundWorkerAdapter<TokenCleanupBackgroundWorker>.DoWorkAsync.

I had to override AddAsync of HangfireBackgroundWorkerManager ( still had to find a better way of handling ids ) also i hade to modify logic a bit since it was filled with obsolete methods of hangfire

//public override async Task AddAsync(IBackgroundWorker worker, CancellationToken cancellationToken = default) //{ // switch (worker) // { // case IHangfireBackgroundWorker hangfireBackgroundWorker: // { // var unProxyWorker = ProxyHelper.UnProxy(hangfireBackgroundWorker);

// var jobId = string.IsNullOrEmpty(hangfireBackgroundWorker.RecurringJobId) ? hangfireBackgroundWorker.GetType().Name : hangfireBackgroundWorker.RecurringJobId; // var timezone = hangfireBackgroundWorker.TimeZone ?? TimeZoneInfo.Utc;

// RecurringJob.AddOrUpdate( // jobId, // hangfireBackgroundWorker.Queue, // () => ((IHangfireBackgroundWorker)unProxyWorker).DoWorkAsync(cancellationToken), // hangfireBackgroundWorker.CronExpression, // new RecurringJobOptions // { // TimeZone = timezone // } // );

// break; // } // case AsyncPeriodicBackgroundWorkerBase or PeriodicBackgroundWorkerBase: // { // var timer = worker.GetType() // .GetProperty("Timer", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(worker);

// var period = worker is AsyncPeriodicBackgroundWorkerBase ? ((AbpAsyncTimer?)timer)?.Period : ((AbpTimer?)timer)?.Period;

// if (period == null) // { // return; // }

// var adapterType = typeof(HangfirePeriodicBackgroundWorkerAdapter<>).MakeGenericType(ProxyHelper.GetUnProxiedType(worker)); // var workerAdapter = (Activator.CreateInstance(adapterType) as IHangfireBackgroundWorker)!;

// var jobId = string.IsNullOrEmpty(workerAdapter.RecurringJobId) ? workerAdapter.GetType().Name : workerAdapter.RecurringJobId; // var timezone = workerAdapter.TimeZone ?? TimeZoneInfo.Utc;

// RecurringJob.AddOrUpdate( // jobId, // workerAdapter.Queue, // () => workerAdapter.DoWorkAsync(cancellationToken), // GetCron(period.Value), // new RecurringJobOptions // { // TimeZone = timezone // } // ); // break; // } // default: // await base.AddAsync(worker, cancellationToken); // break; // }

//}

the below image shows the ids generated by abp ( they work in abp 8.1)

here are the versions i use for hangfire/background workers

  • ABP Framework version: v8.2.0
  • UI Type: Angular
  • Database System: EF Core (SQL Server )
  • Exception message and full stack trace:

[14:40:24 FTL] Host terminated unexpectedly! Volo.Abp.AbpInitializationException: An error occurred during the initialize Volo.Abp.Modularity.OnApplicationInitializationModuleLifecycleContributor phase of the module Volo.Abp.Identity.AbpIdentityProDomainModule, Volo.Abp.Identity.Pro.Domain, Version=8.2.0.0, Culture=neutral, PublicKeyToken=null: String or binary data would be truncated in table 'sqldb-qq-dev.HangFire.Hash', column 'Key'. Truncated value: 'recurring-job:HangfirePeriodicBackgroundWorkerAdapter<IdentitySessionCleanupBackgroundWorker>.DoWork'.. See the inner exception for details. ---> Microsoft.Data.SqlClient.SqlException (0x80131904): String or binary data would be truncated in table 'sqldb-qq-dev.HangFire.Hash', column 'Key'. Truncated value: 'recurring-job:HangfirePeriodicBackgroundWorkerAdapter<IdentitySessionCleanupBackgroundWorker>.DoWork'. at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction) at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction) at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at Microsoft.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted) at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean isAsync, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest) at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, TaskCompletionSource1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry, String method) at Microsoft.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource1 completion, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry, String methodName) at Microsoft.Data.SqlClient.SqlCommand.ExecuteNonQuery() at Hangfire.SqlServer.SqlCommandBatch.ExecuteNonQuery() in C:\projects\hangfire-525\src\Hangfire.SqlServer\SqlCommandBatch.cs:line 122 at Hangfire.SqlServer.SqlServerWriteOnlyTransaction.<Commit>b__17_0(DbConnection connection, DbTransaction transaction) in C:\projects\hangfire-525\src\Hangfire.SqlServer\SqlServerWriteOnlyTransaction.cs:line 100 at Hangfire.SqlServer.SqlServerStorage.<>c__DisplayClass41_0.<UseTransaction>b__0(DbConnection connection, DbTransaction transaction) in C:\projects\hangfire-525\src\Hangfire.SqlServer\SqlServerStorage.cs:line 272 at Hangfire.SqlServer.SqlServerStorage.<>c__DisplayClass42_01.<UseTransaction>b__0(DbConnection connection) in C:\projects\hangfire-525\src\Hangfire.SqlServer\SqlServerStorage.cs:line 319 at Hangfire.SqlServer.SqlServerStorage.UseConnection[T](DbConnection dedicatedConnection, Func2 func) in C:\projects\hangfire-525\src\Hangfire.SqlServer\SqlServerStorage.cs:line 257 at Hangfire.SqlServer.SqlServerStorage.UseTransaction[T](DbConnection dedicatedConnection, Func3 func, Nullable1 isolationLevel) in C:\projects\hangfire-525\src\Hangfire.SqlServer\SqlServerStorage.cs:line 307 at Hangfire.SqlServer.SqlServerStorage.UseTransaction(DbConnection dedicatedConnection, Action2 action) in C:\projects\hangfire-525\src\Hangfire.SqlServer\SqlServerStorage.cs:line 270 at Hangfire.SqlServer.SqlServerWriteOnlyTransaction.Commit() in C:\projects\hangfire-525\src\Hangfire.SqlServer\SqlServerWriteOnlyTransaction.cs:line 69 at Hangfire.RecurringJobManager.AddOrUpdate(String recurringJobId, Job job, String cronExpression, RecurringJobOptions options) in C:\projects\hangfire-525\src\Hangfire.Core\RecurringJobManager.cs:line 148 at Hangfire.RecurringJobManagerExtensions.AddOrUpdate(IRecurringJobManager manager, String recurringJobId, Job job, String cronExpression, TimeZoneInfo timeZone, String queue) in C:\projects\hangfire-525\src\Hangfire.Core\RecurringJobManagerExtensions.cs:line 65 at Hangfire.RecurringJob.AddOrUpdate(Expression1 methodCall, String cronExpression, TimeZoneInfo timeZone, String queue) in C:\projects\hangfire-525\src\Hangfire.Core\RecurringJob.cs:line 379 at Volo.Abp.BackgroundWorkers.Hangfire.HangfireBackgroundWorkerManager.AddAsync(IBackgroundWorker worker, CancellationToken cancellationToken) at Volo.Abp.Identity.AbpIdentityProDomainModule.OnApplicationInitializationAsync(ApplicationInitializationContext context) at Volo.Abp.Modularity.OnApplicationInitializationModuleLifecycleContributor.InitializeAsync(ApplicationInitializationContext context, IAbpModule module) at Volo.Abp.Modularity.ModuleManager.InitializeModulesAsync(ApplicationInitializationContext context) ClientConnectionId:10a70a4f-4051-4c9b-bcde-e5e0c47734c6 Error Number:2628,State:1,Class:16 --- End of inner exception stack trace --- at Volo.Abp.Modularity.ModuleManager.InitializeModulesAsync(ApplicationInitializationContext context) at Volo.Abp.AbpApplicationBase.InitializeModulesAsync() at Volo.Abp.AbpApplicationWithExternalServiceProvider.InitializeAsync(IServiceProvider serviceProvider) at Microsoft.AspNetCore.Builder.AbpApplicationBuilderExtensions.InitializeApplicationAsync(IApplicationBuilder app) at Jeremias.QQ.Program.Main(String[] args) in C:\Users\AndreiIonutPislaru\source\repos\src\Jeremias.QQ.HttpApi.Host\Program.cs:line 64


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

    https://github.com/abpframework/abp/commits/b9ae57b38bbfe29837079be54bfab31b9949b55a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerManager.cs

    The main logic has not changed in a year.

    This is not ABP problem, but Hangfire. you can try to change the field length

  • User Avatar
    0
    andrei.pislaru created

    https://github.com/abpframework/abp/commits/b9ae57b38bbfe29837079be54bfab31b9949b55a/framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireBackgroundWorkerManager.cs

    The main logic has not changed in a year.

    This is not ABP problem, but Hangfire. you can try to change the field length

    can you test with the versions i gave you in the screenshot?

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    I will fix it in the next patch version. You can try this

    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(IBackgroundWorkerManager), typeof(HangfireBackgroundWorkerManager))]
    public class MyHangfireBackgroundWorkerManager : HangfireBackgroundWorkerManager
    {
        public MyHangfireBackgroundWorkerManager(IServiceProvider serviceProvider) : base(serviceProvider)
        {
        }
    
        public override async Task AddAsync(IBackgroundWorker worker, CancellationToken cancellationToken = default)
        {
            switch (worker)
            {
                case IHangfireBackgroundWorker hangfireBackgroundWorker:
                {
                    var unProxyWorker = ProxyHelper.UnProxy(hangfireBackgroundWorker);
                    if (hangfireBackgroundWorker.RecurringJobId.IsNullOrWhiteSpace())
                    {
                        RecurringJob.AddOrUpdate(
                            () => ((IHangfireBackgroundWorker)unProxyWorker).DoWorkAsync(cancellationToken),
                            hangfireBackgroundWorker.CronExpression, hangfireBackgroundWorker.TimeZone,
                            hangfireBackgroundWorker.Queue);
                    }
                    else
                    {
                        RecurringJob.AddOrUpdate(hangfireBackgroundWorker.RecurringJobId,
                            () => ((IHangfireBackgroundWorker)unProxyWorker).DoWorkAsync(cancellationToken),
                            hangfireBackgroundWorker.CronExpression, hangfireBackgroundWorker.TimeZone,
                            hangfireBackgroundWorker.Queue);
                    }
    
                    break;
                }
                case AsyncPeriodicBackgroundWorkerBase or PeriodicBackgroundWorkerBase:
                {
                    var timer = worker.GetType()
                        .GetProperty("Timer", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(worker);
    
                    var period = worker is AsyncPeriodicBackgroundWorkerBase
                        ? ((AbpAsyncTimer?)timer)?.Period
                        : ((AbpTimer?)timer)?.Period;
    
                    if (period == null)
                    {
                        return;
                    }
    
                    var unProxiedType = ProxyHelper.GetUnProxiedType(worker);
                    var adapterType =
                        typeof(HangfirePeriodicBackgroundWorkerAdapter<>).MakeGenericType(unProxiedType);
                    var workerAdapter = (Activator.CreateInstance(adapterType) as IHangfireBackgroundWorker)!;
    
                    RecurringJob.AddOrUpdate(unProxiedType.Name, () => workerAdapter.DoWorkAsync(cancellationToken), GetCron(period.Value),
                        workerAdapter.TimeZone, workerAdapter.Queue);
    
                    break;
                }
                default:
                    await base.AddAsync(worker, cancellationToken);
                    break;
            }
        }
        
        protected override string GetCron(int period)
        {
            var time = TimeSpan.FromMilliseconds(period);
            string cron;
    
            if (time.TotalSeconds <= 59)
            {
                cron = $"*/{time.TotalSeconds} * * * * *";
            }
            else if (time.TotalMinutes <= 59)
            {
                cron = $"*/{time.TotalMinutes} * * * *";
            }
            else if (time.TotalHours <= 23)
            {
                cron = $"0 */{time.TotalHours} * * *";
            }
            else if(time.TotalDays <= 31)
            {
                cron = $"0 0 0 1/{time.TotalDays} * *";
            }
            else
            {
                throw new AbpException(
                    $"Cannot convert period: {period} to cron expression, use HangfireBackgroundWorkerBase to define worker");
            }
    
            return cron;
        }
    }
    
Made with ❤️ on ABP v9.1.0-preview. Updated on November 11, 2024, 11:11