Skip to content

Attribute Reference

The Background Jobs module uses two attributes to declare job payloads and their handlers. The generator discovers these at compile time and wires up dispatch, retry logic, and scheduling.

[BackgroundJob]

Declares a record type as a background job payload with execution configuration.

Property Type Default Description
Schedule string? null Cron expression for recurring execution. When null, on-demand only.
MaxRetries int 3 Maximum retry attempts on failure
RetryDelayMs int 1000 Base delay between retries in milliseconds (exponential backoff: RetryDelayMs × 2^attempt)
TimeoutMs int 300000 Execution timeout in milliseconds (5 minutes). Also controls lock expiry for heartbeat.
Priority int 0 Job priority. Higher values are processed first.
DeduplicateBy string? null Property name on the payload to use as an idempotency key. Prevents duplicate active jobs.
using Deepstaging.Jobs;

// Recurring job — runs every 5 minutes
[BackgroundJob(Schedule = "0 */5 * * * *")]
public record SyncInventory(string WarehouseId);

// On-demand with custom retry policy
[BackgroundJob(MaxRetries = 5, RetryDelayMs = 1000)]
public record SendWelcomeEmail(string UserId);

// Long-running with extended timeout (15 minutes)
[BackgroundJob(TimeoutMs = 900000, MaxRetries = 1)]
public record GenerateReport(string ReportId);

// High-priority job — processed before normal-priority jobs
[BackgroundJob(Priority = 10)]
public record ProcessPayment(string OrderId);

// Deduplicated job — only one active job per UserId at a time
[BackgroundJob(DeduplicateBy = nameof(UserId))]
public record SyncUserProfile(string UserId);

Priority

Jobs with higher Priority values are claimed before lower-priority jobs. When multiple jobs are pending, ClaimNextAsync orders by Priority DESC, then NextRunAt, then CreatedAt.

[BackgroundJob(Priority = 100)]  // Processed first
public record CriticalAlert(string Message);

[BackgroundJob(Priority = 0)]    // Default priority
public record SendNewsletter(string CampaignId);

Deduplication

When DeduplicateBy names a payload property, the scheduler computes an idempotency key ({FullTypeName}:{PropertyValue}) and checks for existing active jobs (Pending or Running) with the same key. If a duplicate exists, the existing job ID is returned instead of creating a new entry.

[BackgroundJob(DeduplicateBy = nameof(UserId))]
public record SyncUserProfile(string UserId);

// First call creates the job
var id1 = await scheduler.EnqueueAsync(new SyncUserProfile("user-42"));

// Second call returns the same ID — no duplicate created
var id2 = await scheduler.EnqueueAsync(new SyncUserProfile("user-42"));
// id1 == id2

DSJOB04

The DeduplicateBy value must correspond to a public property on the job payload type. A diagnostic (DSJOB04) warns at compile time if the property doesn't exist.

Cron Expression Format

The Schedule property accepts standard 6-field cron expressions:

┌───────────── second (0–59)
│ ┌───────────── minute (0–59)
│ │ ┌───────────── hour (0–23)
│ │ │ ┌───────────── day of month (1–31)
│ │ │ │ ┌───────────── month (1–12)
│ │ │ │ │ ┌───────────── day of week (0–6, Sun=0)
│ │ │ │ │ │
* * * * * *
Expression Meaning
0 */5 * * * * Every 5 minutes
0 0 * * * * Every hour
0 0 0 * * * Daily at midnight
0 0 9 * * 1-5 Weekdays at 9 AM
0 30 2 1 * * 1st of every month at 2:30 AM

[JobHandler]

Marks a static method as a handler for a specific job type. The first parameter determines which job type is routed to this handler — similar to dispatch command handlers.

using Deepstaging.Jobs;
using LanguageExt;

public static class InventoryJobs
{
    [JobHandler]
    public static Eff<AppRuntime, Unit> Handle(SyncInventory job) =>
        from inventory in WarehouseEffects.Warehouse
            .GetInventoryAsync<AppRuntime>(job.WarehouseId)
        from _ in WarehouseEffects.Warehouse
            .SyncAsync<AppRuntime>(inventory)
        select unit;
}

public static class EmailJobs
{
    [JobHandler]
    public static Eff<AppRuntime, Unit> Handle(SendWelcomeEmail job) =>
        from _ in EmailEffects.Email.SendAsync<AppRuntime>(
            job.UserId, "Welcome!", "Welcome to our platform.")
        select unit;
}

The handler must:

  • Be a static method
  • Have at least one parameter (the job payload type)
  • Return Eff<RT, Unit> (effectful) or Task (plain async)