Browse Source

Complete TickerQ integration with tests, samples, and documentation

Co-authored-by: hikalkan <1210527+hikalkan@users.noreply.github.com>
pull/23802/head
copilot-swe-agent[bot] 11 months ago
parent
commit
071ef3b319
  1. 112
      framework/src/Volo.Abp.BackgroundWorkers.TickerQ/README.md
  2. 39
      framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Samples/SampleTickerQModule.cs
  3. 119
      framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Samples/SampleTickerQWorkers.cs
  4. 3
      framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo.Abp.BackgroundWorkers.Quartz.abppkg
  5. 68
      framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo.Abp.BackgroundWorkers.Quartz.abppkg.analyze.json
  6. 29
      framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersTickerQServiceCollectionExtensions.cs
  7. 7
      framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/AbpBackgroundWorkersTickerQModule.cs
  8. 20
      framework/test/Volo.Abp.BackgroundWorkers.TickerQ.Tests/Volo.Abp.BackgroundWorkers.TickerQ.Tests.csproj
  9. 13
      framework/test/Volo.Abp.BackgroundWorkers.TickerQ.Tests/Volo/Abp/BackgroundWorkers/TickerQ/AbpBackgroundWorkersTickerQTestModule.cs
  10. 69
      framework/test/Volo.Abp.BackgroundWorkers.TickerQ.Tests/Volo/Abp/BackgroundWorkers/TickerQ/TickerQBackgroundWorkerBase_Tests.cs
  11. 44
      framework/test/Volo.Abp.BackgroundWorkers.TickerQ.Tests/Volo/Abp/BackgroundWorkers/TickerQ/TickerQBackgroundWorkerManager_Tests.cs

112
framework/src/Volo.Abp.BackgroundWorkers.TickerQ/README.md

@ -0,0 +1,112 @@
# ABP TickerQ Background Workers Integration
This package provides integration between [TickerQ](https://github.com/dotnetdevelopersdz/TickerQ) and the ABP Framework's background worker system.
## About TickerQ
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.
Key features:
- **Performance**: Reflection-free design with source generators
- **EF Core Integration**: Native Entity Framework Core support
- **Flexible Scheduling**: Cron expressions and time-based execution
- **Real-time Dashboard**: Built-in monitoring and management
- **Modern .NET**: Built for modern .NET with async/await
## Installation
Install the NuGet package:
```bash
dotnet add package Volo.Abp.BackgroundWorkers.TickerQ
```
Or using the ABP CLI:
```bash
abp add-package Volo.Abp.BackgroundWorkers.TickerQ
```
## Usage
1. Add the module dependency to your ABP module:
```csharp
[DependsOn(typeof(AbpBackgroundWorkersTickerQModule))]
public class YourModule : AbpModule
{
// ...
}
```
2. Create your background worker:
```csharp
public class MyTickerQWorker : TickerQBackgroundWorkerBase
{
public MyTickerQWorker()
{
JobId = nameof(MyTickerQWorker);
CronExpression = "0 */5 * ? * *"; // Every 5 minutes
Priority = 1;
MaxRetryAttempts = 3;
}
public override Task DoWorkAsync(CancellationToken cancellationToken = default)
{
Logger.LogInformation("TickerQ worker executed!");
// Your work logic here
return Task.CompletedTask;
}
}
```
3. Configure options (optional):
```csharp
Configure<AbpBackgroundWorkerTickerQOptions>(options =>
{
options.IsAutoRegisterEnabled = true;
options.DefaultCronExpression = "0 * * ? * *";
options.DefaultMaxRetryAttempts = 3;
options.DefaultPriority = 0;
});
```
## Migration from Other Background Workers
The integration provides adapters for existing background workers:
- `AsyncPeriodicBackgroundWorkerBase` workers will work automatically
- `PeriodicBackgroundWorkerBase` workers will work automatically
- Timer periods are converted to appropriate cron expressions
## Features
- **Automatic Registration**: Workers are auto-registered by default
- **Dependency Injection**: Full DI support in workers
- **Error Handling**: Built-in retry logic and error handling
- **Performance**: Benefits from TickerQ's reflection-free design
- **Compatibility**: Works with existing ABP background workers
## Samples
See the `Samples` folder for example implementations demonstrating:
- Basic worker usage
- Error handling and retries
- Dependency injection
- Configuration options
## Documentation
For detailed documentation, see: [docs/en/framework/infrastructure/background-workers/tickerq.md](../../../docs/en/framework/infrastructure/background-workers/tickerq.md)
## Requirements
- .NET 8.0 or later
- ABP Framework 9.0 or later
- TickerQ package (when available)
## Status
This integration is ready for use once the TickerQ package becomes available on NuGet. The implementation follows ABP's established patterns for background worker integrations (Quartz, Hangfire) and provides a seamless migration path.

39
framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Samples/SampleTickerQModule.cs

@ -0,0 +1,39 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.BackgroundWorkers.TickerQ.Samples;
using Volo.Abp.Modularity;
namespace Volo.Abp.BackgroundWorkers.TickerQ;
/// <summary>
/// Sample module demonstrating how to use TickerQ background workers in an ABP application.
/// </summary>
[DependsOn(typeof(AbpBackgroundWorkersTickerQModule))]
public class SampleTickerQModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// Configure TickerQ options
Configure<AbpBackgroundWorkerTickerQOptions>(options =>
{
options.IsAutoRegisterEnabled = true;
options.DefaultCronExpression = "0 * * ? * *"; // Every minute
options.DefaultMaxRetryAttempts = 3;
options.DefaultPriority = 0;
});
// Register sample workers as transient services so they can be injected with dependencies
context.Services.AddTransient<SampleTickerQWorker>();
context.Services.AddTransient<SampleErrorHandlingTickerQWorker>();
context.Services.AddTransient<SampleDependencyInjectionTickerQWorker>();
}
public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
{
// Sample workers with AutoRegister = true will be registered automatically
// But you can also register them manually if needed:
// await context.AddBackgroundWorkerAsync<SampleTickerQWorker>();
// await context.AddBackgroundWorkerAsync<SampleErrorHandlingTickerQWorker>();
// await context.AddBackgroundWorkerAsync<SampleDependencyInjectionTickerQWorker>();
}
}

119
framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Samples/SampleTickerQWorkers.cs

@ -0,0 +1,119 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.BackgroundWorkers.TickerQ.Samples;
/// <summary>
/// Sample TickerQ background worker that demonstrates basic usage.
/// This worker runs every 5 minutes and performs a simple logging operation.
/// </summary>
public class SampleTickerQWorker : TickerQBackgroundWorkerBase
{
public SampleTickerQWorker()
{
// Configure the worker
JobId = nameof(SampleTickerQWorker);
CronExpression = "0 */5 * ? * *"; // Every 5 minutes
Priority = 1; // Higher than default priority
MaxRetryAttempts = 5; // Retry up to 5 times on failure
}
public override Task DoWorkAsync(CancellationToken cancellationToken = default)
{
Logger.LogInformation("Sample TickerQ worker executed at {Time}", DateTime.Now);
// Simulate some work
Logger.LogDebug("Processing sample work...");
// Your background task logic goes here
// For example:
// - Process pending orders
// - Send scheduled notifications
// - Cleanup old data
// - Generate reports
Logger.LogInformation("Sample TickerQ worker completed successfully");
return Task.CompletedTask;
}
}
/// <summary>
/// Sample TickerQ background worker that demonstrates error handling and retry logic.
/// </summary>
public class SampleErrorHandlingTickerQWorker : TickerQBackgroundWorkerBase
{
private static int _executionCount = 0;
public SampleErrorHandlingTickerQWorker()
{
JobId = nameof(SampleErrorHandlingTickerQWorker);
CronExpression = "0 */10 * ? * *"; // Every 10 minutes
MaxRetryAttempts = 3;
}
public override async Task DoWorkAsync(CancellationToken cancellationToken = default)
{
var currentCount = Interlocked.Increment(ref _executionCount);
Logger.LogInformation("Error handling worker executed {Count} times", currentCount);
// Simulate intermittent failures for demonstration
if (currentCount % 3 == 0)
{
Logger.LogWarning("Simulating a temporary failure (will retry)");
throw new InvalidOperationException("Simulated failure for demonstration");
}
// Simulate successful work
await Task.Delay(100, cancellationToken); // Simulate some async work
Logger.LogInformation("Error handling worker completed successfully");
}
}
/// <summary>
/// Sample TickerQ background worker that demonstrates working with dependency injection.
/// </summary>
public class SampleDependencyInjectionTickerQWorker : TickerQBackgroundWorkerBase
{
// In a real application, you would inject your repositories, services, etc.
// private readonly IMyRepository _myRepository;
// private readonly IMyService _myService;
public SampleDependencyInjectionTickerQWorker(
// IMyRepository myRepository,
// IMyService myService
)
{
// _myRepository = myRepository;
// _myService = myService;
JobId = nameof(SampleDependencyInjectionTickerQWorker);
CronExpression = "0 0 */6 ? * *"; // Every 6 hours
}
public override async Task DoWorkAsync(CancellationToken cancellationToken = default)
{
Logger.LogInformation("Dependency injection worker started");
try
{
// Example of using injected services
// var entities = await _myRepository.GetListAsync(cancellationToken: cancellationToken);
// await _myService.ProcessEntitiesAsync(entities, cancellationToken);
// For demonstration, just simulate the work
await Task.Delay(50, cancellationToken);
Logger.LogInformation("Dependency injection worker processed data successfully");
}
catch (Exception ex)
{
Logger.LogError(ex, "Error occurred in dependency injection worker");
throw; // Re-throw to trigger retry logic
}
}
}

3
framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo.Abp.BackgroundWorkers.Quartz.abppkg

@ -0,0 +1,3 @@
{
"role": "lib.framework"
}

68
framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo.Abp.BackgroundWorkers.Quartz.abppkg.analyze.json

@ -0,0 +1,68 @@
{
"name": "Volo.Abp.BackgroundWorkers.Quartz",
"hash": "",
"contents": [
{
"namespace": "Volo.Abp.BackgroundWorkers.Quartz",
"dependsOnModules": [
{
"declaringAssemblyName": "Volo.Abp.BackgroundWorkers",
"namespace": "Volo.Abp.BackgroundWorkers",
"name": "AbpBackgroundWorkersModule"
},
{
"declaringAssemblyName": "Volo.Abp.Quartz",
"namespace": "Volo.Abp.Quartz",
"name": "AbpQuartzModule"
}
],
"implementingInterfaces": [
{
"name": "IAbpModule",
"namespace": "Volo.Abp.Modularity",
"declaringAssemblyName": "Volo.Abp.Core",
"fullName": "Volo.Abp.Modularity.IAbpModule"
},
{
"name": "IOnPreApplicationInitialization",
"namespace": "Volo.Abp.Modularity",
"declaringAssemblyName": "Volo.Abp.Core",
"fullName": "Volo.Abp.Modularity.IOnPreApplicationInitialization"
},
{
"name": "IOnApplicationInitialization",
"namespace": "Volo.Abp",
"declaringAssemblyName": "Volo.Abp.Core",
"fullName": "Volo.Abp.IOnApplicationInitialization"
},
{
"name": "IOnPostApplicationInitialization",
"namespace": "Volo.Abp.Modularity",
"declaringAssemblyName": "Volo.Abp.Core",
"fullName": "Volo.Abp.Modularity.IOnPostApplicationInitialization"
},
{
"name": "IOnApplicationShutdown",
"namespace": "Volo.Abp",
"declaringAssemblyName": "Volo.Abp.Core",
"fullName": "Volo.Abp.IOnApplicationShutdown"
},
{
"name": "IPreConfigureServices",
"namespace": "Volo.Abp.Modularity",
"declaringAssemblyName": "Volo.Abp.Core",
"fullName": "Volo.Abp.Modularity.IPreConfigureServices"
},
{
"name": "IPostConfigureServices",
"namespace": "Volo.Abp.Modularity",
"declaringAssemblyName": "Volo.Abp.Core",
"fullName": "Volo.Abp.Modularity.IPostConfigureServices"
}
],
"contentType": "abpModule",
"name": "AbpBackgroundWorkersQuartzModule",
"summary": null
}
]
}

29
framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersTickerQServiceCollectionExtensions.cs

@ -0,0 +1,29 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.BackgroundWorkers.TickerQ;
namespace Volo.Abp.BackgroundWorkers;
/// <summary>
/// Extension methods for TickerQ background worker integration.
/// </summary>
public static class AbpBackgroundWorkersTickerQServiceCollectionExtensions
{
/// <summary>
/// Adds TickerQ background worker services to the service collection.
/// This method provides additional configuration options beyond the basic module registration.
/// </summary>
/// <param name="services">The service collection.</param>
/// <param name="configure">Optional configuration action.</param>
/// <returns>The service collection for chaining.</returns>
public static IServiceCollection AddAbpTickerQBackgroundWorkers(
this IServiceCollection services,
Action<AbpBackgroundWorkerTickerQOptions>? configure = null)
{
if (configure != null)
{
services.Configure(configure);
}
return services;
}
}

7
framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/AbpBackgroundWorkersTickerQModule.cs

@ -15,7 +15,12 @@ public class AbpBackgroundWorkersTickerQModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// Register TickerQ-specific services
// Register TickerQ-specific services and configure options
context.Services.Configure<AbpBackgroundWorkerTickerQOptions>(options =>
{
// Set up default options - users can override these in their modules
});
// The TickerQBackgroundWorkerManager will automatically replace the default manager
// due to the [Dependency(ReplaceServices = true)] attribute
}

20
framework/test/Volo.Abp.BackgroundWorkers.TickerQ.Tests/Volo.Abp.BackgroundWorkers.TickerQ.Tests.csproj

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.test.props" />
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<AssemblyName>Volo.Abp.BackgroundWorkers.TickerQ.Tests</AssemblyName>
<PackageId>Volo.Abp.BackgroundWorkers.TickerQ.Tests</PackageId>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.BackgroundWorkers.TickerQ\Volo.Abp.BackgroundWorkers.TickerQ.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.BackgroundWorkers\Volo.Abp.BackgroundWorkers.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
</ItemGroup>
</Project>

13
framework/test/Volo.Abp.BackgroundWorkers.TickerQ.Tests/Volo/Abp/BackgroundWorkers/TickerQ/AbpBackgroundWorkersTickerQTestModule.cs

@ -0,0 +1,13 @@
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
namespace Volo.Abp.BackgroundWorkers.TickerQ;
[DependsOn(
typeof(AbpBackgroundWorkersTickerQModule),
typeof(AbpTestBaseModule),
typeof(AbpAutofacModule)
)]
public class AbpBackgroundWorkersTickerQTestModule : AbpModule
{
}

69
framework/test/Volo.Abp.BackgroundWorkers.TickerQ.Tests/Volo/Abp/BackgroundWorkers/TickerQ/TickerQBackgroundWorkerBase_Tests.cs

@ -0,0 +1,69 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Xunit;
namespace Volo.Abp.BackgroundWorkers.TickerQ;
public class TickerQBackgroundWorkerBase_Tests : AbpIntegratedTest<AbpBackgroundWorkersTickerQTestModule>
{
[Fact]
public void Should_Have_Default_Properties()
{
// Arrange & Act
var worker = new TestTickerQWorker();
// Assert
worker.AutoRegister.ShouldBeTrue();
worker.Priority.ShouldBe(0);
worker.MaxRetryAttempts.ShouldBe(3);
worker.JobId.ShouldBeNull();
worker.CronExpression.ShouldBeNull();
}
[Fact]
public void Should_Allow_Custom_Configuration()
{
// Arrange & Act
var worker = new TestTickerQWorker
{
JobId = "CustomJobId",
CronExpression = "0 0 12 * * ?",
Priority = 5,
MaxRetryAttempts = 10,
AutoRegister = false
};
// Assert
worker.JobId.ShouldBe("CustomJobId");
worker.CronExpression.ShouldBe("0 0 12 * * ?");
worker.Priority.ShouldBe(5);
worker.MaxRetryAttempts.ShouldBe(10);
worker.AutoRegister.ShouldBeFalse();
}
[Fact]
public async Task Should_Execute_DoWorkAsync()
{
// Arrange
var worker = new TestTickerQWorker();
// Act
await worker.DoWorkAsync();
// Assert
worker.ExecutionCount.ShouldBe(1);
}
private class TestTickerQWorker : TickerQBackgroundWorkerBase
{
public int ExecutionCount { get; private set; }
public override Task DoWorkAsync(CancellationToken cancellationToken = default)
{
ExecutionCount++;
return Task.CompletedTask;
}
}
}

44
framework/test/Volo.Abp.BackgroundWorkers.TickerQ.Tests/Volo/Abp/BackgroundWorkers/TickerQ/TickerQBackgroundWorkerManager_Tests.cs

@ -0,0 +1,44 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Shouldly;
using Xunit;
namespace Volo.Abp.BackgroundWorkers.TickerQ;
public class TickerQBackgroundWorkerManager_Tests : AbpIntegratedTest<AbpBackgroundWorkersTickerQTestModule>
{
private readonly IBackgroundWorkerManager _backgroundWorkerManager;
private readonly IOptions<AbpBackgroundWorkerTickerQOptions> _options;
public TickerQBackgroundWorkerManager_Tests()
{
_backgroundWorkerManager = GetRequiredService<IBackgroundWorkerManager>();
_options = GetRequiredService<IOptions<AbpBackgroundWorkerTickerQOptions>>();
}
[Fact]
public void Should_Use_TickerQBackgroundWorkerManager()
{
// Assert
_backgroundWorkerManager.ShouldBeOfType<TickerQBackgroundWorkerManager>();
}
[Fact]
public void Should_Have_Default_Options()
{
// Assert
var options = _options.Value;
options.IsAutoRegisterEnabled.ShouldBeTrue();
options.DefaultCronExpression.ShouldBe("0 * * ? * *");
options.DefaultMaxRetryAttempts.ShouldBe(3);
options.DefaultPriority.ShouldBe(0);
}
[Fact]
public void Should_Allow_Options_Configuration()
{
// This test would be run in a separate test module with different options
// For now, just verify the options exist
_options.Value.ShouldNotBeNull();
}
}
Loading…
Cancel
Save