Trax.Scheduler
1.34.1
Prefix Reserved
See the version list below for details.
dotnet add package Trax.Scheduler --version 1.34.1
NuGet\Install-Package Trax.Scheduler -Version 1.34.1
<PackageReference Include="Trax.Scheduler" Version="1.34.1" />
<PackageVersion Include="Trax.Scheduler" Version="1.34.1" />
<PackageReference Include="Trax.Scheduler" />
paket add Trax.Scheduler --version 1.34.1
#r "nuget: Trax.Scheduler, 1.34.1"
#:package Trax.Scheduler@1.34.1
#addin nuget:?package=Trax.Scheduler&version=1.34.1
#tool nuget:?package=Trax.Scheduler&version=1.34.1
Trax.Scheduler
Timetable management for Trax trains — recurring schedules, automatic retries, dead-letter handling, and dependent departures.
The Trax Stack
Trax is a layered framework split across several repos. You can stop at whatever layer solves your problem. You are here: Trax.Scheduler.
| Repo | Adds |
|---|---|
| Trax.Core | Pipelines, junctions, railway error propagation |
| Trax.Effect | Execution logging, DI, pluggable storage |
| Trax.Mediator | Decoupled dispatch via TrainBus |
| Trax.Scheduler | Cron schedules, retries, dead-letter queues |
| Trax.Api | GraphQL API for remote access |
| Trax.Dashboard | Blazor monitoring UI |
| Trax.Cli | trax-cli project scaffolding tool |
| Trax.Samples | Sample apps and a dotnet new template |
Full documentation: traxsharp.net/docs.
What This Does
If you have trains that need to run on a timetable — ETL pipelines, data syncs, nightly reports, periodic cleanup — Trax.Scheduler handles the dispatch. You write a manifest for each train (what cargo it carries, when it departs, how many times to retry if it derails), and the scheduler takes care of the rest.
Every scheduled run is a normal train journey, so you get the same journey logging, station services, and control room visibility as any other train.
Installation
dotnet add package Trax.Scheduler
dotnet add package Trax.Effect.Data.Postgres
The scheduler needs a persistent data provider. Manifests, the work queue and dead letters live in the database, so Trax.Scheduler alone is not enough: add Trax.Effect.Data.Postgres and call UsePostgres(...) (or Trax.Effect.Data.Sqlite and UseSqlite(...) for a single-node host). AddScheduler() throws at startup when no data provider is configured. Trax.Effect.Data.InMemory (UseInMemory()) also satisfies that check, but nothing survives a restart, so keep it to tests.
Optional packages, for running trains outside the scheduler host:
| Package | Use it when |
|---|---|
Trax.Scheduler.Lambda |
the scheduler invokes an AWS Lambda function directly (UseLambdaWorkers, UseLambdaRun) |
Trax.Runner.Lambda |
you are writing that Lambda function (TraxLambdaFunction base class) |
Trax.Scheduler.Sqs |
jobs go to workers through an Amazon SQS queue (UseSqsWorkers, SqsJobRunnerHandler) |
Trax.Scheduler.Tests.ArrayLogger |
a test needs to assert on what was logged (in-memory ILoggerProvider) |
Setup
A scheduled train is an ordinary Trax train whose input implements IManifestProperties, so the scheduler can store it with the manifest:
using LanguageExt;
using Trax.Core.Junction;
using Trax.Effect.Data.Postgres.Extensions;
using Trax.Effect.Extensions;
using Trax.Effect.Models.Manifest;
using Trax.Effect.Services.ServiceTrain;
using Trax.Mediator.Extensions;
using Trax.Scheduler.Extensions;
using Trax.Scheduler.Services.Scheduling;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("TraxDatabase")!;
builder.Services.AddTrax(trax =>
trax.AddEffects(effects => effects.UsePostgres(connectionString))
.AddMediator(typeof(Program).Assembly)
.AddScheduler(scheduler =>
scheduler.Schedule<IGenerateReportTrain>(
"nightly-report",
new GenerateReportInput { Format = "pdf" },
Cron.Daily(hour: 3)
)
)
);
builder.Build().Run();
public record GenerateReportInput : IManifestProperties
{
public string Format { get; init; } = "pdf";
}
public interface IGenerateReportTrain : IServiceTrain<GenerateReportInput, Unit>;
public class GenerateReportTrain : ServiceTrain<GenerateReportInput, Unit>, IGenerateReportTrain
{
protected override Task<Either<Exception, Unit>> Junctions() =>
Chain<RenderReportJunction>().Resolve();
}
public class RenderReportJunction : Junction<GenerateReportInput, Unit>
{
public override Task<Unit> Run(GenerateReportInput input) => Task.FromResult(Unit.Default);
}
Schedule<TTrain> infers the input type from the train's IServiceTrain<TInput, TOutput> interface. The explicit form Schedule<IGenerateReportTrain, GenerateReportInput, Unit>(...) takes all three type arguments; there is no two-argument overload. Manifests declared here are upserted by external ID when the host starts.
Writing Manifests
A manifest describes a scheduled train: which service to run, what cargo it carries, and when it departs. Just like a shipping manifest lists what's on board and where it's going.
Interval-based departures
scheduler.Schedule<IHealthCheckTrain>(
"health-check",
new HealthCheckInput(),
Every.Minutes(5)
);
Cron-based departures
scheduler.Schedule<ISyncCustomersTrain>(
"sync-customers",
new SyncCustomersInput { Source = "crm" },
Cron.Hourly(minute: 0)
);
Available cron helpers: Cron.Minutely(), Cron.Hourly(), Cron.Daily(), Cron.Weekly(), Cron.Monthly(), and Cron.Expression("...") for arbitrary cron strings.
Retry policy
scheduler.Schedule<IImportDataTrain>(
"import-data",
new ImportDataInput(),
Every.Hours(1),
options: o => o.MaxRetries(5)
);
A train that derails gets re-dispatched up to MaxRetries times. If it keeps failing, the manifest moves to the dead-letter queue — the lost shipment office where undeliverable work sits until someone investigates.
Fleet Scheduling
Dispatch a fleet of the same train type with different cargo:
scheduler.ScheduleMany<IExtractTrain>(
"extract",
Enumerable.Range(0, 10).Select(i =>
new ManifestItem($"{i}", new ExtractInput { TableIndex = i })),
Every.Minutes(5)
);
This creates 10 manifests (extract-0 through extract-9), each departing on the same interval with different cargo.
Connected Departures
Schedule trains so that one departs only after another arrives:
scheduler
.Schedule<IExtractTrain>(
"extract",
new ExtractInput(),
Every.Hours(1)
)
.Include<ITransformTrain>(
"transform",
new TransformInput()
);
transform departs automatically when extract arrives successfully. You can chain further with .ThenInclude<T>(), or fan out with .IncludeMany<T>() and .ThenIncludeMany<T>() for fleet-scale dependent scheduling.
Trains waiting in the yard
Sometimes a dependent train should only depart conditionally. Mark it as dormant — it sits in the yard, ready to go, waiting for a signal:
// In scheduler config
scheduler
.Schedule<IExtractTrain>("extract", input, Every.Hours(1))
.IncludeMany<IQualityCheckTrain>(
"quality",
items,
options: o => o.Dormant()
);
// In a step, when you decide it's needed — signal the departure
public class CheckDataJunction(IDormantDependentContext dormants) : Junction<ExtractInput, Unit>
{
public override async Task<Unit> Run(ExtractInput input)
{
if (input.AnomaliesDetected)
{
await dormants.ActivateAsync<IQualityCheckTrain, QualityCheckInput, Unit>(
"quality-0",
new QualityCheckInput { /* ... */ }
);
}
return Unit.Default;
}
}
Scheduling at Runtime
Inject ITraxScheduler to create, trigger or cancel manifests after startup. Its methods take the train, input and output types explicitly:
public class ReportController(ITraxScheduler scheduler)
{
public Task<Manifest> ScheduleWeekly(string format) =>
scheduler.ScheduleAsync<IGenerateReportTrain, GenerateReportInput, Unit>(
$"weekly-report-{format}",
new GenerateReportInput { Format = format },
Cron.Weekly(DayOfWeek.Monday, hour: 6)
);
public Task<Manifest> RunInFiveMinutes() =>
scheduler.ScheduleOnceAsync<IGenerateReportTrain, GenerateReportInput, Unit>(
new GenerateReportInput(),
TimeSpan.FromMinutes(5)
);
public Task<int> Cancel(string externalId) => scheduler.CancelAsync(externalId);
}
ITraxScheduler lives in Trax.Scheduler.Services.TraxScheduler, Manifest in Trax.Effect.Models.Manifest.
How the Yard Works
The scheduler runs as three internal trains — all visible in the control room with full journey logging:
- ManifestManager — the yard master. Polls on an interval, checks which manifests are due, and queues departures.
- JobDispatcher — the dispatcher. Reads the departure queue, respects per-line capacity limits, and assigns trains to the track.
- JobRunner — the engineer. Picks up an assigned job and drives the train through its route, recording the outcome.
Journey Log Cleanup
Long-running timetables accumulate journey records. Configure automatic deletion of finished runs per train type:
scheduler.AddMetadataCleanup(cleanup =>
{
cleanup.RetentionPeriod = TimeSpan.FromHours(2);
cleanup.AddTrainType<IHealthCheckTrain>();
cleanup.AddTrainType<ISyncCustomersTrain>(TimeSpan.FromDays(7));
});
Next Layer
When you need a programmatic interface for external consumers (queuing jobs, running trains on demand, querying state over HTTP), move up to Trax.Api.
Documentation
Guides, the full scheduler reference and the architecture of the whole stack: traxsharp.net/docs.
License
MIT
Trademark & Brand Notice
Trax is an open-source .NET framework provided by TraxSharp. This project is an independent community effort and is not affiliated with, sponsored by, or endorsed by the Utah Transit Authority, Trax Retail, or any other entity using the "Trax" name in other industries.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Cronos (>= 0.13.0)
- Npgsql (>= 10.0.3)
- Trax.Effect (>= 1.57.1)
- Trax.Effect.Data (>= 1.57.1)
- Trax.Mediator (>= 1.23.0)
NuGet packages (5)
Showing the top 5 NuGet packages that depend on Trax.Scheduler:
| Package | Downloads |
|---|---|
|
Trax.Api
Core services behind the Trax .NET GraphQL API: health checks, per-train authorization, the HTTP principal provider and the shared DTOs. Trax.Api.GraphQL brings it in; reference it directly only to use those services without the GraphQL schema. |
|
|
Trax.Dashboard
Blazor Server operations dashboard for Trax .NET, mounted into your existing ASP.NET Core host at /trax: trains, run metadata and logs, scheduler manifests and groups, dead letters, the work queue, and runtime effect settings. Install it in the app that already calls AddTrax, then call builder.AddTraxDashboard() and app.UseTraxDashboard(). |
|
|
Trax.Runner.Lambda
Base class for an AWS Lambda function that executes Trax .NET trains invoked directly by a scheduler using Trax.Scheduler.Lambda (UseLambdaWorkers or UseLambdaRun). Install it in the Lambda project that hosts your trains. |
|
|
Trax.Scheduler.Sqs
Amazon SQS transport for Trax.Scheduler: dispatches queued jobs as SQS messages and provides the SQS-triggered Lambda consumer that runs them. Install it when scheduled trains should run on workers fed by an SQS queue. |
|
|
Trax.Scheduler.Lambda
AWS Lambda transport for Trax.Scheduler: dispatches queued jobs and synchronous train runs to an AWS Lambda function by direct SDK invocation. Install it in the scheduler host when your trains run in a Lambda function built on Trax.Runner.Lambda. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.62.0 | 0 | 10/9/2026 |
| 1.61.0 | 196 | 10/6/2026 |
| 1.37.0 | 274 | 10/6/2026 |
| 1.36.1 | 248 | 10/5/2026 |
| 1.36.0 | 394 | 10/4/2026 |
| 1.35.0 | 216 | 10/2/2026 |
| 1.34.2 | 231 | 10/1/2026 |
| 1.34.1 | 364 | 9/29/2026 |
| 1.34.0 | 398 | 9/28/2026 |
| 1.33.0 | 378 | 9/25/2026 |
| 1.32.2 | 505 | 9/24/2026 |
| 1.32.1 | 807 | 9/16/2026 |
| 1.32.0 | 1,130 | 9/15/2026 |
| 1.31.0 | 2,774 | 7/29/2026 |
| 1.30.0 | 283 | 7/7/2026 |
| 1.29.0 | 5,177 | 5/8/2026 |
| 1.28.0 | 238 | 5/4/2026 |
| 1.27.0 | 823 | 4/15/2026 |
| 1.26.0 | 292 | 4/3/2026 |
| 1.25.1 | 181 | 4/3/2026 |