Robust. Adaptive. Precise.
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.
📚 Full Docs: https://tickerq.arcenox.com
Note: As of v2.2.0, all TickerQ packages are versioned together — even if a package has no changes — to keep the ecosystem in sync. Always update all packages to the same version.
- Time and Cron Scheduling
- Stateless Core with source generator
- EF Core Persistence
- Live Dashboard UI
- Retry Policies & Throttling
- Dependency Injection support
- Multi-node distributed coordination
dotnet add package TickerQ
dotnet add package TickerQ.EntityFrameworkCore
dotnet add package TickerQ.Dashboard
builder.Services.AddTickerQ(options =>
{
options.SetMaxConcurrency(4); // Optional
options.<
D69C
/span>SetExceptionHandler<MyExceptionHandler>(); // Optional
options.AddOperationalStore<MyDbContext>(efOpt =>
{
efOpt.UseModelCustomizerForMigrations(); // Applies custom model customization only during EF Core migrations
efOpt.CancelMissedTickersOnApplicationRestart(); // Useful in distributed mode
}); // Enables EF-backed storage
options.AddDashboard(basePath: "/tickerq-dashboard"); // Dashboard path
options.AddDashboardBasicAuth(); // Enables simple auth
});
app.UseTickerQ(); // Activates job processor
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Apply TickerQ entity configurations explicitly
builder.ApplyConfiguration(new TimeTickerConfigurations());
builder.ApplyConfiguration(new CronTickerConfigurations());
builder.ApplyConfiguration(new CronTickerOccurrenceConfigurations());
// Alternatively, apply all configurations from assembly:
// builder.ApplyConfigurationsFromAssembly(typeof(TimeTickerConfigurations).Assembly);
}
}
💡 Recommendation:
UseUseModelCustomizerForMigrations()
to cleanly separate infrastructure concerns from your core domain model, especially during design-time operations like migrations.
Note: If you're using third-party libraries (e.g., OpenIddict) that also overrideIModelCustomizer
, you must either merge customizations or fall back to manual configuration insideOnModelCreating()
to avoid conflicts.
public class CleanupJobs
{
[TickerFunction(functionName: "CleanupLogs", cronExpression: "0 0 * * *" )]
public void CleanupLogs()
{
// Runs every midnight
}
}
This uses a cron expression to run daily at midnight.
public class NotificationJobs
{
[TickerFunction(functionName: "SendWelcome")]
public Task SendWelcome(TickerFunctionContext<string> tickerContext ,CancellationToken ct)
{
Console.WriteLine(tickerContext.Request); // Output: User123
return Task.CompletedTask;
}
}
Then schedule it:
await _timeTickerManager.AddAsync(new TimeTicker
{
Function = "SendWelcome",
ExecutionTime = DateTime.UtcNow.AddMinutes(1),
Request = TickerHelper.CreateTickerRequest<string>("User123"),
Retries = 3,
RetryIntervals = new[] { 30, 60, 120 } // Retry after 30s, 60s, then 2min
});
public class ReportJobs
{
private readonly IReportService _reportService;
public ReportJobs(IReportService reportService)
{
_reportService = reportService;
}
[TickerFunction(functionName: "GenerateDailyReport", cronExpression: "0 6 * * *")]
public async Task GenerateDailyReport()
{
await _reportService.GenerateAsync();
}
}
Check out Dashboard Overview: TickerQ-Dashboard-Examples
Enabled by adding:
options.AddDashboard(basePath: "/tickerq-dashboard");
options.AddDashboardBasicAuth(); // Optional
Accessible at /tickerq-dashboard
, it shows:
- System status
- Active tickers
- Job queue state
- Cron ticker stats
- Execution history
- Trigger/cancel/edit jobs live
Auth config (optional):
"TickerQBasicAuth": {
"Username": "admin",
"Password": "admin"
}
TickerQ supports:
- Retries per job
- Retry intervals (
RetryIntervals
) - Distributed locking (EF mode only)
- Job ownership tracking across instances
- Cooldown on job failure
await _cronTickerManager.AddAsync(new CronTicker
{
Function = "CleanupLogs",
CronExpression = "0 */6 * * *", // Every 6 hours
Retries = 2,
RetryIntervals = new[] { 60, 300 }
});
- Use
[TickerFunction]
to register jobs - Use
FunctionName
consistently across schedule and handler - Use
CancellationToken
for graceful cancellation - Use
Request
to pass dynamic data to jobs
PRs, ideas, and issues are welcome!
- Fork & branch
- Code your change
- Submit a Pull Request
MIT OR Apache 2.0 © Arcenox
You may choose either license to use this software.