Background
BackgroundJobs already persists ScheduledAt and CronExpression columns, but there is no fluent API to register recurring jobs at module-startup time, nor a worker that wakes up and enqueues due jobs. Laravel's Kernel::schedule() is the canonical example: every cron tick the scheduler enumerates definitions and dispatches due ones.
Motivation
- Most apps need recurring work: nightly cleanup, daily digest emails, hourly sync, etc.
- Schema is already in place; only the dispatcher + DSL are missing
- Eliminates per-app cron + AppHost glue
Design sketch
public interface IScheduler
{
IScheduledJob<T> Job<T>() where T : IBackgroundJob;
}
public interface IScheduledJob<T>
{
IScheduledJob<T> Cron(string expression);
IScheduledJob<T> Daily();
IScheduledJob<T> DailyAt(string time); // "03:00"
IScheduledJob<T> EveryMinutes(int minutes);
IScheduledJob<T> Hourly();
IScheduledJob<T> Weekdays();
IScheduledJob<T> Timezone(string tz);
IScheduledJob<T> WithoutOverlapping(); // mutex via DB
IScheduledJob<T> OnOneServer(); // leader election
IScheduledJob<T> WithPayload(object payload);
}
Module registers schedules in IModule.ConfigureServices:
scheduler.Job<NightlyAuditPurge>().DailyAt("02:00").Timezone("UTC");
scheduler.Job<DigestEmail>().Cron("0 8 * * MON-FRI").WithoutOverlapping();
- A hosted
SchedulerService polls every 30s, computes due jobs from registered definitions, inserts into BackgroundJobs queue
- Distributed safety:
WithoutOverlapping writes a mutex row keyed by job-name; OnOneServer uses an instance-id leader lease (DB row with TTL)
- Cron parsing via
Cronos (already on .NET; widely used)
Acceptance criteria
References
Background
BackgroundJobsalready persistsScheduledAtandCronExpressioncolumns, but there is no fluent API to register recurring jobs at module-startup time, nor a worker that wakes up and enqueues due jobs. Laravel'sKernel::schedule()is the canonical example: every cron tick the scheduler enumerates definitions and dispatches due ones.Motivation
Design sketch
Module registers schedules in
IModule.ConfigureServices:SchedulerServicepolls every 30s, computes due jobs from registered definitions, inserts intoBackgroundJobsqueueWithoutOverlappingwrites a mutex row keyed by job-name;OnOneServeruses an instance-id leader lease (DB row with TTL)Cronos(already on .NET; widely used)Acceptance criteria
ISchedulerexposed fromBackgroundJobs.ContractsSchedulerServiceruns asIHostedServicein worker processWithoutOverlapping) and leader (OnOneServer) primitives implemented and testedsm jobs list-scheduledprints next-run-atReferences