From bdcc69755cda31604c3762fb207f0d6eeb2b75c4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Sep 2025 18:57:35 +0000 Subject: [PATCH] Add TickerQ background worker integration Co-authored-by: hikalkan <1210527+hikalkan@users.noreply.github.com> --- .../background-workers/index.md | 5 +- .../background-workers/tickerq.md | 211 ++++++++++++++++++ .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 +++ .../Volo.Abp.BackgroundWorkers.TickerQ.csproj | 25 +++ .../AbpBackgroundWorkerTickerQOptions.cs | 31 +++ .../AbpBackgroundWorkersTickerQModule.cs | 37 +++ .../TickerQ/ITickerQBackgroundWorker.cs | 44 ++++ .../TickerQ/TickerQBackgroundWorkerBase.cs | 46 ++++ .../TickerQ/TickerQBackgroundWorkerManager.cs | 131 +++++++++++ .../TickerQPeriodicBackgroundWorkerAdapter.cs | 128 +++++++++++ 11 files changed, 689 insertions(+), 2 deletions(-) create mode 100644 docs/en/framework/infrastructure/background-workers/tickerq.md create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.TickerQ/FodyWeavers.xml create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.TickerQ/FodyWeavers.xsd create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo.Abp.BackgroundWorkers.TickerQ.csproj create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/AbpBackgroundWorkerTickerQOptions.cs create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/AbpBackgroundWorkersTickerQModule.cs create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/ITickerQBackgroundWorker.cs create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/TickerQBackgroundWorkerBase.cs create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/TickerQBackgroundWorkerManager.cs create mode 100644 framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/TickerQPeriodicBackgroundWorkerAdapter.cs diff --git a/docs/en/framework/infrastructure/background-workers/index.md b/docs/en/framework/infrastructure/background-workers/index.md index 6ab88a9e53..498a52ed9b 100644 --- a/docs/en/framework/infrastructure/background-workers/index.md +++ b/docs/en/framework/infrastructure/background-workers/index.md @@ -41,7 +41,7 @@ Start your worker in the `StartAsync` (which is called when the application begi Assume that we want to make a user passive, if the user has not logged in to the application in last 30 days. `AsyncPeriodicBackgroundWorkerBase` class simplifies to create periodic workers, so we will use it for the example below: -> You can use `CronExpression` property to set the cron expression for the background worker if you will use the [Hangfire Background Worker Manager](./hangfire.md) or [Quartz Background Worker Manager](./quartz.md). +> You can use `CronExpression` property to set the cron expression for the background worker if you will use the [Hangfire Background Worker Manager](./hangfire.md), [Quartz Background Worker Manager](./quartz.md), or [TickerQ Background Worker Manager](./tickerq.md). ````csharp public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase @@ -216,7 +216,8 @@ Background worker system is extensible and you can change the default background See pre-built worker manager alternatives: * [Quartz Background Worker Manager](./quartz.md) -* [Hangfire Background Worker Manager](./hangfire.md) +* [Hangfire Background Worker Manager](./hangfire.md) +* [TickerQ Background Worker Manager](./tickerq.md) ## See Also diff --git a/docs/en/framework/infrastructure/background-workers/tickerq.md b/docs/en/framework/infrastructure/background-workers/tickerq.md new file mode 100644 index 0000000000..eb89b218b9 --- /dev/null +++ b/docs/en/framework/infrastructure/background-workers/tickerq.md @@ -0,0 +1,211 @@ +# TickerQ Background Worker Manager + +[TickerQ](https://github.com/dotnetdevelopersdz/TickerQ) is a fast, reflection-free background task scheduler for .NET — built with source generators, EF Core integration, cron + time-based execution, and a real-time dashboard. You can integrate TickerQ with the ABP Framework to use it instead of the [default background worker manager](../background-workers). + +The major advantages of TickerQ include: +- **Performance**: Reflection-free design with source generators for optimal performance +- **EF Core Integration**: Native support for Entity Framework Core for job persistence +- **Flexible Scheduling**: Support for both cron expressions and time-based execution +- **Real-time Dashboard**: Built-in dashboard for monitoring and managing background jobs +- **Modern .NET**: Built for modern .NET with async/await support throughout + +## Installation + +It is suggested to use the [ABP CLI](../../../cli) to install this package. + +### Using the ABP CLI + +Open a command line window in the folder of the project (.csproj file) and type the following command: + +````bash +abp add-package Volo.Abp.BackgroundWorkers.TickerQ +```` + +### Manual Installation + +If you want to manually install: + +1. Add the [Volo.Abp.BackgroundWorkers.TickerQ](https://www.nuget.org/packages/Volo.Abp.BackgroundWorkers.TickerQ) NuGet package to your project: + + ```` + dotnet add package Volo.Abp.BackgroundWorkers.TickerQ + ```` + +2. Add the `AbpBackgroundWorkersTickerQModule` to the dependency list of your module: + +````csharp +[DependsOn( + //...other dependencies + typeof(AbpBackgroundWorkersTickerQModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ +} +```` + +> TickerQ background worker integration provides an adapter `TickerQPeriodicBackgroundWorkerAdapter` to automatically load any `PeriodicBackgroundWorkerBase` and `AsyncPeriodicBackgroundWorkerBase` derived classes as `ITickerQBackgroundWorker` instances. This allows you to easily switch over to use TickerQ as the background manager even if you have existing background workers that are based on the [default background workers implementation](../background-workers). + +## Configuration + +You need to configure TickerQ with your preferred storage provider. TickerQ supports various storage options including Entity Framework Core. + +1. First, configure TickerQ in your module's `ConfigureServices` method: + +````csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + var configuration = context.Services.GetConfiguration(); + var hostingEnvironment = context.Services.GetHostingEnvironment(); + + //... other configurations. + + ConfigureTickerQ(context, configuration); +} + +private void ConfigureTickerQ(ServiceConfigurationContext context, IConfiguration configuration) +{ + // TODO: Configure TickerQ here when the package becomes available + // This would typically involve setting up the database connection, + // configuring the scheduler options, and setting up the dashboard +} +```` + +2. You can configure the ABP TickerQ integration options: + +````csharp +Configure(options => +{ + options.IsAutoRegisterEnabled = true; // Auto-register TickerQ workers + options.DefaultCronExpression = "0 * * ? * *"; // Default: every minute + options.DefaultMaxRetryAttempts = 3; // Default retry attempts + options.DefaultPriority = 0; // Default priority +}); +```` + +## Create a Background Worker + +`TickerQBackgroundWorkerBase` is an easy way to create a background worker. + +````csharp +public class MyLogWorker : TickerQBackgroundWorkerBase +{ + public MyLogWorker() + { + JobId = nameof(MyLogWorker); + CronExpression = "0 */10 * ? * *"; // Every 10 minutes + Priority = 1; // Higher priority + MaxRetryAttempts = 5; // Retry up to 5 times on failure + } + + public override Task DoWorkAsync(CancellationToken cancellationToken = default) + { + Logger.LogInformation("Executed MyLogWorker with TickerQ!"); + return Task.CompletedTask; + } +} +```` + +### Properties + +* **JobId** - A unique identifier for the job (optional, defaults to the class name) +* **CronExpression** - A CRON expression for scheduling (see [CRON expression](https://en.wikipedia.org/wiki/Cron#CRON_expression)) +* **Priority** - Job priority (higher values = higher priority, default is 0) +* **MaxRetryAttempts** - Maximum number of retry attempts on failure (default is 3) +* **AutoRegister** - Whether to automatically register this worker (default is true) + +> You can directly implement the `ITickerQBackgroundWorker` interface, but `TickerQBackgroundWorkerBase` provides useful properties like Logger and service access. + +## Register Background Workers + +TickerQ background workers are automatically registered if `AutoRegister` is `true` (default). However, you can also manually register them: + +````csharp +[DependsOn(typeof(AbpBackgroundWorkersTickerQModule))] +public class MyModule : AbpModule +{ + public override async Task OnApplicationInitializationAsync( + ApplicationInitializationContext context) + { + await context.AddBackgroundWorkerAsync(); + } +} +```` + +## Migrating from Other Background Worker Implementations + +TickerQ integration provides adapters for existing background workers: + +### From Default Background Workers + +Existing `AsyncPeriodicBackgroundWorkerBase` and `PeriodicBackgroundWorkerBase` workers will automatically work with TickerQ through the adapter system. The adapter will convert timer periods to appropriate cron expressions. + +### From Quartz or Hangfire + +When migrating from Quartz or Hangfire, you can: + +1. Keep existing workers unchanged (they'll work through adapters) +2. Gradually migrate to `TickerQBackgroundWorkerBase` for better performance and features +3. Use the native TickerQ features like source generator optimizations + +## Dashboard Integration + +TickerQ provides a real-time dashboard for monitoring background jobs. To enable the dashboard: + +````csharp +public override void OnApplicationInitialization(ApplicationInitializationContext context) +{ + var app = context.GetApplicationBuilder(); + + // ... others + + // TODO: Add TickerQ dashboard integration when available + // app.UseTickerQDashboard("/tickerq"); + + app.UseConfiguredEndpoints(); +} +```` + +## Advanced Features + +### Source Generator Optimizations + +TickerQ uses source generators to eliminate reflection and improve performance. When using TickerQ-specific features, your jobs will benefit from: + +- Compile-time job registration +- Zero-allocation job execution +- Optimized serialization + +### EF Core Integration + +TickerQ provides native Entity Framework Core integration for job persistence: + +````csharp +public class MyEfCoreWorker : TickerQBackgroundWorkerBase +{ + private readonly IRepository _repository; + + public MyEfCoreWorker(IRepository repository) + { + _repository = repository; + JobId = nameof(MyEfCoreWorker); + CronExpression = "0 0 2 ? * *"; // Daily at 2 AM + } + + public override async Task DoWorkAsync(CancellationToken cancellationToken = default) + { + // Work with EF Core entities + var entities = await _repository.GetListAsync(cancellationToken: cancellationToken); + + // Process entities... + + Logger.LogInformation("Processed {Count} entities", entities.Count); + } +} +```` + +## See Also + +* [Background Workers](../background-workers) +* [Background Jobs](../background-jobs) +* [Quartz Background Worker Manager](./quartz.md) +* [Hangfire Background Worker Manager](./hangfire.md) \ No newline at end of file diff --git a/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/FodyWeavers.xml b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/FodyWeavers.xml new file mode 100644 index 0000000000..1715698ccd --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/FodyWeavers.xsd b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/FodyWeavers.xsd new file mode 100644 index 0000000000..ffa6fc4b78 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo.Abp.BackgroundWorkers.TickerQ.csproj b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo.Abp.BackgroundWorkers.TickerQ.csproj new file mode 100644 index 0000000000..9782dd0c10 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo.Abp.BackgroundWorkers.TickerQ.csproj @@ -0,0 +1,25 @@ + + + + + + + netstandard2.0;netstandard2.1;net8.0;net9.0 + enable + Nullable + Volo.Abp.BackgroundWorkers.TickerQ + Volo.Abp.BackgroundWorkers.TickerQ + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/AbpBackgroundWorkerTickerQOptions.cs b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/AbpBackgroundWorkerTickerQOptions.cs new file mode 100644 index 0000000000..d782505bf3 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/AbpBackgroundWorkerTickerQOptions.cs @@ -0,0 +1,31 @@ +namespace Volo.Abp.BackgroundWorkers.TickerQ; + +/// +/// Options for TickerQ background workers. +/// +public class AbpBackgroundWorkerTickerQOptions +{ + /// + /// Gets or sets whether automatic registration is enabled for TickerQ workers. + /// Default is true. + /// + public bool IsAutoRegisterEnabled { get; set; } = true; + + /// + /// Gets or sets the default cron expression for workers that don't specify one. + /// Default is every minute: "0 * * ? * *" + /// + public string DefaultCronExpression { get; set; } = "0 * * ? * *"; + + /// + /// Gets or sets the default maximum retry attempts. + /// Default is 3. + /// + public int DefaultMaxRetryAttempts { get; set; } = 3; + + /// + /// Gets or sets the default priority for workers. + /// Default is 0 (normal priority). + /// + public int DefaultPriority { get; set; } = 0; +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/AbpBackgroundWorkersTickerQModule.cs b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/AbpBackgroundWorkersTickerQModule.cs new file mode 100644 index 0000000000..94404da0d2 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/AbpBackgroundWorkersTickerQModule.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BackgroundWorkers.TickerQ; + +/// +/// ABP module for TickerQ background workers integration. +/// TickerQ is a fast, reflection-free background task scheduler for .NET — built with source generators, +/// EF Core integration, cron + time-based execution, and a real-time dashboard. +/// +[DependsOn( + typeof(AbpBackgroundWorkersModule))] +public class AbpBackgroundWorkersTickerQModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + // Register TickerQ-specific services + // The TickerQBackgroundWorkerManager will automatically replace the default manager + // due to the [Dependency(ReplaceServices = true)] attribute + } + + public override void OnPreApplicationInitialization(ApplicationInitializationContext context) + { + // Check if background workers are enabled + var options = context.ServiceProvider.GetRequiredService>().Value; + if (!options.IsEnabled) + { + // If background workers are disabled, we don't need to initialize TickerQ + return; + } + + // Initialize TickerQ background worker manager + var tickerQManager = context.ServiceProvider.GetRequiredService(); + tickerQManager.Initialize(); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/ITickerQBackgroundWorker.cs b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/ITickerQBackgroundWorker.cs new file mode 100644 index 0000000000..fd4708cde1 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/ITickerQBackgroundWorker.cs @@ -0,0 +1,44 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Volo.Abp.BackgroundWorkers.TickerQ; + +/// +/// Interface for TickerQ background workers. +/// TickerQ is a fast, reflection-free background task scheduler for .NET. +/// +public interface ITickerQBackgroundWorker : IBackgroundWorker +{ + /// + /// Gets or sets the cron expression for the job scheduling. + /// + string? CronExpression { get; set; } + + /// + /// Gets or sets the job identifier. + /// + string? JobId { get; set; } + + /// + /// Gets or sets whether to automatically register this worker. + /// Default is true. + /// + bool AutoRegister { get; set; } + + /// + /// Gets or sets the job priority. + /// + int Priority { get; set; } + + /// + /// Gets or sets the maximum retry attempts for failed jobs. + /// + int MaxRetryAttempts { get; set; } + + /// + /// The main work execution method. + /// + /// The cancellation token. + /// A task representing the work execution. + Task DoWorkAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/TickerQBackgroundWorkerBase.cs b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/TickerQBackgroundWorkerBase.cs new file mode 100644 index 0000000000..75f233b091 --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/TickerQBackgroundWorkerBase.cs @@ -0,0 +1,46 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Volo.Abp.BackgroundWorkers.TickerQ; + +/// +/// Base class for TickerQ background workers. +/// TickerQ is a fast, reflection-free background task scheduler for .NET. +/// +public abstract class TickerQBackgroundWorkerBase : BackgroundWorkerBase, ITickerQBackgroundWorker +{ + /// + /// Gets or sets the cron expression for the job scheduling. + /// + public string? CronExpression { get; set; } + + /// + /// Gets or sets the job identifier. + /// + public string? JobId { get; set; } + + /// + /// Gets or sets whether to automatically register this worker. + /// Default is true. + /// + public bool AutoRegister { get; set; } = true; + + /// + /// Gets or sets the job priority. + /// Default is 0 (normal priority). + /// + public int Priority { get; set; } = 0; + + /// + /// Gets or sets the maximum retry attempts for failed jobs. + /// Default is 3. + /// + public int MaxRetryAttempts { get; set; } = 3; + + /// + /// The main work execution method that must be implemented by derived classes. + /// + /// The cancellation token. + /// A task representing the work execution. + public abstract Task DoWorkAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/TickerQBackgroundWorkerManager.cs b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/TickerQBackgroundWorkerManager.cs new file mode 100644 index 0000000000..21629bfc8e --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/TickerQBackgroundWorkerManager.cs @@ -0,0 +1,131 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; + +namespace Volo.Abp.BackgroundWorkers.TickerQ; + +/// +/// TickerQ implementation of the background worker manager. +/// Replaces the default background worker manager when TickerQ integration is enabled. +/// +[Dependency(ReplaceServices = true)] +public class TickerQBackgroundWorkerManager : BackgroundWorkerManager, ISingletonDependency +{ + private readonly AbpBackgroundWorkerTickerQOptions _options; + private readonly IServiceProvider _serviceProvider; + private bool _isInitialized; + + public TickerQBackgroundWorkerManager( + IOptions options, + IServiceProvider serviceProvider) + { + _options = options.Value; + _serviceProvider = serviceProvider; + } + + public override async Task StartAsync(CancellationToken cancellationToken = default) + { + Logger.LogInformation("Starting TickerQ Background Worker Manager..."); + + if (!_isInitialized) + { + await InitializeAsync(); + } + + await base.StartAsync(cancellationToken); + + Logger.LogInformation("TickerQ Background Worker Manager started."); + } + + public override async Task StopAsync(CancellationToken cancellationToken = default) + { + Logger.LogInformation("Stopping TickerQ Background Worker Manager..."); + + await base.StopAsync(cancellationToken); + + Logger.LogInformation("TickerQ Background Worker Manager stopped."); + } + + public override async Task AddAsync(IBackgroundWorker worker, CancellationToken cancellationToken = default) + { + if (worker is ITickerQBackgroundWorker tickerQWorker) + { + await ScheduleTickerQJobAsync(tickerQWorker, cancellationToken); + } + else + { + // For non-TickerQ workers, use the default behavior + await base.AddAsync(worker, cancellationToken); + } + } + + protected virtual async Task InitializeAsync() + { + Logger.LogDebug("Initializing TickerQ Background Worker Manager..."); + + // TODO: Initialize TickerQ scheduler here when the actual TickerQ library is available + // This would involve setting up the TickerQ configuration, database connections, etc. + + _isInitialized = true; + + Logger.LogDebug("TickerQ Background Worker Manager initialized."); + + await Task.CompletedTask; + } + + protected virtual async Task ScheduleTickerQJobAsync(ITickerQBackgroundWorker worker, CancellationToken cancellationToken = default) + { + Logger.LogInformation("Scheduling TickerQ job: {JobId}", worker.JobId ?? worker.GetType().Name); + + try + { + // TODO: Implement actual TickerQ job scheduling when the library is available + // This would involve: + // 1. Creating a TickerQ job definition + // 2. Setting up the cron expression or time-based trigger + // 3. Configuring retry policy and priority + // 4. Registering the job with TickerQ scheduler + + // For now, we'll just log the configuration + Logger.LogDebug("TickerQ job configuration: JobId={JobId}, CronExpression={CronExpression}, Priority={Priority}, MaxRetryAttempts={MaxRetryAttempts}", + worker.JobId ?? worker.GetType().Name, + worker.CronExpression ?? _options.DefaultCronExpression, + worker.Priority, + worker.MaxRetryAttempts); + + await Task.CompletedTask; + } + catch (Exception ex) + { + Logger.LogError(ex, "Failed to schedule TickerQ job: {JobId}", worker.JobId ?? worker.GetType().Name); + throw; + } + } + + public void Initialize() + { + // Automatically register TickerQ background workers if auto-registration is enabled + if (!_options.IsAutoRegisterEnabled) + { + return; + } + + Logger.LogDebug("Auto-registering TickerQ background workers..."); + + var backgroundWorkers = _serviceProvider.GetServices(); + foreach (var backgroundWorker in backgroundWorkers) + { + if (backgroundWorker is ITickerQBackgroundWorker tickerQWorker && tickerQWorker.AutoRegister) + { + AddAsync(tickerQWorker).ConfigureAwait(false).GetAwaiter().GetResult(); + } + } + + Logger.LogDebug("Auto-registration of TickerQ background workers completed."); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/TickerQPeriodicBackgroundWorkerAdapter.cs b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/TickerQPeriodicBackgroundWorkerAdapter.cs new file mode 100644 index 0000000000..e1a87503fa --- /dev/null +++ b/framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/TickerQPeriodicBackgroundWorkerAdapter.cs @@ -0,0 +1,128 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.BackgroundWorkers.TickerQ; + +/// +/// Adapter to enable existing periodic background workers to work with TickerQ. +/// This allows users to migrate from the default background worker implementation to TickerQ +/// without changing their existing worker code. +/// +public class TickerQPeriodicBackgroundWorkerAdapter : TickerQBackgroundWorkerBase, ITransientDependency + where TWorker : class, IBackgroundWorker +{ + private readonly IServiceProvider _serviceProvider; + + public TickerQPeriodicBackgroundWorkerAdapter(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + + // Set default job ID based on the worker type + JobId = typeof(TWorker).Name; + } + + public override async Task DoWorkAsync(CancellationToken cancellationToken = default) + { + Logger.LogDebug("Executing adapted periodic worker: {WorkerType}", typeof(TWorker).Name); + + using var scope = _serviceProvider.CreateScope(); + var worker = scope.ServiceProvider.GetRequiredService(); + + try + { + if (worker is IPeriodicBackgroundWorker periodicWorker) + { + await periodicWorker.DoWorkAsync(cancellationToken); + } + else if (worker is AsyncPeriodicBackgroundWorkerBase asyncPeriodicWorker) + { + await asyncPeriodicWorker.DoWorkAsync(cancellationToken); + } + else if (worker is PeriodicBackgroundWorkerBase syncPeriodicWorker) + { + syncPeriodicWorker.DoWork(); + await Task.CompletedTask; + } + else + { + Logger.LogWarning("Worker {WorkerType} is not a supported periodic worker type", typeof(TWorker).Name); + } + } + catch (Exception ex) + { + Logger.LogError(ex, "Error executing adapted periodic worker: {WorkerType}", typeof(TWorker).Name); + throw; + } + } + + /// + /// Configures the adapter based on the original worker's settings. + /// + public virtual TickerQPeriodicBackgroundWorkerAdapter Configure(TWorker originalWorker) + { + // Try to extract timing information from the original worker + if (originalWorker is AsyncPeriodicBackgroundWorkerBase asyncWorker) + { + // Convert timer period to cron expression (approximate) + if (asyncWorker.Timer?.Period != null) + { + var periodMinutes = asyncWorker.Timer.Period / 60000; // Convert ms to minutes + if (periodMinutes < 1) + { + CronExpression = "*/30 * * ? * *"; // Every 30 seconds for very short periods + } + else if (periodMinutes == 1) + { + CronExpression = "0 * * ? * *"; // Every minute + } + else if (periodMinutes < 60) + { + CronExpression = $"0 */{periodMinutes} * ? * *"; // Every N minutes + } + else + { + var hours = periodMinutes / 60; + CronExpression = $"0 0 */{hours} ? * *"; // Every N hours + } + } + + // Use cron expression if available + if (!string.IsNullOrEmpty(asyncWorker.CronExpression)) + { + CronExpression = asyncWorker.CronExpression; + } + } + else if (originalWorker is PeriodicBackgroundWorkerBase syncWorker) + { + // Similar logic for sync workers + if (syncWorker.Timer?.Period != null) + { + var periodMinutes = syncWorker.Timer.Period / 60000; + if (periodMinutes < 1) + { + CronExpression = "*/30 * * ? * *"; + } + else if (periodMinutes == 1) + { + CronExpression = "0 * * ? * *"; + } + else if (periodMinutes < 60) + { + CronExpression = $"0 */{periodMinutes} * ? * *"; + } + else + { + var hours = periodMinutes / 60; + CronExpression = $"0 0 */{hours} ? * *"; + } + } + } + + return this; + } +} \ No newline at end of file