Configuration Reference
This is a complete reference for all EverTask configuration options.
Table of Contents
- Service Configuration
- Queue Configuration
- Storage Configuration
- Logging Configuration
- Monitoring Configuration
- Storage Provider Details
- Handler Configuration
- Complete Examples
Service Configuration
Use the fluent API in AddEverTask() to configure EverTask’s core behavior.
SetChannelOptions
Controls how many tasks can be queued and what happens when the queue fills up.
Signatures:
SetChannelOptions(int capacity)
SetChannelOptions(Action<BoundedChannelOptions> configure)
Parameters:
capacity(int): Maximum number of tasks that can be queuedconfigure(Action): Custom configuration forBoundedChannelOptions
Default: Environment.ProcessorCount * 200 (minimum 1000)
Examples:
// Simple capacity
opt.SetChannelOptions(5000)
// Custom configuration
opt.SetChannelOptions(options =>
{
options.Capacity = 5000;
options.FullMode = BoundedChannelFullMode.Wait; // or DropWrite, DropOldest
})
FullMode Options:
Wait: Block until space is available (default)DropWrite: Drop the new item if fullDropOldest: Drop the oldest item and add the new one
SetMaxDegreeOfParallelism
Controls how many tasks can run at the same time.
Signature:
SetMaxDegreeOfParallelism(int maxDegreeOfParallelism)
Parameters:
maxDegreeOfParallelism(int): Number of concurrent workers
Default: Environment.ProcessorCount * 2 (minimum 4)
Examples:
// Fixed parallelism
opt.SetMaxDegreeOfParallelism(16)
// Scale with CPUs
opt.SetMaxDegreeOfParallelism(Environment.ProcessorCount * 4)
Notes:
- Use higher values for I/O-bound tasks like API calls or database operations
- Use lower values for CPU-intensive tasks
- Setting to 1 will log a warning since it’s generally a bad idea in production
SetDefaultRetryPolicy
Sets how tasks should retry when they fail (applies to all tasks unless overridden).
Signature:
SetDefaultRetryPolicy(IRetryPolicy retryPolicy)
Parameters:
retryPolicy(IRetryPolicy): Retry policy implementation
Default: LinearRetryPolicy(3, TimeSpan.FromMilliseconds(500))
Examples:
// Linear retry with fixed delay
opt.SetDefaultRetryPolicy(new LinearRetryPolicy(5, TimeSpan.FromSeconds(1)))
// Linear retry with custom delays
opt.SetDefaultRetryPolicy(new LinearRetryPolicy(new[]
{
TimeSpan.FromMilliseconds(100),
TimeSpan.FromMilliseconds(500),
TimeSpan.FromSeconds(2)
}))
// Custom retry policy
opt.SetDefaultRetryPolicy(new ExponentialBackoffPolicy())
// No retries
opt.SetDefaultRetryPolicy(new LinearRetryPolicy(1, TimeSpan.Zero))
SetDefaultTimeout
Sets a maximum execution time for tasks (applies globally unless overridden).
Signature:
SetDefaultTimeout(TimeSpan? timeout)
Parameters:
timeout(TimeSpan?): Maximum execution time, ornullfor no timeout
Default: null (no timeout)
Examples:
// 5 minute timeout
opt.SetDefaultTimeout(TimeSpan.FromMinutes(5))
// 30 second timeout
opt.SetDefaultTimeout(TimeSpan.FromSeconds(30))
// No timeout (explicit)
opt.SetDefaultTimeout(null)
Notes:
- When the timeout is reached, the
CancellationTokengets cancelled - Your handler needs to check the token for this to work (cooperative cancellation)
- You can override this per handler or per queue
SetThrowIfUnableToPersist
Controls what happens when a task can’t be saved to storage.
Signature:
SetThrowIfUnableToPersist(bool throwIfUnableToPersist)
Parameters:
throwIfUnableToPersist(bool): Whether to throw on persistence failure
Default: true
Examples:
// Throw on persistence failure (recommended)
opt.SetThrowIfUnableToPersist(true)
// Don't throw (tasks may be lost)
opt.SetThrowIfUnableToPersist(false)
Notes:
- When
true, the dispatch fails immediately if the task can’t be saved - When
false, the task might run but won’t be saved (risky!) - Keep this
trueunless you have a good reason not to
UseShardedScheduler
Enables a sharded scheduler that can handle extremely high loads by distributing work across multiple internal schedulers.
Signature:
UseShardedScheduler()
UseShardedScheduler(int shardCount)
Parameters:
shardCount(int): Number of shards (default: auto-scale based on CPU count, minimum 4)
Default: Not enabled (uses PeriodicTimerScheduler)
Examples:
// Auto-scale based on CPUs
opt.UseShardedScheduler()
// Fixed shard count
opt.UseShardedScheduler(8)
// Scale with CPUs
opt.UseShardedScheduler(Environment.ProcessorCount)
When to Use: You probably need this if you’re seeing:
- Sustained load above 10,000
Schedule()calls/second - Burst spikes above 20,000
Schedule()calls/second - More than 100,000 tasks scheduled at once
- High lock contention showing up in your profiler
RegisterTasksFromAssembly
Scans an assembly and registers all task handlers it finds.
Signature:
RegisterTasksFromAssembly(Assembly assembly)
Parameters:
assembly(Assembly): Assembly containing task handlers
Examples:
// Current assembly
opt.RegisterTasksFromAssembly(typeof(Program).Assembly)
// Specific assembly
opt.RegisterTasksFromAssembly(typeof(MyTask).Assembly)
// Assembly by name
opt.RegisterTasksFromAssembly(Assembly.Load("MyTasksAssembly"))
RegisterTasksFromAssemblies
Scans multiple assemblies and registers all task handlers from them.
Signature:
RegisterTasksFromAssemblies(params Assembly[] assemblies)
Parameters:
assemblies(Assembly[]): Assemblies containing task handlers
Examples:
opt.RegisterTasksFromAssemblies(
typeof(CoreTask).Assembly,
typeof(ApiTask).Assembly,
typeof(BackgroundTask).Assembly)
Queue Configuration
You can set up multiple queues to isolate different types of work and give them different priorities or resource allocations.
ConfigureDefaultQueue
Customizes the default queue (used when you don’t specify a queue name for a task).
Signature:
ConfigureDefaultQueue(Action<QueueConfiguration> configure)
Example:
.ConfigureDefaultQueue(q => q
.SetMaxDegreeOfParallelism(10)
.SetChannelCapacity(1000)
.SetFullBehavior(QueueFullBehavior.Wait)
.SetDefaultTimeout(TimeSpan.FromMinutes(5))
.SetDefaultRetryPolicy(new LinearRetryPolicy(3, TimeSpan.FromSeconds(1))))
AddQueue
Creates a new named queue with its own configuration.
Signature:
AddQueue(string queueName, Action<QueueConfiguration> configure)
Parameters:
queueName(string): Unique queue nameconfigure(Action): Queue configuration
Example:
.AddQueue("high-priority", q => q
.SetMaxDegreeOfParallelism(20)
.SetChannelCapacity(500)
.SetFullBehavior(QueueFullBehavior.Wait))
.AddQueue("background", q => q
.SetMaxDegreeOfParallelism(2)
.SetChannelCapacity(100)
.SetFullBehavior(QueueFullBehavior.FallbackToDefault))
ConfigureRecurringQueue
Customizes the recurring queue (EverTask automatically creates this queue for recurring tasks).
Signature:
ConfigureRecurringQueue(Action<QueueConfiguration> configure)
Example:
.ConfigureRecurringQueue(q => q
.SetMaxDegreeOfParallelism(5)
.SetChannelCapacity(200)
.SetDefaultTimeout(TimeSpan.FromMinutes(10)))
QueueConfiguration Methods
Each queue supports these configuration methods:
// Parallelism
SetMaxDegreeOfParallelism(int maxDegreeOfParallelism)
// Capacity
SetChannelCapacity(int capacity)
// Full behavior
SetFullBehavior(QueueFullBehavior behavior)
// Timeout
SetDefaultTimeout(TimeSpan? timeout)
// Retry policy
SetDefaultRetryPolicy(IRetryPolicy retryPolicy)
Storage Configuration
Choose where EverTask saves task data.
AddMemoryStorage
Uses in-memory storage (fine for development/testing, but tasks won’t survive a restart).
Signature:
AddMemoryStorage()
Example:
builder.Services.AddEverTask(opt => opt.RegisterTasksFromAssembly(typeof(Program).Assembly))
.AddMemoryStorage();
Characteristics:
- No external dependencies
- Fast performance
- Tasks lost on restart
AddSqlServerStorage
Uses SQL Server for persistent storage.
Signature:
AddSqlServerStorage(string connectionString)
AddSqlServerStorage(string connectionString, Action<StorageOptions> configure)
Parameters:
connectionString(string): SQL Server connection stringconfigure(Action): Storage configuration options
Examples:
// Basic
.AddSqlServerStorage("Server=localhost;Database=EverTaskDb;Trusted_Connection=True;")
// With options
.AddSqlServerStorage(
connectionString,
opt =>
{
opt.SchemaName = "EverTask";
opt.AutoApplyMigrations = true;
})
StorageOptions Properties:
SchemaName(string?): Database schema name (default: “EverTask”, null = main schema)AutoApplyMigrations(bool): Auto-apply EF Core migrations (default: true)
AddSqliteStorage
Uses SQLite for persistent storage.
Signature:
AddSqliteStorage(string connectionString)
AddSqliteStorage(string connectionString, Action<StorageOptions> configure)
Parameters:
connectionString(string): SQLite connection stringconfigure(Action): Storage configuration options
Examples:
// Basic
.AddSqliteStorage("Data Source=evertask.db")
// With options
.AddSqliteStorage(
"Data Source=evertask.db;Cache=Shared;",
opt =>
{
opt.AutoApplyMigrations = true;
})
Notes:
SchemaNameis not supported in SQLite (always null)
Logging Configuration
AddSerilog
Integrates Serilog for structured logging throughout EverTask.
Package: EverTask.Logging.Serilog
Signature:
AddSerilog(Action<LoggerConfiguration> configure)
Parameters:
configure(Action): Serilog logger configuration
Example:
.AddSerilog(opt =>
opt.ReadFrom.Configuration(
configuration,
new ConfigurationReaderOptions { SectionName = "EverTaskSerilog" }))
appsettings.json Example:
{
"EverTaskSerilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
},
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "Logs/evertask-.txt",
"rollingInterval": "Day",
"retainedFileCountLimit": 10
}
}
],
"Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"],
"Properties": {
"Application": "MyApp"
}
}
}
WithPersistentLogger
Available since: v3.0
Configures persistent handler logging options. When enabled, logs written via Logger property in handlers are stored in the database for audit trails.
Important: Logs are ALWAYS forwarded to ILogger infrastructure (console, file, Serilog, etc.) regardless of this setting. This option only controls database persistence.
Signature:
WithPersistentLogger(Action<PersistentLoggerOptions> configure)
Parameters:
configure(Action): Configuration action for persistent logger options
Default: Disabled
Example:
.AddEverTask(opt => opt
.WithPersistentLogger(log => log
.SetMinimumLevel(LogLevel.Information)
.SetMaxLogsPerTask(1000)))
Note: Calling .WithPersistentLogger() automatically enables database persistence. You don’t need to call .Enable().
PersistentLoggerOptions Methods:
Disable()
Disables persistent logging to the database (logs still go to ILogger). Use this if you want to temporarily disable persistence.
.WithPersistentLogger(log => log.Disable())
SetMinimumLevel(LogLevel level)
Sets the minimum log level for database persistence. Logs below this level are not stored in the database but are still forwarded to ILogger.
Parameters:
level(LogLevel): Minimum level to persist (Trace,Debug,Information,Warning,Error,Critical)
Default: LogLevel.Information
Example:
.WithPersistentLogger(log => log
.SetMinimumLevel(LogLevel.Warning)) // Only persist Warning and above
Note: This only affects database persistence. ILogger receives all log levels regardless of this setting.
SetMaxLogsPerTask(int? maxLogs)
Sets the maximum number of logs to persist per task execution. Once this limit is reached, additional logs are not persisted (but still forwarded to ILogger).
Parameters:
maxLogs(int?): Maximum logs to persist.null= unlimited (not recommended for production)
Default: 1000
Example:
.WithPersistentLogger(log => log
.SetMaxLogsPerTask(500)) // Limit to 500 logs
Performance: ~100 bytes per log in memory during execution. Single bulk INSERT to database after task completion.
Complete Example:
.AddEverTask(opt => opt
.RegisterTasksFromAssembly(typeof(Program).Assembly)
.WithPersistentLogger(log => log
.SetMinimumLevel(LogLevel.Information)
.SetMaxLogsPerTask(1000)))
Monitoring Configuration
AddSignalRMonitoring
Enables real-time task monitoring via SignalR.
Package: EverTask.Monitor.AspnetCore.SignalR
Signature:
AddSignalRMonitoring()
AddSignalRMonitoring(Action<SignalRMonitorOptions> configure)
Parameters:
configure(Action): Monitoring configuration options
Examples:
// Basic
.AddSignalRMonitoring()
// With options
.AddSignalRMonitoring(opt =>
{
opt.HubRoute = "/evertask-hub";
opt.EnableDetailedErrors = true;
})
SignalRMonitorOptions Properties:
HubRoute(string): SignalR hub endpoint (default: “/evertask-hub”)EnableDetailedErrors(bool): Include detailed error messages (default: false)
Client-Side Setup:
<!-- Add SignalR client library -->
<script src="https://cdn.jsdelivr.net/npm/@microsoft/signalr@latest/dist/browser/signalr.min.js"></script>
<script>
const connection = new signalR.HubConnectionBuilder()
.withUrl("/evertask-hub")
.build();
connection.on("TaskEvent", (event) => {
console.log("Task event:", event);
// event.TaskId, event.EventType, event.Timestamp, etc.
});
connection.start().catch(err => console.error(err));
</script>
Event Types:
TaskDispatched: Task was dispatchedTaskStarted: Task execution startedTaskCompleted: Task completed successfullyTaskFailed: Task failed after all retriesTaskCancelled: Task was cancelled
Storage Provider Details
SQL Server Storage Options
Package: EverTask.Storage.SqlServer
Advanced Configuration:
.AddSqlServerStorage(connectionString, opt =>
{
// Schema name (default: "EverTask", null = main schema)
opt.SchemaName = "EverTask";
// Auto-apply migrations (default: true)
opt.AutoApplyMigrations = true;
// Connection pooling (enabled by default in v2.0+)
// Uses DbContextFactory for 30-50% performance improvement
// Stored procedures (enabled by default in v2.0+)
// Reduces roundtrips for status updates
})
Manual Migrations:
For production environments, apply migrations manually:
# Generate migration script
dotnet ef migrations script --context TaskStoreDbContext --output migration.sql
# Apply via your deployment pipeline
sqlcmd -S localhost -d EverTaskDb -i migration.sql
Stored Procedures:
EverTask v2.0+ uses stored procedures for critical operations:
[EverTask].[SetTaskStatus]: Atomic status update + audit insert- Performance: 50% fewer roundtrips for status changes
Connection String Options:
// Basic
"Server=localhost;Database=EverTaskDb;Trusted_Connection=True;"
// With pooling (recommended)
"Server=localhost;Database=EverTaskDb;Trusted_Connection=True;Min Pool Size=5;Max Pool Size=100;"
// Azure SQL
"Server=tcp:yourserver.database.windows.net,1433;Database=EverTaskDb;User ID=user;Password=pass;Encrypt=True;"
Schema Customization:
-- Custom schema
CREATE SCHEMA [CustomSchema]
GO
-- Configure in code
opt.SchemaName = "CustomSchema";
SQLite Storage Options
Package: EverTask.Storage.Sqlite
Advanced Configuration:
.AddSqliteStorage(connectionString, opt =>
{
// Auto-apply migrations (default: true)
opt.AutoApplyMigrations = true;
// Note: SchemaName is not supported in SQLite (always null)
})
Connection String Options:
// Basic
"Data Source=evertask.db"
// In-memory (for testing)
"Data Source=:memory:"
// Shared cache
"Data Source=evertask.db;Cache=Shared;"
// Full options
"Data Source=evertask.db;Mode=ReadWriteCreate;Cache=Shared;Foreign Keys=True;"
Performance Tuning:
-- WAL mode for better concurrency
PRAGMA journal_mode=WAL;
-- Optimize for performance
PRAGMA synchronous=NORMAL;
PRAGMA cache_size=10000;
PRAGMA temp_store=MEMORY;
Limitations:
- No schema support (unlike SQL Server)
- Not recommended for high-concurrency scenarios (>100 tasks/sec)
- Best for: Single-server deployments, development, small workloads
Handler Configuration
You can configure behavior at the handler level to override global defaults.
Handler Properties
Set these in your handler’s constructor:
public class MyHandler : EverTaskHandler<MyTask>
{
public MyHandler()
{
// Timeout
Timeout = TimeSpan.FromMinutes(10);
// Retry policy
RetryPolicy = new LinearRetryPolicy(5, TimeSpan.FromSeconds(2));
}
// Queue routing
public override string? QueueName => "high-priority";
public override async Task Handle(MyTask task, CancellationToken cancellationToken)
{
// Handler logic
}
}
Available Properties:
Timeout(TimeSpan?): Handler-specific timeoutRetryPolicy(IRetryPolicy): Handler-specific retry policyQueueName(string?): Target queue for this handler
Complete Examples
Basic Configuration
The simplest setup for getting started:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEverTask(opt =>
{
opt.RegisterTasksFromAssembly(typeof(Program).Assembly);
})
.AddMemoryStorage();
var app = builder.Build();
app.Run();
Production Configuration
A more robust setup with SQL Server storage, proper retry policies, and logging:
builder.Services.AddEverTask(opt =>
{
opt.SetChannelOptions(5000)
.SetMaxDegreeOfParallelism(Environment.ProcessorCount * 4)
.SetDefaultTimeout(TimeSpan.FromMinutes(5))
.SetDefaultRetryPolicy(new LinearRetryPolicy(3, TimeSpan.FromSeconds(1)))
.SetThrowIfUnableToPersist(true)
.RegisterTasksFromAssembly(typeof(Program).Assembly);
})
.AddSqlServerStorage(
builder.Configuration.GetConnectionString("EverTaskDb")!,
opt =>
{
opt.SchemaName = "EverTask";
opt.AutoApplyMigrations = false; // Manual migrations in production
})
.AddSerilog(opt =>
opt.ReadFrom.Configuration(
builder.Configuration,
new ConfigurationReaderOptions { SectionName = "EverTaskSerilog" }));
Multi-Queue Configuration
Isolating different workloads into separate queues:
builder.Services.AddEverTask(opt =>
{
opt.RegisterTasksFromAssembly(typeof(Program).Assembly);
})
.ConfigureDefaultQueue(q => q
.SetMaxDegreeOfParallelism(10)
.SetChannelCapacity(1000))
.AddQueue("critical", q => q
.SetMaxDegreeOfParallelism(20)
.SetChannelCapacity(500)
.SetFullBehavior(QueueFullBehavior.Wait)
.SetDefaultTimeout(TimeSpan.FromMinutes(2))
.SetDefaultRetryPolicy(new LinearRetryPolicy(5, TimeSpan.FromSeconds(1))))
.AddQueue("email", q => q
.SetMaxDegreeOfParallelism(10)
.SetChannelCapacity(10000)
.SetFullBehavior(QueueFullBehavior.FallbackToDefault))
.AddQueue("reports", q => q
.SetMaxDegreeOfParallelism(2)
.SetChannelCapacity(50)
.SetDefaultTimeout(TimeSpan.FromMinutes(30)))
.ConfigureRecurringQueue(q => q
.SetMaxDegreeOfParallelism(5)
.SetChannelCapacity(200))
.AddSqlServerStorage(connectionString);
High-Performance Configuration
Optimized for handling massive workloads:
builder.Services.AddEverTask(opt => opt
.RegisterTasksFromAssembly(typeof(Program).Assembly)
.UseShardedScheduler(shardCount: Environment.ProcessorCount)
.SetMaxDegreeOfParallelism(Environment.ProcessorCount * 4)
.SetChannelOptions(10000)
.SetDefaultTimeout(TimeSpan.FromMinutes(10))
)
.AddSqlServerStorage(connectionString, opt =>
{
opt.SchemaName = "EverTask";
opt.AutoApplyMigrations = false;
});
Multi-Assembly Configuration
When your task handlers are spread across multiple assemblies:
builder.Services.AddEverTask(opt =>
{
opt.RegisterTasksFromAssemblies(
typeof(CoreTasks.MyTask).Assembly,
typeof(ApiTasks.MyTask).Assembly,
typeof(BackgroundTasks.MyTask).Assembly)
.SetMaxDegreeOfParallelism(20);
})
.AddSqlServerStorage(connectionString);
Environment-Specific Configuration
Adjusting configuration based on your environment:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEverTask(opt =>
{
opt.RegisterTasksFromAssembly(typeof(Program).Assembly);
if (builder.Environment.IsProduction())
{
opt.SetMaxDegreeOfParallelism(Environment.ProcessorCount * 4)
.SetChannelOptions(10000)
.SetDefaultTimeout(TimeSpan.FromMinutes(10));
}
else
{
opt.SetMaxDegreeOfParallelism(2)
.SetChannelOptions(100);
}
});
if (builder.Environment.IsProduction())
{
builder.Services.AddSqlServerStorage(
builder.Configuration.GetConnectionString("EverTaskDb")!,
opt => opt.AutoApplyMigrations = false);
}
else
{
builder.Services.AddMemoryStorage();
}
Configuration Validation
EverTask checks your configuration at startup and will complain if something looks off:
Warnings:
MaxDegreeOfParallelism = 1: Usually a bad idea in production - consider scaling with CPU count- Large
ChannelCapacitywith lowMaxDegreeOfParallelism: You might end up with a huge backlog
Errors:
MaxDegreeOfParallelism < 1: Must be at least 1ChannelCapacity < 1: Must be at least 1- Duplicate queue names: Each queue needs a unique name
- No handlers registered: You need to register at least one assembly
Performance Tuning Guidelines
CPU-Bound Tasks
If your tasks do heavy computation, match your parallelism to your CPU cores:
opt.SetMaxDegreeOfParallelism(Environment.ProcessorCount) // Match CPU cores
.SetChannelOptions(100); // Small queue
I/O-Bound Tasks
If your tasks spend most of their time waiting on I/O (database, APIs, files), you can run many more in parallel:
opt.SetMaxDegreeOfParallelism(Environment.ProcessorCount * 4) // Higher parallelism
.SetChannelOptions(5000); // Larger queue
Mixed Workloads
When you have different types of tasks, use separate queues:
.ConfigureDefaultQueue(q => q
.SetMaxDegreeOfParallelism(Environment.ProcessorCount * 2))
.AddQueue("cpu-intensive", q => q
.SetMaxDegreeOfParallelism(Environment.ProcessorCount))
.AddQueue("io-intensive", q => q
.SetMaxDegreeOfParallelism(Environment.ProcessorCount * 4))
Extreme High Load
For truly massive workloads, enable the sharded scheduler:
opt.UseShardedScheduler(Environment.ProcessorCount)
.SetMaxDegreeOfParallelism(Environment.ProcessorCount * 4)
.SetChannelOptions(10000);
Next Steps
- Getting Started - Setup guide
- Advanced Features - Multi-queue and sharded scheduler
- Resilience - Retry policies and timeouts
- Storage - Storage options and configuration