Browse Source

Implement dynamic background job handling with new DynamicBackgroundJobArgs and executor

pull/25059/head
maliming 6 months ago
parent
commit
92a72fcef5
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 2
      docs/en/framework/infrastructure/background-jobs/index.md
  2. 8
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DefaultDynamicBackgroundJobManager.cs
  3. 6
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobArgs.cs
  4. 8
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobExecutorJob.cs
  5. 2
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/IDynamicBackgroundJobManager.cs
  6. 4
      framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/AbpDashboardOptionsProvider.cs
  7. 8
      framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobManager_Tests.cs
  8. 4
      modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/DemoAppSharedModule.cs
  9. 16
      modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/SampleJobCreator.cs

2
docs/en/framework/infrastructure/background-jobs/index.md

@ -420,7 +420,7 @@ bool removed = _dynamicJobManager.UnregisterHandler("ProcessOrder");
### How It Works ### How It Works
- **Typed job path**: When the job name matches a registered typed job configuration, the args are serialized to JSON and deserialized to the expected args type, then enqueued through `IBackgroundJobManager.EnqueueAsync<TArgs>`. - **Typed job path**: When the job name matches a registered typed job configuration, the args are serialized to JSON and deserialized to the expected args type, then enqueued through `IBackgroundJobManager.EnqueueAsync<TArgs>`.
- **Dynamic handler path**: When the job name matches a registered dynamic handler, the args are wrapped as `AnonymousJobArgs` (an internal transport type) and enqueued through `IBackgroundJobManager.EnqueueAsync<AnonymousJobArgs>`. When the job executes, the framework looks up the handler by name and invokes it. - **Dynamic handler path**: When the job name matches a registered dynamic handler, the args are wrapped as `DynamicBackgroundJobArgs` (an internal transport type) and enqueued through `IBackgroundJobManager.EnqueueAsync<DynamicBackgroundJobArgs>`. When the job executes, the framework looks up the handler by name and invokes it.
- All dynamic jobs go through the **standard typed job pipeline**, which means they work with all providers (Default, Hangfire, Quartz, RabbitMQ, TickerQ) without any provider-specific changes. - All dynamic jobs go through the **standard typed job pipeline**, which means they work with all providers (Default, Hangfire, Quartz, RabbitMQ, TickerQ) without any provider-specific changes.
## Integrations ## Integrations

8
framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DefaultDynamicBackgroundJobManager.cs

@ -47,7 +47,7 @@ public class DefaultDynamicBackgroundJobManager : IDynamicBackgroundJobManager,
if (HandlerRegistry.IsRegistered(jobName)) if (HandlerRegistry.IsRegistered(jobName))
{ {
return await EnqueueAnonymousJobAsync(jobName, args, priority, delay); return await EnqueueDynamicHandlerJobAsync(jobName, args, priority, delay);
} }
throw new AbpException( throw new AbpException(
@ -88,15 +88,15 @@ public class DefaultDynamicBackgroundJobManager : IDynamicBackgroundJobManager,
return await task; return await task;
} }
protected virtual Task<string> EnqueueAnonymousJobAsync( protected virtual Task<string> EnqueueDynamicHandlerJobAsync(
string jobName, string jobName,
object args, object args,
BackgroundJobPriority priority, BackgroundJobPriority priority,
TimeSpan? delay) TimeSpan? delay)
{ {
var jsonData = JsonSerializer.Serialize(args); var jsonData = JsonSerializer.Serialize(args);
var anonymousArgs = new AnonymousJobArgs(jobName, jsonData); var dynamicArgs = new DynamicBackgroundJobArgs(jobName, jsonData);
return BackgroundJobManager.EnqueueAsync(anonymousArgs, priority, delay); return BackgroundJobManager.EnqueueAsync(dynamicArgs, priority, delay);
} }
private static MethodInfo GetOrCreateEnqueueMethod(Type argsType) private static MethodInfo GetOrCreateEnqueueMethod(Type argsType)

6
framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobArgs.cs → framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobArgs.cs

@ -1,15 +1,15 @@
namespace Volo.Abp.BackgroundJobs; namespace Volo.Abp.BackgroundJobs;
[BackgroundJobName(JobNameConstant)] [BackgroundJobName(JobNameConstant)]
public class AnonymousJobArgs public class DynamicBackgroundJobArgs
{ {
public const string JobNameConstant = "Abp.AnonymousJob"; public const string JobNameConstant = "Abp.DynamicJob";
public string JobName { get; } public string JobName { get; }
public string JsonData { get; } public string JsonData { get; }
public AnonymousJobArgs(string jobName, string jsonData) public DynamicBackgroundJobArgs(string jobName, string jsonData)
{ {
JobName = Check.NotNullOrWhiteSpace(jobName, nameof(jobName)); JobName = Check.NotNullOrWhiteSpace(jobName, nameof(jobName));
JsonData = Check.NotNull(jsonData, nameof(jsonData)); JsonData = Check.NotNull(jsonData, nameof(jsonData));

8
framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobExecutorAsyncBackgroundJob.cs → framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/DynamicBackgroundJobExecutorJob.cs

@ -7,12 +7,12 @@ using Volo.Abp.Threading;
namespace Volo.Abp.BackgroundJobs; namespace Volo.Abp.BackgroundJobs;
public class AnonymousJobExecutorAsyncBackgroundJob : AsyncBackgroundJob<AnonymousJobArgs>, ITransientDependency public class DynamicBackgroundJobExecutorJob : AsyncBackgroundJob<DynamicBackgroundJobArgs>, ITransientDependency
{ {
protected IDynamicBackgroundJobHandlerRegistry HandlerRegistry { get; } protected IDynamicBackgroundJobHandlerRegistry HandlerRegistry { get; }
protected IServiceProvider ServiceProvider { get; } protected IServiceProvider ServiceProvider { get; }
public AnonymousJobExecutorAsyncBackgroundJob( public DynamicBackgroundJobExecutorJob(
IDynamicBackgroundJobHandlerRegistry handlerRegistry, IDynamicBackgroundJobHandlerRegistry handlerRegistry,
IServiceProvider serviceProvider) IServiceProvider serviceProvider)
{ {
@ -20,11 +20,11 @@ public class AnonymousJobExecutorAsyncBackgroundJob : AsyncBackgroundJob<Anonymo
ServiceProvider = serviceProvider; ServiceProvider = serviceProvider;
} }
public override async Task ExecuteAsync(AnonymousJobArgs args) public override async Task ExecuteAsync(DynamicBackgroundJobArgs args)
{ {
Logger.LogInformation( Logger.LogInformation(
"Executing dynamic job. TransportJobName: {TransportJobName}, EffectiveJobName: {EffectiveJobName}", "Executing dynamic job. TransportJobName: {TransportJobName}, EffectiveJobName: {EffectiveJobName}",
AnonymousJobArgs.JobNameConstant, DynamicBackgroundJobArgs.JobNameConstant,
args.JobName args.JobName
); );

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

@ -15,7 +15,7 @@ public interface IDynamicBackgroundJobManager
/// If a typed job configuration exists for this name, the args will be /// If a typed job configuration exists for this name, the args will be
/// deserialized to the configured args type and enqueued through the typed pipeline. /// deserialized to the configured args type and enqueued through the typed pipeline.
/// If a dynamic handler is registered, the args will be wrapped as /// If a dynamic handler is registered, the args will be wrapped as
/// <see cref="AnonymousJobArgs"/> and enqueued through the standard typed pipeline. /// <see cref="DynamicBackgroundJobArgs"/> and enqueued through the standard typed pipeline.
/// </summary> /// </summary>
/// <param name="jobName">Name of the background job.</param> /// <param name="jobName">Name of the background job.</param>
/// <param name="args">Job arguments (will be serialized to JSON).</param> /// <param name="args">Job arguments (will be serialized to JSON).</param>

4
framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/AbpDashboardOptionsProvider.cs

@ -24,9 +24,9 @@ public class AbpDashboardOptionsProvider : ITransientDependency
var jobName = job.ToString(); var jobName = job.ToString();
if (job.Args.Count == 3 && job.Args.Last() is CancellationToken) if (job.Args.Count == 3 && job.Args.Last() is CancellationToken)
{ {
if (job.Args[1] is AnonymousJobArgs anonymousJobArgs) if (job.Args[1] is DynamicBackgroundJobArgs dynamicJobArgs)
{ {
jobName = anonymousJobArgs.JobName; jobName = dynamicJobArgs.JobName;
} }
else else
{ {

8
framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobManager_Tests.cs

@ -73,7 +73,7 @@ public class BackgroundJobManager_Tests : BackgroundJobsTestBase
var jobInfo = await _backgroundJobStore.FindAsync(Guid.Parse(jobIdAsString)); var jobInfo = await _backgroundJobStore.FindAsync(Guid.Parse(jobIdAsString));
jobInfo.ShouldNotBeNull(); jobInfo.ShouldNotBeNull();
jobInfo.JobName.ShouldBe(AnonymousJobArgs.JobNameConstant); jobInfo.JobName.ShouldBe(DynamicBackgroundJobArgs.JobNameConstant);
jobInfo.JobArgs.ShouldContain("TestDynamicJob"); jobInfo.JobArgs.ShouldContain("TestDynamicJob");
jobInfo.JobArgs.ShouldContain("ORD-001"); jobInfo.JobArgs.ShouldContain("ORD-001");
} }
@ -86,8 +86,8 @@ public class BackgroundJobManager_Tests : BackgroundJobsTestBase
await _backgroundJobExecuter.ExecuteAsync( await _backgroundJobExecuter.ExecuteAsync(
new JobExecutionContext( new JobExecutionContext(
ServiceProvider, ServiceProvider,
typeof(AnonymousJobExecutorAsyncBackgroundJob), typeof(DynamicBackgroundJobExecutorJob),
new AnonymousJobArgs("TestDynamicJob", "{\"OrderId\":\"ORD-001\"}") new DynamicBackgroundJobArgs("TestDynamicJob", "{\"OrderId\":\"ORD-001\"}")
) )
); );
@ -106,7 +106,7 @@ public class BackgroundJobManager_Tests : BackgroundJobsTestBase
var jobInfo = await _backgroundJobStore.FindAsync(Guid.Parse(jobIdAsString)); var jobInfo = await _backgroundJobStore.FindAsync(Guid.Parse(jobIdAsString));
jobInfo.ShouldNotBeNull(); jobInfo.ShouldNotBeNull();
jobInfo.JobName.ShouldBe(typedJobName); jobInfo.JobName.ShouldBe(typedJobName);
jobInfo.JobName.ShouldNotBe(AnonymousJobArgs.JobNameConstant); jobInfo.JobName.ShouldNotBe(DynamicBackgroundJobArgs.JobNameConstant);
} }
[Fact] [Fact]

4
modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/DemoAppSharedModule.cs

@ -15,7 +15,7 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Shared
{ {
var dynamicJobManager = context.ServiceProvider.GetRequiredService<IDynamicBackgroundJobManager>(); var dynamicJobManager = context.ServiceProvider.GetRequiredService<IDynamicBackgroundJobManager>();
dynamicJobManager.RegisterHandler("CompileTimeAnonymousJob", (ctx, ct) => dynamicJobManager.RegisterHandler("CompileTimeDynamicJob", (ctx, ct) =>
{ {
using (var doc = JsonDocument.Parse(ctx.JsonData)) using (var doc = JsonDocument.Parse(ctx.JsonData))
{ {
@ -24,7 +24,7 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Shared
: doc.RootElement.TryGetProperty("Value", out prop) : doc.RootElement.TryGetProperty("Value", out prop)
? prop.GetString() ? prop.GetString()
: null; : null;
Console.WriteLine($"[ANONYMOUS-COMPILE] {value}"); Console.WriteLine($"[DYNAMIC-COMPILE] {value}");
return Task.CompletedTask; return Task.CompletedTask;
} }
}); });

16
modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.Shared/Jobs/SampleJobCreator.cs

@ -31,7 +31,7 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs
await _backgroundJobManager.EnqueueAsync(new WriteToConsoleYellowJobArgs { Value = "test 1 (yellow) - typed" }); await _backgroundJobManager.EnqueueAsync(new WriteToConsoleYellowJobArgs { Value = "test 1 (yellow) - typed" });
// Register runtime dynamic handler // Register runtime dynamic handler
_dynamicBackgroundJobManager.RegisterHandler("RuntimeAnonymousJob", (context, ct) => _dynamicBackgroundJobManager.RegisterHandler("RuntimeDynamicJob", (context, ct) =>
{ {
using (var doc = JsonDocument.Parse(context.JsonData)) using (var doc = JsonDocument.Parse(context.JsonData))
{ {
@ -40,7 +40,7 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs
: doc.RootElement.TryGetProperty("Value", out prop) : doc.RootElement.TryGetProperty("Value", out prop)
? prop.GetString() ? prop.GetString()
: null; : null;
Console.WriteLine($"[ANONYMOUS-RUNTIME] {value}"); Console.WriteLine($"[DYNAMIC-RUNTIME] {value}");
return Task.CompletedTask; return Task.CompletedTask;
} }
}); });
@ -58,21 +58,21 @@ namespace Volo.Abp.BackgroundJobs.DemoApp.Shared.Jobs
// String-based enqueue with anonymous object (typed job path) // String-based enqueue with anonymous object (typed job path)
await _dynamicBackgroundJobManager.EnqueueAsync( await _dynamicBackgroundJobManager.EnqueueAsync(
"GreenJob", "GreenJob",
new { Value = "test 3 (green) - by name, anonymous", Time = DateTime.Now } new { Value = "test 3 (green) - by name, dynamic", Time = DateTime.Now }
); );
await _dynamicBackgroundJobManager.EnqueueAsync( await _dynamicBackgroundJobManager.EnqueueAsync(
"YellowJob", "YellowJob",
new { Value = "test 3 (yellow) - by name, anonymous", Time = DateTime.Now } new { Value = "test 3 (yellow) - by name, dynamic", Time = DateTime.Now }
); );
// Dynamic job handlers // Dynamic job handlers
await _dynamicBackgroundJobManager.EnqueueAsync( await _dynamicBackgroundJobManager.EnqueueAsync(
"CompileTimeAnonymousJob", "CompileTimeDynamicJob",
new { Value = "test 4 (anonymous) - compile-time" } new { Value = "test 4 (dynamic) - compile-time" }
); );
await _dynamicBackgroundJobManager.EnqueueAsync( await _dynamicBackgroundJobManager.EnqueueAsync(
"RuntimeAnonymousJob", "RuntimeDynamicJob",
new { Value = "test 5 (anonymous) - runtime" } new { Value = "test 5 (dynamic) - runtime" }
); );
} }
} }

Loading…
Cancel
Save