Skip to main content

Option Precedence

Queue name, max attempts, and timeout are configured at two levels. When a job is enqueued, the SDK resolves each setting by checking the levels top-to-bottom — the first non-null value wins, otherwise hardcoded defaults apply.

The two levels

PrioritySourceScopeWhen to use
1 (highest)JobOptionsPer-callOne-off overrides for specific enqueue calls
2 (lowest)[JobConfig]Per-classDefaults that apply to every run of a job type
note

ZeridionFlareOptions has DefaultQueue, DefaultMaxAttempts, and DefaultTimeout properties, but the SDK's enqueue path does not currently read them. The effective cascade is JobOptions > [JobConfig] > hardcoded defaults. The global options properties are reserved for a future release.

Resolution table

SettingJobOptions[JobConfig]Hardcoded default
Queue.Queue.Queue"default"
Max Attempts.MaxAttempts.MaxAttempts3
Timeout.Timeout.TimeoutSeconds30 minutes

Properties left as null (for JobOptions) or at their attribute defaults (for [JobConfig]) fall through to the hardcoded defaults.

Example scenario

Consider a payment processing application with these configurations:

Class-level attribute (on the job):

[JobConfig(Queue = "payments", MaxAttempts = 10, TimeoutSeconds = 300)]
public class ProcessPayment : IJob<PaymentPayload>
{
public async Task ExecuteAsync(PaymentPayload payload, JobContext ctx)
{
// ...
}
}

Per-call override (at the enqueue site):

// Scenario A: No JobOptions — uses [JobConfig] values
await jobs.EnqueueAsync<ProcessPayment>(payload);
// Queue = "payments", MaxAttempts = 10, Timeout = 5 min

// Scenario B: Override max attempts only
await jobs.EnqueueAsync<ProcessPayment>(payload, new JobOptions
{
MaxAttempts = 1
});
// Queue = "payments" (from [JobConfig]), MaxAttempts = 1 (from JobOptions), Timeout = 5 min (from [JobConfig])

// Scenario C: Override everything
await jobs.EnqueueAsync<ProcessPayment>(payload, new JobOptions
{
Queue = "critical",
MaxAttempts = 20,
Timeout = TimeSpan.FromMinutes(60)
});
// Queue = "critical", MaxAttempts = 20, Timeout = 60 min (all from JobOptions)

What each scenario resolves to

SettingScenario AScenario BScenario C
Queue"payments" (attr)"payments" (attr)"critical" (opts)
MaxAttempts10 (attr)1 (opts)20 (opts)
Timeout5 min (attr)5 min (attr)60 min (opts)

Jobs without [JobConfig]

If a job class has no [JobConfig] attribute, the hardcoded defaults apply directly ("default", 3, 1800). The JobTypeRegistry uses the same fallback values as the attribute's property defaults, so the resolution falls through to the hardcoded defaults.

// No [JobConfig] attribute
public class SendWelcomeEmail : IJob<NewUserEvent>
{
public async Task ExecuteAsync(NewUserEvent payload, JobContext ctx) { /* ... */ }
}

await jobs.EnqueueAsync<SendWelcomeEmail>(payload);
// Queue = "default", MaxAttempts = 3, Timeout = 30 min (all hardcoded defaults)

await jobs.EnqueueAsync<SendWelcomeEmail>(payload, new JobOptions { Queue = "email" });
// Queue = "email" (opts), MaxAttempts = 3 (default), Timeout = 30 min (default)

Tags and IdempotencyKey

Tags and IdempotencyKey exist only on JobOptions — they do not participate in the cascade. They are either set per-call or not set at all.

See also