Added job interruption handling

This commit is contained in:
Khwezi Mngoma
2026-06-03 10:40:29 +02:00
parent 4bac14881d
commit a0cf847e51
7 changed files with 71 additions and 31 deletions
@@ -1,12 +0,0 @@
using LiteCharms.Features.Abstractions;
namespace LiteCharms.Features.Quartz.Abstractions;
public interface IJobOrchestrator
{
Task SendAsync<TNotification>(TNotification notification, CancellationToken cancellationToken = default)
where TNotification : IEvent;
Task ScheduleAsync<TNotification>(TNotification notification, string cronExpression, CancellationToken cancellationToken = default)
where TNotification : IEvent;
}
+24 -4
View File
@@ -1,11 +1,10 @@
using LiteCharms.Features.Abstractions;
using LiteCharms.Features.Quartz.Abstractions;
namespace LiteCharms.Features.Quartz;
public sealed class JobOrchestrator(ISchedulerFactory schedulerFactory) : IJobOrchestrator
{
public async Task SendAsync<TNotification>(TNotification notification, CancellationToken cancellationToken = default)
public async ValueTask SendAsync<TNotification>(TNotification notification, CancellationToken cancellationToken = default)
where TNotification : IEvent
{
var chainedJobGroup = "onetime-jobs";
@@ -23,13 +22,13 @@ public sealed class JobOrchestrator(ISchedulerFactory schedulerFactory) : IJobOr
var trigger = global::Quartz.TriggerBuilder.Create()
.WithIdentity(triggerKey)
.StartNow()
.StartNow()
.Build();
await scheduler.ScheduleJob(job, new List<ITrigger> { trigger }.AsReadOnly(), replace: true, cancellationToken);
}
public async Task ScheduleAsync<TNotification>(TNotification notification, string cronExpression, CancellationToken cancellationToken = default)
public async ValueTask ScheduleAsync<TNotification>(TNotification notification, string cronExpression, CancellationToken cancellationToken = default)
where TNotification : IEvent
{
var chainedJobGroup = "scheduled-jobs";
@@ -63,4 +62,25 @@ public sealed class JobOrchestrator(ISchedulerFactory schedulerFactory) : IJobOr
else
await scheduler.ScheduleJob(job, new List<ITrigger> { trigger }.AsReadOnly(), replace: true, cancellationToken);
}
public async ValueTask<bool> InterruptAsync(string eventName, string? correlationId = null, CancellationToken cancellationToken = default)
{
var scheduler = await schedulerFactory.GetScheduler(cancellationToken);
var jobKeyName = string.Empty;
var jobGroup = string.Empty;
if (!string.IsNullOrWhiteSpace(correlationId))
{
jobKeyName = $"{eventName.ToLower(CultureInfo.InvariantCulture)}-{correlationId.ToLower(CultureInfo.InvariantCulture)}";
jobGroup = "onetime-jobs";
}
else
{
jobKeyName = eventName.ToLower(CultureInfo.InvariantCulture);
jobGroup = "scheduled-jobs";
}
return await scheduler.Interrupt(JobKey.Create(jobKeyName, jobGroup), cancellationToken);
}
}
+17 -6
View File
@@ -21,17 +21,28 @@ public sealed class MediatorJob<TNotification>(IMediator mediator) : IJob where
if (notification is null)
{
Trace.WriteLine("Notification could not be JSon converted from data string, job ended");
Trace.WriteLine("Notification could not be Json converted from data string, job ended");
return;
}
using var activity = MediatorTelemetry.Source.StartActivity($"Quartz: {typeof(TNotification).Name}");
using var activity = MediatorTelemetry.Source.StartActivity(typeof(TNotification).Name);
activity?.SetTag("event.correlation_id", notification.CorrelationId);
await mediator.Publish(notification, context.CancellationToken);
try
{
await mediator.Publish(notification, context.CancellationToken);
Trace.WriteLine("Job published");
Trace.WriteLine("Job published successfully");
}
catch (OperationCanceledException) when (context.CancellationToken.IsCancellationRequested)
{
Trace.WriteLine($"Job '{typeof(TNotification).Name}' was gracefully interrupted by the cluster control plane.");
activity?.SetStatus(ActivityStatusCode.Ok);
return;
}
}
}
@@ -12,6 +12,9 @@ public sealed class RetryJobListener : IJobListener
public async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException? jobException, CancellationToken cancellationToken = default)
{
if (context.CancellationToken.IsCancellationRequested)
return;
if (jobException is not null && context.RefireCount < RetryCount)
jobException.RefireImmediately = true;
}