ConfigureLocalWorkers

Customizes the built-in PostgreSQL local worker pool. Local workers are registered automatically when UsePostgres() is configured. No explicit call is needed to enable them.

Signature

public SchedulerConfigurationBuilder ConfigureLocalWorkers(
    Action<LocalWorkerOptions> configure
)

Parameters

ParameterTypeRequiredDescription
configureAction<LocalWorkerOptions>YesCallback to customize worker count, polling interval, and timeouts

Returns

SchedulerConfigurationBuilder, for continued fluent chaining.

LocalWorkerOptions

PropertyTypeDefaultDescription
WorkerCountintEnvironment.ProcessorCountNumber of concurrent worker tasks polling for jobs. 1 to 256
PollingIntervalTimeSpan1 secondHow often idle workers poll for new jobs. Greater than zero, up to 30 days
VisibilityTimeoutTimeSpan30 minutesHow 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
BatchSizeint1Number 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
ShutdownTimeoutTimeSpan30 secondsGrace period for in-flight jobs during shutdown. 0 to 30 days

Examples

Default Behavior (No Call Needed)

When UsePostgres() is configured, local workers are enabled automatically with default settings. You don't need to call anything to get local execution:

services.AddTrax(trax => trax
    .AddEffects(effects => effects
        .UsePostgres(connectionString)
    )
    .AddMediator(typeof(Program).Assembly)
    .AddScheduler(scheduler => scheduler
        .Schedule<IMyTrain>("my-job", new MyInput(), Every.Minutes(5))
    )
);

Custom Configuration

Use ConfigureLocalWorkers() to customize worker behavior:

services.AddTrax(trax => trax
    .AddEffects(effects => effects
        .UsePostgres(connectionString)
    )
    .AddMediator(typeof(Program).Assembly)
    .AddScheduler(scheduler => scheduler
        .ConfigureLocalWorkers(options =>
        {
            options.WorkerCount = 4;
            options.PollingInterval = TimeSpan.FromSeconds(2);
            options.VisibilityTimeout = TimeSpan.FromMinutes(15);
            options.ShutdownTimeout = TimeSpan.FromMinutes(1);
        })
        .Schedule<IMyTrain>("my-job", new MyInput(), Every.Minutes(5))
    )
);

Mixed Local and Remote Workers

Local workers are always registered alongside remote submitters. Trains not routed to a remote submitter execute locally:

services.AddTrax(trax => trax
    .AddEffects(effects => effects
        .UsePostgres(connectionString)
    )
    .AddMediator(assemblies)
    .AddScheduler(scheduler => scheduler
        .ConfigureLocalWorkers(opts => opts.WorkerCount = 8)
        .UseRemoteWorkers(
            remote => remote.BaseUrl = "https://gpu-workers/trax/execute",
            routing => routing
                .ForTrain<IHeavyComputeTrain>()
                .ForTrain<IAiInferenceTrain>())
        .Schedule<IMyTrain>("my-job", new MyInput(), Every.Minutes(5))
        .Schedule<IHeavyComputeTrain>("heavy-compute", new HeavyInput(), Every.Hours(1))
    )
);

In this example, IHeavyComputeTrain is dispatched to the remote HTTP endpoint, while IMyTrain executes locally with 8 worker threads.

Remarks

  • Local workers are the implicit default when UsePostgres() is configured. You only need ConfigureLocalWorkers() if you want to change the defaults.
  • No connection string parameter is needed. Local workers use the same IDataContext registered by UsePostgres().
  • No additional NuGet packages required. This is included in Trax.Scheduler.
  • Jobs are queued to the trax.background_job table and dequeued atomically using PostgreSQL's FOR UPDATE SKIP LOCKED.
  • 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.
  • AddScheduler refuses options outside their ranges when it is built (see Value Ranges); VisibilityTimeout must be between one second and ten years.
  • While a worker holds jobs, it refreshes the fetched_at of every job it has claimed and not yet finished, the running one and any waiting behind it in a batch, every third of VisibilityTimeout. If a worker crashes mid-execution, the refreshes stop, the timestamp becomes stale, and the job is reclaimed after VisibilityTimeout.
  • A job waiting in trax.background_job for a free worker is not failed by the stale-pending reaper, however long it waits.
  • When UseRemoteWorkers() or UseSqsWorkers() is also configured, local workers still run. Only the trains explicitly routed via ForTrain<T>() or [TraxRemote] are dispatched remotely.

Registered Services

The scheduler automatically registers these services when UsePostgres() is configured:

ServiceLifetimeDescription
LocalWorkerOptionsSingletonConfiguration options
IJobRunnerTrain → JobRunnerTrainScopedThe train that workers use to execute each job
IJobSubmitter → PostgresJobSubmitterScopedEnqueue implementation (INSERT into background_job)
LocalWorkerServiceHosted ServiceBackground worker that polls and executes jobs

Package

dotnet add package Trax.Scheduler

See Also