YAMCqrs.BackgroundWorker.Core
10.0.2
dotnet add package YAMCqrs.BackgroundWorker.Core --version 10.0.2
NuGet\Install-Package YAMCqrs.BackgroundWorker.Core -Version 10.0.2
<PackageReference Include="YAMCqrs.BackgroundWorker.Core" Version="10.0.2" />
<PackageVersion Include="YAMCqrs.BackgroundWorker.Core" Version="10.0.2" />
<PackageReference Include="YAMCqrs.BackgroundWorker.Core" />
paket add YAMCqrs.BackgroundWorker.Core --version 10.0.2
#r "nuget: YAMCqrs.BackgroundWorker.Core, 10.0.2"
#:package YAMCqrs.BackgroundWorker.Core@10.0.2
#addin nuget:?package=YAMCqrs.BackgroundWorker.Core&version=10.0.2
#tool nuget:?package=YAMCqrs.BackgroundWorker.Core&version=10.0.2
YAMCqrs.BackgroundWorker.Core
A .NET library for creating background workers that process batch tasks with support for parallel processing, execution auditing, and health monitoring in compliance with what is defined in ADR 7
βοΈ Installation
dotnet add package YAMCqrs.BackgroundWorker.Core
π Description
This library provides a robust infrastructure for implementing background services that process items in batches. The library offers:
- Controlled batch processing: Efficiently processes multiple items with support for configurable parallelism
- Execution auditing: Automatically logs each execution with success/failure metrics
- Health monitoring: Integrated health checks to verify worker status
- Error management: Configurable error threshold to determine execution status
- Flexible storage: Includes an in-memory implementation with support for custom implementations
π Quick Start
To register the Background Workers engine in your dependency container:
builder.Services.AddBackgroundWorker(options =>
{
options.MinutesToKeepSuccesTask = 60;
options.MinutesToKeepFailedTask = BackgroundWorkerConfiguration.DayToMinutes(7);
});
Here in the configuration we are specifying that successfully completed tasks should be deleted every 60 minutes, while tasks that ended with errors should be deleted after 7 days.
π οΈ Implementation Details
- YABackgroundWorker: Abstract class to standardize background task behavior
- IWorkerStorage: Storage for execution history. Includes an
InMemoryimplementation (not recommended for production). - CleanBackGroundWorker: Worker responsible for cleaning the storage
- HealthCheckReport: Automatic health check over worker execution results
π Dependencies
- Only official Microsoft packages.
π‘ Basic Usage
1. Create a Custom Worker
Inherit from YABackgroundWorker<TWorkItem> and implement the abstract methods:
public class MyWorker : YABackgroundWorker<MyItem>
{
public MyWorker(IServiceProvider serviceProvider, ILogger<MyWorker> logger) : base(serviceProvider, logger)
{
}
// Initial setup before the worker starts
protected override Task<bool> InitialSetupAsync(IServiceScope serviceScope, CancellationToken stoppingToken)
{
// Your initialization code here
// Return true if initialization was successful, false otherwise
// For example, use this to validate feature flags for task activation
return Task.FromResult(true);
}
// Get the batch of items to process
protected override async Task<IEnumerable<MyItem>?> GetBatchForProcessing(
IServiceScope serviceScope, CancellationToken stoppingToken)
{
// Retrieve items from your data source
return await GetItems();
}
// Process an individual item
protected override async Task<bool> ProcessItemAsync(
MyItem item, IServiceScope serviceScope, CancellationToken stoppingToken)
{
// Your processing logic here
// Return true if processed successfully, false otherwise
return true;
}
// Validation before processing the batch
protected override Task<PrevalidationResult> BatchPrevalidation(
IServiceScope serviceScope, CancellationToken stoppingToken)
{
// Check preconditions (e.g., external service availability)
// The goal is to avoid executing a batch that could fail 100% due to a third-party issue.
return Task.FromResult(PrevalidationResult.Execute());
}
// Final cleanup when the worker stops
protected override void FinalCleanUp()
{
// Safely dispose resources or perform other actions when the Task (NOT THE BATCH) shuts down.
}
// Wait interval between executions (in seconds)
protected override int SleepIntervalInSeconds() => 60;
// Degree of parallelism (number of items processed simultaneously)
protected override int ParallelismDegree() => 5;
// Error threshold percentage (0-100)
protected override int ErrorThresholdPercentage() => 10;
// If no items are found when retrieving a batch, the execution is not logged in storage.
protected override bool SkipEmptyResults() => true;
}
Although in theory the wait time between executions can be set to DAYS, depending on where and how the task is deployed, it may stop unexpectedly. It is recommended to use a shorter sleep interval and perform a deeper validation in BatchPrevalidation to decide whether execution should occur when intervals are very long.
2. Register the Worker
In your Program.cs or Startup.cs:
var builder = WebApplication.CreateBuilder(args);
// Configure the background worker core
builder.Services.AddBackgroundWorker(options =>
{
// Base BackgroundWorker library configuration
});
// Register your custom worker traditionally.
builder.Services.AddHostedService<MyWorker>();
var app = builder.Build();
app.Run();
βοΈ Configuration
BackgroundWorkerConfiguration
Configure how long executions are retained in storage:
new BackgroundWorkerConfiguration
{
// Time in minutes to keep successful tasks (default: 60)
MinutesToKeepSuccesTask = 60,
// Time in minutes to keep failed tasks (default: 60)
MinutesToKeepFailedTask = BackgroundWorkerConfiguration.DayToMinutes(7)
}
Available helper methods:
BackgroundWorkerConfiguration.HourToMinutes(int hours)- Converts hours to minutesBackgroundWorkerConfiguration.DayToMinutes(int days)- Converts days to minutes
Worker Parameters
Each custom worker must configure:
- SleepIntervalInSeconds: Wait time between executions (processing time is automatically discounted)
- ParallelismDegree: Maximum number of items processed in parallel (1 = sequential)
- ErrorThresholdPercentage: Maximum percentage of allowed errors before marking execution as failed (0-100)
- SkipEmptyResults: Determines whether empty batches should be stored in the audit log.
π₯ Health Checks
The library includes automatic health checks to monitor your workers:
app.MapHealthChecks("/health", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("background")
});
The health check reports:
- Healthy: All workers executed successfully
- Degraded: Some workers failed
- Unhealthy: No workers are healthy
β οΈ Important: What Health Checks Monitor
Health checks DO NOT monitor the current state of the worker, but rather the result of its latest processing execution. This means they evaluate the result of the following operations:
BatchPrevalidation()- Batch pre-validationGetBatchForProcessing()- Retrieval of the item batchProcessItemAsync()- Processing of each itemBatchPostProcesing()- Batch post-processing
Note about InitialSetupAsync(): If InitialSetupAsync() returns false and the worker does not start, the health check will display the result of a previous execution if one exists in storage. If no previous execution exists, the worker will not appear in the health check until it has completed at least one execution.
π Execution Storage
In-Memory Implementation (Default)
By default, InMemoryWorkerStorage is used to store executions in memory.
β οΈ WARNING: DO NOT USE IN PRODUCTION
The
InMemoryWorkerStorageimplementation is NOT designed for production environments for the following reasons:
Loss of history: Restarting the application causes all execution history stored in memory to be lost.
Incorrect execution intervals: The worker uses history to calculate when it should execute again. Without persistence:
- A worker configured to run every 1 hour will restart immediately after a service restart
- Remaining calculated time is lost (e.g., if 40 minutes remained until the next execution, it will execute immediately)
- This may cause duplicate executions or system overload
Inconsistent health checks: Without persistent history, health checks cannot report the actual state between restarts.
Recommendation: For production, implement a persistent version of
IWorkerStorageusing a database.
Recommended use: Only for local development and testing.
π WorkerExecution
Each worker execution is logged with the following information:
public class WorkerExecution
{
public Guid Id { get; } // Unique ID
public string WorkerName { get; } // Worker name
public DateTime ExecutionStartTime { get; } // Start time (UTC)
public DateTime ExecutionEndTime { get; } // End time (UTC)
public ExecutionStatus Status { get; } // Execution status
public int Success { get; } // Successfully processed items
public int Failed { get; } // Failed items
public string Message { get; } // Additional message
public bool IsSuccessful { get; } // Whether execution was successful
}
Possible statuses:
Null: Initial stateSuccess: Successfully completedFailed: Failed due to exceeding the error thresholdFailedPrevalidation: Skipped due to failed pre-validation (equivalent toPrevalidationResult.Skip(...))NoItemsToProcess: Completed but with no items to process
π― Advanced Features
Batch Pre-validation
Implement BatchPrevalidation to verify conditions before processing. The result determines whether the batch executes, is skipped with logging, or skipped silently:
protected override async Task<PrevalidationResult> BatchPrevalidation(
IServiceScope serviceScope, CancellationToken stoppingToken)
{
var service = serviceScope.ServiceProvider.GetRequiredService<MyService>();
if (!await service.IsAvailable())
{
// Logged skip in storage: useful when the skip is abnormal or relevant for auditing
return PrevalidationResult.Skip("External service unavailable");
}
if (!IsMonday())
{
// Silent skip: no record in storage.
// Ideal for expected and recurring conditions that would otherwise generate unnecessary noise
return PrevalidationResult.SkipSilently();
}
return PrevalidationResult.Execute();
}
| Result | Logged in storage | When to use |
|---|---|---|
PrevalidationResult.Execute() |
β Yes | The batch should process normally |
PrevalidationResult.Skip(message) |
β Yes | The skip is abnormal or relevant for auditing (e.g., external service down) |
PrevalidationResult.SkipSilently() |
β No | The skip is expected and recurring (e.g., "only on Mondays") |
Parallel Processing
Configure the degree of parallelism according to your needs:
protected override int ParallelismDegree()
{
// 1 = Sequential
// >1 = Parallel processing
return Environment.ProcessorCount; // Uses all available cores
}
Automatic Cleanup
The CleanBackGroundWorker runs every 1 hour to clean old executions according to your configuration.
Code Analyzers
| Rule ID | Category | Severity | Notes |
|---|---|---|---|
| YAMCBGWO001 | Usage | Warning | use YABackgroundWorker instead of BackgroundService |
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.DependencyInjection (>= 10.0.2)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 10.0.2)
- Microsoft.Extensions.Hosting (>= 10.0.1)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on YAMCqrs.BackgroundWorker.Core:
| Package | Downloads |
|---|---|
|
YAMCqrs.BackgroundWorker.Storage.MongoDb
Package Description |
|
|
YAMCqrs.EventBus.Core
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.