-
Notifications
You must be signed in to change notification settings - Fork 166
Add AspireQuartz - Production-Ready Background Job Scheduling for .NET Aspire #1259
Description
AspireQuartz - Production-Ready Background Job Scheduling for .NET Aspire
📋 Overview
.NET Aspire currently lacks a native solution for background job scheduling, forcing developers to manually integrate job scheduling libraries like Quartz.NET or Hangfire. This creates significant friction in cloud-native development as developers must:
- ❌ Manually configure job persistence and database connections
- ❌ Set up observability (tracing, metrics, logging) from scratch
- ❌ Implement idempotency and retry logic themselves
- ❌ Handle health checks and monitoring separately
- ❌ Deal with complex deployment configurations
- ❌ Write 200+ lines of boilerplate code for basic setup
AspireQuartz solves this by providing a production-ready, Aspire-native integration for background job scheduling using Quartz.NET. It follows Aspire's resource model, includes built-in observability, and works seamlessly with Aspire's deployment patterns - making background jobs as easy to use as any other Aspire integration.
🎯 The Problem
Current State: Manual Integration is Complex
Without AspireQuartz, developers must write extensive boilerplate:
// 1. Install 5+ NuGet packages manually
// - Quartz
// - Quartz.Extensions.Hosting
// - Quartz.Serialization.Json
// - Npgsql (or other DB provider)
// - Custom OpenTelemetry instrumentation
// 2. Configure Quartz manually (50+ lines)
builder.Services.AddQuartz(q =>
{
q.SchedulerId = "AUTO";
q.SchedulerName = "MyScheduler";
q.UseMicrosoftDependencyInjectionJobFactory();
// 3. Configure persistence manually (30+ lines)
q.UsePersistentStore(store =>
{
store.UsePostgres(connectionString);
store.UseNewtonsoftJsonSerializer();
store.UseClustering(c =>
{
c.CheckinInterval = TimeSpan.FromSeconds(20);
c.CheckinMisfireThreshold = TimeSpan.FromSeconds(30);
});
});
// 4. Configure thread pool
q.UseDefaultThreadPool(tp => tp.MaxConcurrency = 10);
});
// 5. Add hosted service
builder.Services.AddQuartzHostedService(options =>
{
options.WaitForJobsToComplete = true;
});
// 6. Manually implement idempotency (50+ lines of custom code)
// 7. Manually implement retry policies (30+ lines of custom code)
// 8. Manually add OpenTelemetry instrumentation (20+ lines)
// 9. Manually add health checks (10+ lines)
// 10. Manually handle database migrations (50+ lines)
// Total: 200+ lines of boilerplate codeWith AspireQuartz: One Line
// Single line - everything above is automatic ✅
builder.Services.AddQuartzClient(builder.Configuration.GetConnectionString("quartzdb"));
// Simple, type-safe API
await jobClient.EnqueueAsync<SendEmailJob>(
new { email = "user@example.com" },
new JobOptions
{
IdempotencyKey = "email-123", // ✅ Built-in idempotency
RetryPolicy = RetryPolicy.Exponential(3, TimeSpan.FromSeconds(5)) // ✅ Built-in retry
});✨ What AspireQuartz Provides
🎯 Core Features
1. Aspire-Native Integration
- Follows Aspire resource model and patterns
- Seamless integration with Aspire Dashboard
- Automatic connection string injection
- Works with Aspire deployment tools
2. Production-Ready Features (Out of the Box)
- ✅ Idempotency: Prevent duplicate job execution with
IdempotencyKey - ✅ Retry Policies: Exponential and linear backoff strategies
- ✅ OpenTelemetry: Full distributed tracing and metrics
- ✅ Health Checks: Built-in scheduler health monitoring
- ✅ Database Migrations: Automatic schema creation and updates
3. Multi-Database Support
- PostgreSQL (primary)
- SQL Server
- MySQL
- SQLite
- Automatic migration scripts for all databases
4. Developer Experience
- Type-safe API with generics
- Fluent configuration
- Minimal boilerplate (1 line vs 200+ lines)
- IntelliSense-friendly
5. Observability
- Full OpenTelemetry integration (traces, metrics, logs)
- Compatible with Aspire Dashboard
- Structured logging with correlation IDs
- Performance metrics and job statistics
📦 Package Structure
AspireQuartz follows Aspire conventions with three packages:
1. CommunityToolkit.Aspire.Quartz.Abstractions
Core interfaces and contracts:
public interface IBackgroundJobClient
{
Task<string> EnqueueAsync<TJob>(object? data = null, JobOptions? options = null) where TJob : IJob;
Task<string> ScheduleAsync<TJob>(DateTimeOffset scheduledTime, object? data = null, JobOptions? options = null) where TJob : IJob;
Task<bool> CancelAsync(string jobId);
}
public class JobOptions
{
public string? IdempotencyKey { get; set; }
public RetryPolicy? RetryPolicy { get; set; }
public int Priority { get; set; }
public TimeSpan? Timeout { get; set; }
}
public class RetryPolicy
{
public static RetryPolicy Exponential(int maxRetries, TimeSpan initialDelay);
public static RetryPolicy Linear(int maxRetries, TimeSpan delay);
}2. CommunityToolkit.Aspire.Quartz
Client integration with job scheduling:
// Extension methods
public static IServiceCollection AddQuartzClient(
this IServiceCollection services,
string connectionString,
Action<QuartzClientOptions>? configure = null);
// Features
- BackgroundJobClient implementation
- IdempotencyStore for duplicate prevention
- JobSerializer for type-safe serialization
- OpenTelemetry instrumentation
- Health check integration3. CommunityToolkit.Aspire.Hosting.Quartz
Aspire hosting integration:
// Extension methods
public static IResourceBuilder<QuartzResource> AddQuartz(
this IDistributedApplicationBuilder builder,
string name);
// Features
- QuartzResource for Aspire resource model
- Automatic database configuration
- QuartzMigrationService for schema setup
- Health check endpoints
- OpenTelemetry metrics💻 Usage Examples
🧩 AppHost Configuration
var builder = DistributedApplication.CreateBuilder(args);
// Setup PostgreSQL and database
var postgres = builder
.AddPostgres("postgres")
.AddDatabase("quartzdb");
// Reference database in API service
builder.AddProject<Projects.ApiService>("api")
.WithReference(postgres);
builder.Build().Run();⚙️ API Service Configuration
var builder = WebApplication.CreateBuilder(args);
// Add service defaults (OpenTelemetry, health checks, etc.)
builder.AddServiceDefaults();
// Add Quartz client - single line setup ✅
builder.Services.AddQuartzClient(
builder.Configuration.GetConnectionString("quartzdb"));
var app = builder.Build();
app.MapDefaultEndpoints();
// Enqueue jobs via API
app.MapPost("/jobs/enqueue", async (IBackgroundJobClient jobClient) =>
{
var jobId = await jobClient.EnqueueAsync<SendEmailJob>(
new { email = "user@example.com" },
new JobOptions { IdempotencyKey = "email-123" });
return Results.Ok(new { jobId });
});
app.Run();🧠 Define a Job
using Quartz;
public class SendEmailJob : IJob
{
private readonly ILogger<SendEmailJob> _logger;
private readonly IEmailService _emailService;
public SendEmailJob(ILogger<SendEmailJob> logger, IEmailService emailService)
{
_logger = logger;
_emailService = emailService;
}
public async Task Execute(IJobExecutionContext context)
{
var email = context.JobDetail.JobDataMap.GetString("email");
_logger.LogInformation("Sending email to {Email}", email);
await _emailService.SendAsync(email, "Hello from AspireQuartz!");
_logger.LogInformation("Email sent successfully!");
}
}📤 Job Scheduling Patterns
// 1. Enqueue immediately
await jobClient.EnqueueAsync<SendEmailJob>(
new { email = "user@example.com" });
// 2. Enqueue with idempotency (prevents duplicates)
await jobClient.EnqueueAsync<SendEmailJob>(
new { email = "user@example.com" },
new JobOptions { IdempotencyKey = "email-123" });
// 3. Schedule with delay
await jobClient.ScheduleAsync<SendEmailJob>(
TimeSpan.FromMinutes(5),
new { email = "user@example.com" });
// 4. Schedule at specific time
await jobClient.ScheduleAsync<SendEmailJob>(
DateTimeOffset.UtcNow.AddHours(2),
new { email = "user@example.com" });
// 5. Schedule with cron expression
await jobClient.ScheduleAsync<SendEmailJob>(
"0 0 9 * * ?", // Every day at 9 AM
new { email = "user@example.com" });
// 6. Schedule with retry policy
await jobClient.EnqueueAsync<SendEmailJob>(
new { email = "user@example.com" },
new JobOptions
{
RetryPolicy = RetryPolicy.Exponential(3, TimeSpan.FromSeconds(5))
});🎯 Why This Follows Aspire Patterns
AspireQuartz is designed exactly like other Aspire integrations:
Comparison with Existing Integrations
| Integration | Without Aspire | With Aspire Integration |
|---|---|---|
| Redis | 50+ lines of StackExchange.Redis configuration | builder.AddRedis("cache") |
| PostgreSQL | 30+ lines of Npgsql configuration | builder.AddPostgres("db") |
| RabbitMQ | 100+ lines of RabbitMQ.Client configuration | builder.AddRabbitMQ("messaging") |
| Quartz | 200+ lines of Quartz.NET configuration | builder.AddQuartzClient("quartzdb") ✅ |
What All Aspire Integrations Provide
- ✅ Automatic Configuration: Connection strings, settings, etc.
- ✅ OpenTelemetry Integration: Traces, metrics, logs
- ✅ Health Checks: Built-in health monitoring
- ✅ Resource Model: Follows Aspire patterns
- ✅ Developer Experience: Simple, consistent API
AspireQuartz provides all of these!
📊 Current Status
✅ Production Ready
- Published on NuGet: AspireQuartz v1.0.1
- GitHub Repository: aspire-hosting-quartz
- Active Usage: Real-world production deployments
- Comprehensive Documentation: Getting started guides, API docs, examples
- Multi-Targeting: .NET 8.0, 9.0, 10.0
✅ Quality Metrics
- 10 unit tests (all passing)
- Full XML documentation on all public APIs
- Example application with 4 projects
- Follows C# coding conventions
- Zero build warnings
✅ Features Implemented
- Core job scheduling (enqueue, schedule, cancel)
- Idempotency support
- Retry policies (exponential, linear)
- OpenTelemetry integration (traces, metrics, logs)
- Health checks
- Database migrations (PostgreSQL, SQL Server)
- Multi-database support
- Type-safe API with generics
- Aspire resource model integration
🚀 Future Enhancements (Roadmap)
Phase 1: Enhanced Observability
- Aspire Dashboard integration (live job monitoring)
- Job execution metrics and statistics
- Performance analytics and insights
Phase 2: Advanced Features
- Job chaining and workflows
- Job priorities and SLA management
- Notifications and webhooks (email, Slack, Teams)
Phase 3: Enterprise Features
- Multi-region distributed execution
- Job versioning and blue-green deployments
- Security and authorization (RBAC, multi-tenancy)
Phase 4: Developer Tools
- Job testing and debugging tools
- Job replay for debugging
- Plugin system for extensibility
📚 Resources
- NuGet Package: https://www.nuget.org/packages/AspireQuartz
- GitHub Repository: https://github.com/alnuaimicoder/aspire-hosting-quartz
- Getting Started Guide: GETTING_STARTED.md
- Sample Applications: samples/
- Architecture Deep Dive: ARCHITECTURE_DEEP_DIVE.md