ITraxScheduler

The runtime scheduling API. Inject it to create, change, trigger and cancel manifests while the application runs, and to resolve dead letters. AddScheduler registers it. The startup equivalent, which seeds manifests when the host starts, is the scheduler builder.

Signature

namespace Trax.Scheduler.Services.TraxScheduler;
 
public interface ITraxScheduler
{
    // Scheduling
    Task<Manifest> ScheduleAsync<TTrain, TInput, TOutput>(string externalId, TInput input, Schedule schedule,
        Action<ScheduleOptions>? options = null, CancellationToken ct = default);
    Task<IReadOnlyList<Manifest>> ScheduleManyAsync<TTrain, TInput, TOutput, TSource>(IEnumerable<TSource> sources,
        Func<TSource, (string ExternalId, TInput Input)> map, Schedule schedule,
        Action<ScheduleOptions>? options = null, Action<TSource, ManifestOptions>? configureEach = null,
        CancellationToken ct = default);
    Task<Manifest> ScheduleDependentAsync<TTrain, TInput, TOutput>(string externalId, TInput input,
        string dependsOnExternalId, Action<ScheduleOptions>? options = null, CancellationToken ct = default);
    Task<IReadOnlyList<Manifest>> ScheduleManyDependentAsync<TTrain, TInput, TOutput, TSource>(
        IEnumerable<TSource> sources, Func<TSource, (string ExternalId, TInput Input)> map,
        Func<TSource, string> dependsOn, Action<ScheduleOptions>? options = null,
        Action<TSource, ManifestOptions>? configureEach = null, CancellationToken ct = default);
    Task<Manifest> ScheduleOnceAsync<TTrain, TInput, TOutput>(TInput input, TimeSpan delay,
        Action<ScheduleOptions>? options = null, CancellationToken ct = default);
    Task<Manifest> ScheduleOnceAsync<TTrain, TInput, TOutput>(string externalId, TInput input, TimeSpan delay,
        Action<ScheduleOptions>? options = null, CancellationToken ct = default);
 
    // Manifest control
    Task DisableAsync(string externalId, CancellationToken ct = default);
    Task EnableAsync(string externalId, CancellationToken ct = default);
    Task TriggerAsync(string externalId, CancellationToken ct = default);
    Task TriggerAsync(string externalId, TimeSpan delay, CancellationToken ct = default);
    Task<int> TriggerGroupAsync(long groupId, CancellationToken ct = default);
    Task<int> CancelAsync(string externalId, CancellationToken ct = default);
    Task<int> CancelGroupAsync(long groupId, CancellationToken ct = default);
 
    // Dead letters
    Task<DeadLetterOperationResult> RequeueDeadLetterAsync(long deadLetterId, CancellationToken ct = default);
    Task<DeadLetterOperationResult> AcknowledgeDeadLetterAsync(long deadLetterId, string note, CancellationToken ct = default);
    Task<BatchDeadLetterResult> RequeueDeadLettersAsync(long[] deadLetterIds, CancellationToken ct = default);
    Task<BatchDeadLetterResult> AcknowledgeDeadLettersAsync(long[] deadLetterIds, string note, CancellationToken ct = default);
    Task<BatchDeadLetterResult> RequeueAllDeadLettersAsync(CancellationToken ct = default);
    Task<BatchDeadLetterResult> AcknowledgeAllDeadLettersAsync(string note, CancellationToken ct = default);
}

Every scheduling method constrains TTrain : IServiceTrain<TInput, TOutput> and TInput : IManifestProperties.

Members

MethodPage
ScheduleAsyncSchedule
ScheduleManyAsyncScheduleMany
ScheduleDependentAsync, ScheduleManyDependentAsyncDependent Scheduling
ScheduleOnceAsync, DisableAsync, EnableAsync, TriggerAsync, CancelAsync, CancelGroupAsyncManifest Management
TriggerGroupAsyncBelow
The six dead-letter methodsBelow

TriggerGroupAsync

Queues an immediate run of every eligible manifest in a manifest group.

Task<int> TriggerGroupAsync(long groupId, CancellationToken ct = default)
ParameterTypeDescription
groupIdlongThe manifest group's id
ctCancellationTokenCancellation token

Returns: the number of manifests queued.

  • Only enabled manifests with a non-dependent schedule type (None, Cron, Interval, OnDemand) are queued. Dependent and dormant dependent manifests are skipped: they rely on a parent's completion and may have no standalone input.
  • Each entry is marked as asked for by name, the way TriggerAsync marks one, so disabling a manifest after the group trigger does not hold its run.
  • A manifest that already has a queued entry is not counted and does not stop the others. That entry is marked the same way, and brought forward to now if it was due later. The number skipped is logged.

Dead letters

A manifest whose failures exceed its retry limit is dead-lettered and stops being scheduled until someone resolves it. Each method below resolves dead letters in AwaitingIntervention; anything already resolved is left alone. Resolving either way resets the manifest's failure counter, so it resumes normal scheduling.

MethodWhat it does
RequeueDeadLetterAsync(deadLetterId)Queues a new run of the manifest and marks the dead letter Retried. A manifest holds one queued entry at a time, so when it already has one the result is a failure and the dead letter stays awaiting intervention.
AcknowledgeDeadLetterAsync(deadLetterId, note)Marks it Acknowledged with your note, without retrying
RequeueDeadLettersAsync(ids)As the single requeue, for up to 1000 ids. At most one entry is queued per manifest: dead letters that share a manifest are folded into one entry and all resolved; one whose manifest already has a queued entry is skipped.
AcknowledgeDeadLettersAsync(ids, note)As the single acknowledge, for up to 1000 ids
RequeueAllDeadLettersAsync()Requeues every dead letter awaiting intervention, a page of manifests at a time, so a large backlog is never loaded at once
AcknowledgeAllDeadLettersAsync(note)Acknowledges every dead letter awaiting intervention

A batch call with an empty list, or more than 1000 ids, is refused: the result counts nothing and its message says why.

Result types

public record DeadLetterOperationResult(bool Success, long? WorkQueueId, string Message);
public record BatchDeadLetterResult(int Count, string Message);
FieldDescription
DeadLetterOperationResult.SuccessWhether the dead letter was resolved
DeadLetterOperationResult.WorkQueueIdThe queued entry a requeue created; null for an acknowledge or a failure
DeadLetterOperationResult.MessageWhat happened, including why it failed (not found, already resolved, already queued)
BatchDeadLetterResult.CountHow many dead letters were resolved
BatchDeadLetterResult.MessageA summary that also counts the folded and skipped dead letters

These are the same operations the dashboard and the GraphQL operations namespace use. See Dead Letters for the concepts.

Example

public class GroupController(ITraxScheduler scheduler)
{
    public async Task<string> RunGroupNow(long groupId, CancellationToken ct)
    {
        var queued = await scheduler.TriggerGroupAsync(groupId, ct);
        return $"{queued} manifest(s) queued.";
    }
 
    public Task<BatchDeadLetterResult> RetryEverything(CancellationToken ct) =>
        scheduler.RequeueAllDeadLettersAsync(ct);
}

Package

dotnet add package Trax.Scheduler