Axon Docs
GitHub Quickstart

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:

WriteHelloWorld.cs
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:

SendEmail.cs
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:

EmailJobs.cs
public class EmailJobs
{
    [AxonConcurrencyLimit("email-sender", 5)]
    public void SendEmail(string to) { /* ... */ }
}

An explicit AxonEnqueueOptions.ConcurrencyKey always overrides the attribute when both are present.