Docs / Guides
Retries and concurrency
Control what happens when a job fails, and how many jobs of a type may run at once.
Retry backoff
By default, a failed job retries up to 3 times with 10s / 30s / 2min backoff. Override this
per job with an AxonRetryPolicy:
var jobId = await axonClient.EnqueueAsync<MyClass>(x => x.WriteHelloWorld("Hello World"),
new AxonEnqueueOptions
{
RetryPolicy = new AxonRetryPolicy { MaxAttempts = 5, RetryDelaysSeconds = [5, 30, 120] }
});
MaxAttempts is the total attempts including the first — 5 means up
to 4 retries. RetryDelaysSeconds is indexed by retry attempt (0-based); once
exhausted, the last entry is reused for every further retry. [5, 30, 120] with
MaxAttempts = 10 retries at 5s, 30s, then 120s, 120s, 120s, ... for the remaining
attempts.
Concurrency limits
Cap how many jobs of a given type may be Processing at once across the whole
fleet — useful for throttling against a rate-limited downstream dependency:
var jobId = await axonClient.EnqueueAsync<MyClass>(x => x.SendEmail(to),
new AxonEnqueueOptions { ConcurrencyKey = "email-sender", MaxConcurrent = 5 });
At most 5 jobs sharing the "email-sender" key will be Processing at
once; a 6th stays Enqueued until one of the 5 finishes — succeeds, fails
terminally, or is reclaimed as orphaned. The limit is enforced atomically inside the same
claim operation that makes multi-instance dispatch safe (see
Architecture), so it holds even with
multiple Axon.Server instances racing to claim jobs sharing a key.
Instead of passing ConcurrencyKey/MaxConcurrent at every call site,
declare a default on the method itself:
public class EmailJobs
{
[AxonConcurrencyLimit("email-sender", 5)]
public void SendEmail(string to) { /* ... */ }
}
An explicit AxonEnqueueOptions.ConcurrencyKey always overrides the attribute when
both are present.