Skip to content

TraxSharp/Trax.Scheduler

Repository files navigation

Trax.Scheduler

Build NuGet Version NuGet Downloads .NET License: MIT Last Commit codecov Docs

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

You'll also need a storage depot for persistent scheduling:

dotnet add package Trax.Effect.Data.Postgres

Setup

Add the scheduler inside your AddTrax configuration:

builder.Services.AddTrax(trax =>
    trax.AddEffects(effects =>
            effects.UsePostgres(connectionString).SaveTrainParameters().AddStepProgress()
        )
        .AddMediator(typeof(Program).Assembly)
        .AddScheduler(scheduler =>
            scheduler.Schedule<IGenerateReportTrain>(
                "nightly-report",
                new GenerateReportInput { Format = "pdf" },
                Cron.Daily(hour: 3)
            )
        )
);

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 (anomaliesDetected)
        {
            await dormants.ActivateAsync<IQualityCheckTrain, QualityCheckInput>(
                "quality-0",
                new QualityCheckInput { /* ... */ }
            );
        }

        return Unit.Default;
    }
}

How the Yard Works

The scheduler runs as three internal trains — all visible in the control room with full journey logging:

  1. ManifestManager — the yard master. Polls on an interval, checks which manifests are due, and queues departures.
  2. JobDispatcher — the dispatcher. Reads the departure queue, respects per-line capacity limits, and assigns trains to the track.
  3. TaskServerExecutor — the engineer. Picks up an assigned train and drives it through its route.

Journey Log Cleanup

Long-running timetables accumulate journey records. Configure automatic archival per train type:

scheduler.AddMetadataCleanup(cleanup =>
{
    cleanup.AddTrainType<IHealthCheckTrain>();
    cleanup.AddTrainType<ISyncCustomersTrain>();
});

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.

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.

About

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages