ConfigureLocalWorkers
Package Trax.SchedulerNamespace Trax.Scheduler.ConfigurationSource SchedulerConfigurationBuilder.Settings.cs, LocalWorkerOptions.cs
Configures the built-in local worker pool, which runs queued jobs in the scheduler's own process. The pool is registered whenever the scheduler has a database provider, UsePostgres() or UseSqlite(), whether or not this is called.
Signature
public SchedulerConfigurationBuilder ConfigureLocalWorkers(
Action<LocalWorkerOptions> configure
)Parameters
| Parameter | Type | Description |
|---|---|---|
configure | Action<LocalWorkerOptions> | Sets the worker count, polling interval and timeouts |
LocalWorkerOptions
| Property | Type | Default | Description |
|---|---|---|---|
WorkerCount | int | Environment.ProcessorCount, or 1 on SQLite | Number of concurrent worker tasks polling for jobs. 1 to 256. A value you set is used on either provider |
PollingInterval | TimeSpan | 1 second | How often idle workers poll for new jobs. Greater than zero, up to 30 days |
VisibilityTimeout | TimeSpan | 30 minutes | How long a claimed job stays invisible after its worker last refreshed its claim before another worker can reclaim it (crash recovery). A running job's claim is refreshed every third of this, so a long job is not reclaimed while it runs |
BatchSize | int | 1 | Number of jobs each worker claims per poll cycle. At least 1. Every job in the batch keeps its claim until the worker runs it, however long the jobs ahead of it take |
ShutdownTimeout | TimeSpan | 30 seconds | Grace period for in-flight jobs during shutdown. 0 to 30 days |
Returns
SchedulerConfigurationBuilder, for chaining.
Behavior
- Local workers use the
IDataContextthe database provider registers. - Jobs are queued to the
trax.background_jobtable. On Postgres they are dequeued withFOR UPDATE SKIP LOCKED, so workers in any number of processes claim without blocking each other. - On SQLite each claim runs under
BEGIN IMMEDIATE, which takes the file's write lock. Workers queue on that lock rather than running side by side, so the pool starts one worker unless you setWorkerCount, and more do not help. - Workers delete job rows after execution (both success and failure), including a job that finishes during shutdown: the delete does not take the host's stopping token. Trax.Core's Metadata and DeadLetter tables handle the audit trail.
AddSchedulerrefuses options outside their ranges when it is built (see Value Ranges);VisibilityTimeoutmust be between one second and ten years.- While a worker holds jobs, it refreshes the
fetched_atof every job it has claimed and not yet finished, the running one and any waiting behind it in a batch, every third ofVisibilityTimeout. If a worker crashes mid-execution, the refreshes stop, the timestamp becomes stale, and the job is reclaimed afterVisibilityTimeout. - A job waiting in
trax.background_jobfor a free worker is not failed by the stale-pending reaper, however long it waits. - A job that fails before its run starts (its input cannot be read, or the run's start cannot be written) is deleted like any other, and the worker fails the run itself, still
Pending, with the reason "The job failed before its run started". The failure goes through the lifecycle hooks at once, so a machine that invoked the run reaches itsOnFailedtarget without waiting for the stale-pending reaper. The worker's log has the error. A run that started records its own outcome. - With
UseRemoteWorkers(),UseSqsWorkers()orUseLambdaWorkers()also configured, local workers still run every train those calls do not route.
Registered services
With a database provider, the scheduler registers:
| Service | Lifetime | Description |
|---|---|---|
LocalWorkerOptions | Singleton | Configuration options |
IJobRunnerTrain → JobRunnerTrain | Scoped | The train that workers use to execute each job |
IJobSubmitter → PostgresJobSubmitter | Scoped | Enqueue implementation (INSERT into background_job) |
LocalWorkerService | Hosted Service | Background worker that polls and executes jobs |
Example
services.AddTrax(trax => trax
.AddEffects(effects => effects.UsePostgres(connectionString))
.AddMediator(assembly)
.AddScheduler(scheduler => scheduler
.ConfigureLocalWorkers(options =>
{
options.WorkerCount = 4;
options.PollingInterval = TimeSpan.FromSeconds(2);
options.VisibilityTimeout = TimeSpan.FromMinutes(15);
options.ShutdownTimeout = TimeSpan.FromMinutes(1);
})
.UseRemoteWorkers(
remote => remote.BaseUrl = "https://gpu-workers/trax/execute",
routing => routing.ForTrain<IReviewContentTrain>())
.Schedule<ISyncTrain>("sync", new SyncInput(), Every.Minutes(5))));IReviewContentTrain is sent to the remote endpoint; ISyncTrain runs on the four local workers.
See also
- Job Submission Architecture: detailed architecture, crash recovery, comparison with Hangfire
- UseRemoteWorkers: per-train HTTP remote dispatch
- UseSqsWorkers: per-train SQS dispatch