JobContext
Runtime context injected into every job execution. Provides metadata about the current job, a cancellation token, a scoped logger, and a progress reporting callback. Never construct this class yourself — the SDK injects it automatically during job execution.
Namespace: Zeridion.Flare · Assembly: Zeridion.Flare.dll
public sealed class JobContext
{
public required string JobId { get; init; }
public required int AttemptNumber { get; init; }
public required int MaxAttempts { get; init; }
public required DateTimeOffset EnqueuedAt { get; init; }
public required CancellationToken CancellationToken { get; init; }
public required ILogger Logger { get; init; }
public Func<double, Task> ReportProgress { get; init; } = _ => Task.CompletedTask;
}
Properties
| Property | Type | Description |
|---|---|---|
JobId | string | Unique identifier assigned by the server when the job was created. |
AttemptNumber | int | Current attempt number, starting at 1. Increments on each retry. |
MaxAttempts | int | Maximum number of attempts configured for this job. |
EnqueuedAt | DateTimeOffset | UTC timestamp when the job was originally enqueued. |
CancellationToken | CancellationToken | Cancelled on host shutdown, job timeout, or explicit cancellation. |
Logger | ILogger | Logger scoped to this job execution with structured context. |
ReportProgress | Func<double, Task> | Report progress from 0.0 to 1.0, visible in the dashboard. |
CancellationToken
The CancellationToken is a linked token that fires in any of these scenarios:
- Host shutdown — the application is stopping gracefully (
IHostApplicationLifetime) - Job timeout — the configured timeout period has elapsed since the worker started processing
- Explicit cancellation — a cancel request was received while the job was processing
Pass this token to every async operation inside your job — HTTP calls, database queries, file I/O — so work stops promptly when cancellation is requested:
public async Task ExecuteAsync(OrderPayload payload, JobContext ctx)
{
var response = await _httpClient.PostAsync(url, content, ctx.CancellationToken);
await _db.SaveChangesAsync(ctx.CancellationToken);
}
The worker reports progress to Flare periodically while the job runs (via the heartbeat path), so dashboard observers see the value advance live. If the worker stops reporting progress, Flare reclaims the job so another worker can pick it up.
ReportProgress
Call ReportProgress with a value between 0.0 and 1.0 to update the job's progress in the dashboard. This is useful for long-running jobs that process items in a loop:
public async Task ExecuteAsync(BatchPayload payload, JobContext ctx)
{
for (int i = 0; i < payload.Items.Count; i++)
{
await ProcessItem(payload.Items[i], ctx.CancellationToken);
await ctx.ReportProgress((double)(i + 1) / payload.Items.Count);
}
}
ReportProgress is safe to call at any frequency. The reported value is sent to the API on the worker's next heartbeat and persisted with a monotonic GREATEST() guard so a later, smaller value never clobbers a higher reported value. The dashboard reflects updates within a few seconds. Reporting more often than once per second offers no extra visibility — pick a granularity that matches the work being done.
Usage example
A job that uses all context properties:
[JobConfig(MaxAttempts = 5, TimeoutSeconds = 600)]
public class ProcessLargeDataset : IJob<DatasetPayload>
{
private readonly IDataStore _store;
public ProcessLargeDataset(IDataStore store) => _store = store;
public async Task ExecuteAsync(DatasetPayload payload, JobContext ctx)
{
ctx.Logger.LogInformation(
"Starting job {JobId}, attempt {Attempt}/{Max}, enqueued at {EnqueuedAt}",
ctx.JobId, ctx.AttemptNumber, ctx.MaxAttempts, ctx.EnqueuedAt);
var records = await _store.GetRecordsAsync(payload.DatasetId, ctx.CancellationToken);
for (int i = 0; i < records.Count; i++)
{
ctx.CancellationToken.ThrowIfCancellationRequested();
await _store.ProcessAsync(records[i], ctx.CancellationToken);
await ctx.ReportProgress((double)(i + 1) / records.Count);
}
ctx.Logger.LogInformation("Job {JobId} completed, processed {Count} records",
ctx.JobId, records.Count);
}
}
See also
- IJob<T> — the job interface that receives
JobContext - IRecurringJob — recurring jobs also receive
JobContext - Progress reporting — patterns for reporting progress in batch jobs