TickerQ : Scheduler de tâches haute performance pour .NET

TickerQ fournit une planification de jobs basée sur le temps ou des expressions cron, un cœur stateless optimisé via source generators, une persistance EF Core optionnelle et un dashboard temps réel pour suivre l'exécution.

Présentation

Librairie open‑source orientée performance et observabilité : pas de réflexion à l'exécution, coordination multi‑nœuds, politiques de retry et throttling intégrées, dashboard embarqué.

Installation

# Core
dotnet add package TickerQ

# EF Core (optionnel)
dotnet add package TickerQ.EntityFrameworkCore

# UI Dashboard (optionnel)
dotnet add package TickerQ.Dashboard

Tous les packages doivent être alignés sur la même version (depuis >= v2.2.0).

Fonctionnalités clés

  • Planification Cron et temporelle (one‑shot)
  • Cœur stateless + source generator (pas de réflexion runtime)
  • Persistance EF Core optionnelle
  • Dashboard temps réel (suivi, inspection, statut)
  • Retries configurables + intervalles progressifs
  • Throttling via contrôle de concurrence global
  • Injection de dépendances native
  • Coordination distribuée multi‑nœuds
Configuration de base

Enregistrement des services et activation du pipeline d'exécution. Le dashboard peut être monté sous un chemin dédié avec authentification basique.

builder.Services.AddTickerQ(options =>
{
    options.SetMaxConcurrency(10);
    options.AddOperationalStore<MyDbContext>(efOpt =>
    {
        efOpt.SetExceptionHandler<MyExceptionHandlerClass>();
        efOpt.UseModelCustomizerForMigrations();
    });
    options.AddDashboard(ui =>
    {
        ui.BasePath = "/tickerq-dashboard";
        ui.AddDashboardBasicAuth();
    });
});

app.UseTickerQ(); // activate processors
Définition & planification de jobs

Un job récurrent se définit via l'attribut [TickerFunction]; les occurrences sont planifiées soit par temps absolu (TimeTicker) soit via une expression cron (CronTicker).

public class CleanupJobs(ICleanUpService cleanUpService)
{
    private readonly ICleanUpService _cleanUpService = cleanUpService;

    [TickerFunction(functionName: "CleanupLogs", cronExpression: "0 0 * * *" )]
    public async Task CleanupLogs(TickerFunctionContext<string> ctx, CancellationToken ct)
    {
        var file = ctx.Request; // e.g. cleanup_example_file.txt
        await _cleanUpService.CleanOldLogsAsync(file, ct);
    }
}
// One-shot time based job
await _timeTickerManager.AddAsync(new TimeTicker
{
    Function = "CleanupLogs",
    ExecutionTime = DateTime.UtcNow.AddMinutes(1),
    Request = TickerHelper.CreateTickerRequest<string>("cleanup_example_file.txt"),
    Retries = 3,
    RetryIntervals = new[] { 30, 60, 120 },
});

// Recurring cron job
await _cronTickerManager.AddAsync(new CronTicker
{
    Function = "CleanupLogs",
    Expression = "0 */6 * * *", // every 6 hours
    Request = TickerHelper.CreateTickerRequest<string>("cleanup_example_file.txt"),
    Retries = 2,
    RetryIntervals = new[] { 60, 300 }
});
Persistance EF Core

Utilisez UseModelCustomizerForMigrations pour intégrer proprement la configuration. Sans cela appliquez manuellement les configurations dans OnModelCreating.

public class MyDbContext : DbContext
{
    public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        // Explicit configuration if not using UseModelCustomizerForMigrations
        builder.ApplyConfiguration(new TimeTickerConfigurations());
        builder.ApplyConfiguration(new CronTickerConfigurations());
        builder.ApplyConfiguration(new CronTickerOccurrenceConfigurations());
        // or: builder.ApplyConfigurationsFromAssembly(typeof(TimeTickerConfigurations).Assembly);
    }
}
Retries & Throttling

Les propriétés Retries + RetryIntervals gèrent les relances. Le throttling global s'obtient via SetMaxConcurrency ; combinez avec une granularité fonction par design.

// Example of retry intervals & throttling like behaviour via configuration
await _timeTickerManager.AddAsync(new TimeTicker
{
    Function = "ImportData",
    ExecutionTime = DateTime.UtcNow.AddSeconds(30),
    Request = TickerHelper.CreateTickerRequest<string>("import_batch_42"),
    Retries = 5,
    RetryIntervals = new[] { 10, 30, 60, 120, 300 },
    // (Pseudo) throttle approach: keep Function concurrency low via SetMaxConcurrency globally
});
Dashboard temps réel

Surveille exécutions, états, erreurs et facilite le diagnostic en production. Authentifiez l'accès et limitez le chemin d'exposition.

// Add dashboard with basic auth (see real docs for advanced auth)
builder.Services.AddTickerQ(o =>
{
    o.AddDashboard(ui =>
    {
        ui.BasePath = "/tickerq-dashboard";
        ui.AddDashboardBasicAuth();
    });
});

Bonnes pratiques

  • Isoler le domaine: Gardez les handlers de jobs fins, déléguez la logique métier à des services testables.
  • Nommer les functions clairement: Facilite la corrélation entre planification et implémentation.
  • Limiter la concurrence globale: Évite la saturation CPU/IO sur charges importantes.
  • Renseigner les RetryIntervals: Contrôle la pression sur les dépendances externes.
  • Centraliser la configuration EF: Réduit le risque de divergence lors des migrations.
  • Sécuriser le dashboard: Protège les métadonnées d'exécution et l'observabilité.

Résumé

TickerQ est adapté lorsque vous avez besoin d'un scheduler .NET extensible, observable et sans overhead de réflexion, avec prise en charge native des scénarios distribués et une intégration EF Core rapide.

Sources : GitHub • Documentation • NuGet
Écrit le 2025-09-06