Version v1.x
Example Tasks
Available in
.NET
Node.js
Python
Practical DurableStack job examples for common application workloads.
These examples show common task shapes you can adapt quickly.
Email notification job (typed payload)
using DurableStack.Core;
using DurableStack.Core.Abstractions;
using DurableStack.Hosting.DependencyInjection;
[DurableJob(Name = "send-welcome-email", MaxAttempts = 5)]
public sealed class SendWelcomeEmailJob : IDurableJob<SendWelcomeEmailArgs>
{
private readonly ILogger<SendWelcomeEmailJob> _logger;
public SendWelcomeEmailJob(ILogger<SendWelcomeEmailJob> logger)
{
_logger = logger;
}
public Task ExecuteAsync(SendWelcomeEmailArgs args, JobContext context, CancellationToken cancellationToken)
{
_logger.LogInformation("Sending welcome email to {Email}. RunId={RunId}", args.Email, context.RunId);
return Task.CompletedTask;
}
}
public sealed class SendWelcomeEmailArgs
{
public string Email { get; set; } = string.Empty;
}
type SendWelcomeEmailArgs = {
email: string;
};
runtime.registerJob<SendWelcomeEmailArgs>("send-welcome-email", async (args, context) => {
console.log(`Sending welcome email to ${args.email}. RunId=${context.runId}`);
});
Enqueue:
var runId = await durableClient.EnqueueAsync<SendWelcomeEmailJob>(new SendWelcomeEmailArgs
{
Email = "[email protected]"
}, cancellationToken);
const runId = await runtime.enqueue<SendWelcomeEmailArgs>("send-welcome-email", {
email: "[email protected]"
});
Recurring cleanup job
[DurableJob(Name = "cleanup-expired-sessions", MaxAttempts = 3)]
[RecurringJob("0 */1 * * *", TimeZone = "UTC", AllowConcurrentRuns = false)]
public sealed class CleanupExpiredSessionsJob : IDurableJob
{
public Task ExecuteAsync(JobContext context, CancellationToken cancellationToken = default)
{
return Task.CompletedTask;
}
}
runtime.registerRecurring(
"cleanup-expired-sessions",
"0 */1 * * *",
"UTC",
async () => {
console.log("Cleaning up expired sessions");
},
{
maxAttempts: 3,
allowConcurrentRuns: false
}
);
Third-party sync job with backoff
[DurableJob(
Name = "sync-third-party-orders",
MaxAttempts = 6,
RetryBehavior = RetryBehavior.Backoff,
RetryInitialDelaySeconds = 15)]
[RecurringJob("*/10 * * * *", TimeZone = "UTC", AllowConcurrentRuns = false)]
public sealed class ThirdPartyOrderSyncJob : IDurableJob
{
public async Task ExecuteAsync(JobContext context, CancellationToken cancellationToken = default)
{
await Task.Delay(TimeSpan.FromMilliseconds(50), cancellationToken);
}
}
runtime.registerRecurring(
"sync-third-party-orders",
"*/10 * * * *",
"UTC",
async () => {
await new Promise((resolve) => setTimeout(resolve, 50));
},
{
maxAttempts: 6,
retryBehavior: "Backoff",
retryInitialDelaySeconds: 15,
allowConcurrentRuns: false
}
);
Guidance
- Use typed jobs when payload shape is part of the contract.
- Prefer non-overlapping recurring jobs for mutable resources.
- Use retry backoff for external dependency instability.