Browse Source

Enhance dynamic background worker management with improved error handling and logging

pull/25059/head
maliming 1 week ago
parent
commit
eb2cf10a3d
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 2
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobExecutorJob.cs
  2. 2
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobHandlerRegistry.cs
  3. 11
      framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireDynamicBackgroundWorkerManager.cs
  4. 9
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzDynamicBackgroundWorkerAdapter.cs
  5. 30
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzDynamicBackgroundWorkerManager.cs
  6. 7
      framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IDynamicBackgroundWorkerManager.cs

2
framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobExecutorJob.cs

@ -22,7 +22,7 @@ public class DynamicBackgroundJobExecutorJob : AsyncBackgroundJob<DynamicBackgro
public override async Task ExecuteAsync(DynamicBackgroundJobArgs args)
{
Logger.LogInformation(
Logger.LogDebug(
"Executing dynamic job. TransportJobName: {TransportJobName}, EffectiveJobName: {EffectiveJobName}",
DynamicBackgroundJobArgs.JobNameConstant,
args.JobName

2
framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobHandlerRegistry.cs

@ -42,7 +42,7 @@ public class DynamicBackgroundJobHandlerRegistry : IDynamicBackgroundJobHandlerR
public virtual IReadOnlyCollection<string> GetAllNames()
{
return Handlers.Keys.ToList();
return Handlers.Keys.ToList().AsReadOnly();
}
public virtual void Clear()

11
framework/src/Volo.Abp.BackgroundWorkers.Hangfire/Volo/Abp/BackgroundWorkers/Hangfire/HangfireDynamicBackgroundWorkerManager.cs

@ -47,8 +47,17 @@ public class HangfireDynamicBackgroundWorkerManager : IDynamicBackgroundWorkerMa
cronExpression = GetCron(period);
}
ScheduleRecurringJob(workerName, cronExpression, cancellationToken);
// Register the handler first so it is available the moment the recurring job fires.
HandlerRegistry.Register(workerName, handler);
try
{
ScheduleRecurringJob(workerName, cronExpression, cancellationToken);
}
catch
{
HandlerRegistry.Unregister(workerName);
throw;
}
return Task.CompletedTask;
}

9
framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzDynamicBackgroundWorkerAdapter.cs

@ -26,13 +26,14 @@ public class QuartzDynamicBackgroundWorkerAdapter : IJob, ITransientDependency
public virtual async Task Execute(IJobExecutionContext context)
{
var workerName = context.MergedJobDataMap.GetString(QuartzDynamicBackgroundWorkerManager.DynamicWorkerNameKey);
if (string.IsNullOrWhiteSpace(workerName))
var rawWorkerName = context.MergedJobDataMap.GetString(QuartzDynamicBackgroundWorkerManager.DynamicWorkerNameKey);
if (string.IsNullOrWhiteSpace(rawWorkerName))
{
return;
}
var handler = HandlerRegistry.Get(workerName!);
var workerName = rawWorkerName!;
var handler = HandlerRegistry.Get(workerName);
if (handler == null)
{
Logger.LogWarning("No handler registered for dynamic worker: {WorkerName}", workerName);
@ -42,7 +43,7 @@ public class QuartzDynamicBackgroundWorkerAdapter : IJob, ITransientDependency
try
{
await handler(
new DynamicBackgroundWorkerExecutionContext(workerName!, ServiceProvider),
new DynamicBackgroundWorkerExecutionContext(workerName, ServiceProvider),
context.CancellationToken);
}
catch (Exception ex)

30
framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzDynamicBackgroundWorkerManager.cs

@ -48,16 +48,24 @@ public class QuartzDynamicBackgroundWorkerManager : IDynamicBackgroundWorkerMana
var trigger = BuildTrigger(schedule, jobDetail, triggerKey);
// Use replace=true to avoid TOCTOU race between CheckExists and ScheduleJob.
await Scheduler.ScheduleJobs(
new Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>>
{
{ jobDetail, new[] { trigger } }
},
replace: true,
cancellationToken);
// Register the handler first so it is available the moment the job fires.
HandlerRegistry.Register(workerName, handler);
try
{
// Use replace=true to avoid TOCTOU race between CheckExists and ScheduleJob.
await Scheduler.ScheduleJobs(
new Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>>
{
{ jobDetail, new[] { trigger } }
},
replace: true,
cancellationToken);
}
catch
{
HandlerRegistry.Unregister(workerName);
throw;
}
}
public virtual async Task<bool> RemoveAsync(string workerName, CancellationToken cancellationToken = default)
@ -68,10 +76,10 @@ public class QuartzDynamicBackgroundWorkerManager : IDynamicBackgroundWorkerMana
// This ensures cleanup works correctly after an application restart, when the registry
// is empty but the Quartz job may still exist in the scheduler store.
var jobKey = new JobKey($"DynamicWorker:{workerName}");
await Scheduler.DeleteJob(jobKey, cancellationToken);
var deleted = await Scheduler.DeleteJob(jobKey, cancellationToken);
var wasRegistered = HandlerRegistry.Unregister(workerName);
return wasRegistered;
return deleted || wasRegistered;
}
public virtual async Task<bool> UpdateScheduleAsync(

7
framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IDynamicBackgroundWorkerManager.cs

@ -21,7 +21,12 @@ public interface IDynamicBackgroundWorkerManager
/// <summary>
/// Removes a previously added dynamic worker by name.
/// Returns true if the worker was found and removed; false otherwise.
/// Always attempts to remove both the in-memory handler and any persistent scheduling record
/// (Hangfire RecurringJob or Quartz job), regardless of current in-memory state.
/// Returns true if the handler was registered in memory at the time of the call, or if
/// the persistent scheduling record was found and deleted (provider-dependent).
/// May return false after an application restart even if a persistent record was cleaned up,
/// when the provider cannot report whether a persistent record existed (e.g. Hangfire).
/// </summary>
Task<bool> RemoveAsync(string workerName, CancellationToken cancellationToken = default);

Loading…
Cancel
Save