Robust. Adaptive. Precise.
TickerQ is a high-performance, modular background job scheduler for .NET applications. It supports cron-based and one-time jobs, integrates with Entity Framework Core for persistence, and offers a live dashboard UI powered by SignalR.
- Time and Cron Scheduling
- Stateless Core with source generator
- EF Core Persistence (optional)
- Live Dashboard UI
- Retry Policies & Throttling
- Dependency Injection support
- Multi-node distributed coordination
dotnet add package TickerQdotnet add package TickerQ.EntityFrameworkCoredotnet add package TickerQ.Dashboardbuilder.Services.AddTickerQ(options =>
{
options.SetMaxConcurrency(4); // Optional
options.SetExceptionHandler<MyExceptionHandler>(); // Optional
options.AddOperationalStore<MyDbContext>(); // Enables EF-backed storage
options.CancelMissedTickersOnApplicationRestart(); // Useful in distributed mode
options.AddDashboard(basePath: "/tickerq-dashboard"); // Dashboard path
options.AddDashboardBasicAuth(); // Enables simple auth
});
app.UseTickerQ(); // Activates job processorpublic 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();
}
}Enabled by adding:
options.AddDashboard(basePath: "/tickerq-dashboard");
options.AddDashboardBasicAuth(); // OptionalAccessible 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"
}| Feature | TickerQ | Hangfire | Quartz.NET |
|---|---|---|---|
| Cron scheduling | ✅ Yes | ✅ Yes | ✅ Yes |
| Time-based one-time jobs | ✅ Yes (TimeTicker) | ✅ Yes | |
| Persistent job store | ✅ With EF Core | ✅ Yes | ✅ Yes |
| In-memory mode | ✅ Built-in | ✅ Built-in | ✅ Built-in |
| Retry/cooldown logic | ✅ Advanced & configurable | ||
| Dashboard UI | ✅ First-party + real-time | ✅ Basic | |
| DI support | ✅ Native and seamless | 🟠 Partial – type-based only | |
| Reflection-free job discovery | ✅ Roslyn-based, compile-time | ❌ Uses reflection | ❌ Uses reflection |
| Multi-node/distributed support | ✅ Native with EF Core | ✅ Yes | |
| Custom tickers (plugin model) | ✅ Fully extensible | ❌ Not extensible | |
| Parallelism & concurrency control | ✅ Built-in scheduler threadpool | ✅ Queues/ServerCount | ✅ ThreadPools |
| Performance under high load | ✅ Optimized, no overhead | ||
| Async/await support | ✅ Yes | ✅ Yes | |
| CancellationToken support | ✅ Propagated & honored | ❌ Not natively supported | 🟠 Optional – must check manually |
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
FunctionNameconsistently across schedule and handler - Use
CancellationTokenfor graceful cancellation - Use
Requestto pass dynamic data to jobs - Use
.AddDashboard()only when EF Core is configured
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.