Background Jobs¶
Hangfire-based background job processing.
Overview¶
flowchart TB
subgraph Triggers
A[Cron Schedule]
B[API Enqueue]
end
subgraph Hangfire
C[Job Queue]
D[Job Processor]
end
subgraph Jobs
E[CleanupJob]
F[EmailSendJob]
G[ProcessRecurringTransactionsJob]
H[PendingUsersReminderJob]
I[ExpireUnconfirmedRegistrationsJob]
end
A --> C
B --> C
C --> D
D --> E
D --> F
D --> G
D --> H
D --> I Job Base Class¶
All jobs extend JobBase<TJob>:
public abstract class JobBase<TJob> : IJob where TJob : JobBase<TJob>
{
public string CronExpression =>
_configuration.GetValue<string>($"Hangfire:JobsCron:{GetType().Name}") ?? CronNever;
public abstract Task ExecuteAsync();
public async Task Process(CancellationToken cancellationToken = default)
{
CancellationToken = cancellationToken;
var jobName = GetType().Name;
try
{
Logger.LogInformation("[{JobName}] Starting job execution", jobName);
await ExecuteAsync();
Logger.LogInformation("[{JobName}] Job completed successfully", jobName);
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
Logger.LogWarning("[{JobName}] Job was cancelled", jobName);
throw;
}
catch (Exception ex)
{
Logger.LogError(ex, "[{JobName}] Job failed with error", jobName);
throw;
}
}
}
Jobs¶
CleanupJob¶
Single cleanup job that handles multiple tasks:
| Task | Purpose |
|---|---|
CleanupExpiredSessionsAsync | Delete expired refresh tokens |
EmptyTrashBinAsync | Permanently delete soft-deleted markers after retention period |
CleanupOrphanedFilesAsync | Remove files with ProcessingStatus.Pending that exceeded upload timeout |
Each task runs independently - if one fails, others continue. Failures aggregate into AggregateException.
EmailSendJob¶
Processes email queue from AppTask table. See Email Outbox for details.
- Processes tasks with
TaskType.EmailSendandTaskStatus.Pending - Priority ordering with retry logic
- Exponential backoff on failures
ProcessRecurringTransactionsJob¶
Creates transactions from RecurringTransaction definitions:
- Checks each enabled recurring transaction
- Supports monthly, weekly, and yearly frequencies
- Handles edge cases (last day of month, leap years)
- Tracks
LastProcessedDateto prevent duplicates
PendingUsersReminderJob¶
Notifies admins about users waiting for approval:
- Finds users with
RegistrationStatus.Pendingand confirmed email - Only triggers after
PendingUserNotificationDayshave passed - Sends single email to all admins with list of pending users
- Marks users with
PendingReminderSentAtto prevent repeat notifications
ExpireUnconfirmedRegistrationsJob¶
Handles registration expiry workflow:
- Warning phase — Sends warning email to users approaching expiry with new confirmation link
- Expiry phase — Sets
RegistrationStatus.Declinedfor expired registrations
Uses AppTask to queue expiry emails.
Cron Configuration¶
Schedules are configurable in appsettings.json under Hangfire:JobsCron. Default values:
{
"Hangfire": {
"JobsCron": {
"CleanupJob": "0 23 * * *",
"EmailSendJob": "*/2 * * * *",
"ProcessRecurringTransactionsJob": "5 0 * * *",
"PendingUsersReminderJob": "0 6 * * *",
"ExpireUnconfirmedRegistrationsJob": "0 5 * * *"
}
}
}
Override any entry to change the schedule without code changes. Jobs without cron config default to CronNever (0 0 31 2 * — Feb 31st, never runs).
Job Registration¶
RecurringJob.AddOrUpdate<CleanupJob>(
nameof(CleanupJob),
job => job.Process(CancellationToken.None),
job.CronExpression);
Dashboard¶
Hangfire dashboard available at /hangfire (admin-only).
Access controlled via JWT stored in hangfire_jwt cookie.
Related¶
- Email Outbox — AppTask-based email queue
- Background Jobs — Conceptual overview