AddTraxWorker

Registers a standalone worker process that polls the background_job table and executes trains. No scheduler logic, just execution.

Signature

public static IServiceCollection AddTraxWorker(
    this IServiceCollection services,
    Action<LocalWorkerOptions>? configure = null
)

Parameters

ParameterTypeRequiredDescription
configureAction<LocalWorkerOptions>?NoOptional callback to customize worker count, polling interval, and timeouts

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 before another worker can reclaim it (crash recovery). 1 second to 10 years
ShutdownTimeoutTimeSpan30 secondsGrace period for in-flight jobs during shutdown. 0 to 30 days

These are the same options used by ConfigureLocalWorkers, with the same ranges (BatchSize at least 1 too). AddTraxWorker throws InvalidOperationException listing every option outside its range.

Examples

Basic Standalone Worker

var builder = WebApplication.CreateBuilder(args);
 
builder.Services.AddTrax(trax => trax
    .AddEffects(effects => effects
        .UsePostgres(connectionString)
    )
    .AddMediator(typeof(MyTrain).Assembly)
);
builder.Services.AddTraxWorker();
 
var app = builder.Build();
app.Run();

Custom Worker Configuration

builder.Services.AddTraxWorker(opts =>
{
    opts.WorkerCount = 4;
    opts.PollingInterval = TimeSpan.FromSeconds(2);
    opts.VisibilityTimeout = TimeSpan.FromMinutes(15);
    opts.ShutdownTimeout = TimeSpan.FromMinutes(1);
});

Multiple Worker Processes

You can run multiple standalone worker processes against the same database. PostgreSQL's FOR UPDATE SKIP LOCKED guarantees each job is claimed by exactly one worker. No duplicates, no coordination needed.

┌── Worker Process A ──┐    ┌── Worker Process B ──┐
│  4 worker tasks       │    │  4 worker tasks       │
│  polling same table   │    │  polling same table   │
└───────────┬───────────┘    └───────────┬───────────┘
            │                            │
            └──────────┬─────────────────┘
                       ▼
              background_job table
              (SKIP LOCKED ensures
               no duplicate claims)

What It Registers

AddTraxWorker() internally calls AddTraxJobRunner() and adds the worker service:

ServiceLifetimeDescription
All services from AddTraxJobRunner()(various)Execution pipeline (JobRunnerTrain, CancellationRegistry, etc.)
LocalWorkerOptionsSingletonWorker configuration
LocalWorkerServiceHosted ServiceBackground worker that polls background_job and executes trains

Not registered: ManifestManager, JobDispatcher, polling services, startup service. This process only executes; it doesn't schedule or dispatch.

A scheduler host whose AddScheduler already runs local workers (the default on Postgres) refuses AddTraxWorker(), in either order, with an InvalidOperationException: two sets of LocalWorkerOptions would silently replace one another. Configure the scheduler's pool with AddScheduler(scheduler => scheduler.ConfigureLocalWorkers(o => ...)) instead.

How It Differs from the Scheduler's Local Workers

AspectScheduler (with Postgres)AddTraxWorker()
Used inFull scheduler processStandalone worker process
SchedulingYes (ManifestManager, JobDispatcher)No
DispatchingYes (writes to WorkQueue, background_job)No
ExecutionYes (LocalWorkerService)Yes (LocalWorkerService)
Job submitterRegisters PostgresJobSubmitterDoes not register any submitter

Shared Requirements

The standalone worker must:

  • Reference the same train assemblies passed to AddMediator(). Train types are resolved by fully-qualified name.
  • Connect to the same Postgres database. Metadata, manifests, and state are shared across all processes.
  • Register the effect system. AddTrax() with UsePostgres() is required.

Package

dotnet add package Trax.Scheduler

See Also