Skip to content
bitzorcas
EN

Concept

Background jobs

Background job scheduling — Quartz.NET in a separate JobHost process with Cron-based scheduling, tenant-context injection, and audit retention cleanup.

Last updated

BitzOrcas runs background jobs in a separate JobHost process using Quartz.NET. This keeps the API Host trim-safe and AOT-compatible.

Architecture

┌──────────────────┐ ┌──────────────────┐
│ API Host │ │ JobHost │
│ (AOT, trimmed) │ │ (untrimmed) │
│ │ │ │
│ No Quartz │ │ Quartz.NET │
│ No CAP consumer │ │ SqlSugar audit │
└────────┬─────────┘ └────────┬─────────┘
│ │
└────────────┬───────────┘
SQL Server
(shared DB)

Registered jobs

JobTriggerPurpose
AuditRetentionQuartzJobCron (configurable)Cleanup old audit records per retention policy

Quartz configuration

{
"Audit": {
"Retention": {
"Enabled": true,
"CronExpression": "0 0 2 * * ?",
"MaxAgeDays": 90
}
}
}

JobHost composition

The JobHost is minimal — only what’s needed for scheduled work:

Host.CreateApplicationBuilder(args)
// Clock + Audit pipeline
.AddBitzOrcasAuditDefaults()
.AddBitzOrcasAuditPipeline()
// SqlSugar (if connection configured)
.AddBitzOrcasSqlSugar()
.AddBitzOrcasSqlSugarAuditStore()
// Quartz scheduling
.AddQuartz(configure)
.AddQuartzHostedService()
// OTel + Health Checks
.AddServiceDefaults()

Tenant context for jobs

Background jobs have no HTTP context. For tenant-scoped operations:

await currentUserAccessor.BeginScopeAsync(userId, tenantId, async () =>
{
// This code runs as the specified user/tenant
});

Adding a new job

  1. Create a class implementing IJob in BitzOrcas.JobHost/
  2. Register in Program.cs:
q.AddJob<MyNewJob>(opts => opts.WithIdentity("my-job"));
q.AddTrigger(opts => opts
.ForJob("my-job")
.WithCronSchedule("0 0 */6 * * ?")); // Every 6 hours

See also