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
staticmethod - Have at least one parameter (the job payload type)
- Return
Eff<RT, Unit>(effectful) orTask(plain async)