Added scheduler call type using Quartz
This commit is contained in:
@@ -9,6 +9,8 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ElmahCore" Version="2.1.2" />
|
<PackageReference Include="ElmahCore" Version="2.1.2" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.14" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.14" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.8" />
|
||||||
|
<PackageReference Include="Quartz.Extensions.Hosting" Version="3.15.0" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||||
|
|||||||
11
src/Indexer/Models/ActionJob.cs
Normal file
11
src/Indexer/Models/ActionJob.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Indexer.Models;
|
||||||
|
using Quartz;
|
||||||
|
public class ActionJob : IJob
|
||||||
|
{
|
||||||
|
public Task Execute(IJobExecutionContext context)
|
||||||
|
{
|
||||||
|
var action = (Action)context.MergedJobDataMap["action"];
|
||||||
|
action?.Invoke();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -120,6 +120,8 @@ public class IntervalCallbackInfos : ICallbackInfos
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ScheduleCallbackInfos : ICallbackInfos {}
|
||||||
|
|
||||||
public class ManualTriggerCallbackInfos : ICallbackInfos {}
|
public class ManualTriggerCallbackInfos : ICallbackInfos {}
|
||||||
|
|
||||||
public struct ScriptUpdateInfo
|
public struct ScriptUpdateInfo
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||||
using Indexer.Exceptions;
|
using Indexer.Exceptions;
|
||||||
|
using Quartz;
|
||||||
|
using Quartz.Impl;
|
||||||
|
|
||||||
namespace Indexer.Models;
|
namespace Indexer.Models;
|
||||||
|
|
||||||
@@ -69,7 +71,7 @@ public class WorkerCollection
|
|||||||
timer.AutoReset = true;
|
timer.AutoReset = true;
|
||||||
timer.Enabled = true;
|
timer.Enabled = true;
|
||||||
DateTime now = DateTime.Now;
|
DateTime now = DateTime.Now;
|
||||||
IntervalCall call = new(timer, worker.Scriptable, _logger, callConfig)
|
IntervalCall intervallCall = new(timer, worker.Scriptable, _logger, callConfig)
|
||||||
{
|
{
|
||||||
LastExecution = now,
|
LastExecution = now,
|
||||||
LastSuccessfulExecution = now
|
LastSuccessfulExecution = now
|
||||||
@@ -79,29 +81,32 @@ public class WorkerCollection
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
DateTime beforeExecution = DateTime.Now;
|
DateTime beforeExecution = DateTime.Now;
|
||||||
call.IsExecuting = true;
|
intervallCall.IsExecuting = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
worker.Scriptable.Update(new IntervalCallbackInfos() { sender = sender, e = e });
|
worker.Scriptable.Update(new IntervalCallbackInfos() { sender = sender, e = e });
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
call.IsExecuting = false;
|
intervallCall.IsExecuting = false;
|
||||||
call.LastExecution = beforeExecution;
|
intervallCall.LastExecution = beforeExecution;
|
||||||
worker.LastExecution = beforeExecution;
|
worker.LastExecution = beforeExecution;
|
||||||
}
|
}
|
||||||
DateTime afterExecution = DateTime.Now;
|
DateTime afterExecution = DateTime.Now;
|
||||||
UpdateCallAndWorkerTimestamps(call, worker, beforeExecution, afterExecution);
|
UpdateCallAndWorkerTimestamps(intervallCall, worker, beforeExecution, afterExecution);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError("Exception occurred in a Call of Worker \"{name}\": \"{ex}\"", worker.Name, ex.Message);
|
_logger.LogError("Exception occurred in a Call of Worker \"{name}\": \"{ex}\"", worker.Name, ex.Message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
worker.Calls.Add(call);
|
worker.Calls.Add(intervallCall);
|
||||||
break;
|
break;
|
||||||
case "schedule": // TODO implement scheduled tasks using Quartz
|
case "schedule": // TODO implement scheduled tasks using Quartz
|
||||||
throw new NotImplementedException("schedule not implemented yet");
|
ScheduleCall scheduleCall = new(worker, callConfig, _logger);
|
||||||
|
worker.Calls.Add(scheduleCall);
|
||||||
|
break;
|
||||||
|
//throw new NotImplementedException("schedule not implemented yet");
|
||||||
case "fileupdate":
|
case "fileupdate":
|
||||||
if (callConfig.Path is null)
|
if (callConfig.Path is null)
|
||||||
{
|
{
|
||||||
@@ -221,6 +226,7 @@ public class CallConfig
|
|||||||
public required string Type { get; set; }
|
public required string Type { get; set; }
|
||||||
public long? Interval { get; set; } // For Type: Interval
|
public long? Interval { get; set; } // For Type: Interval
|
||||||
public string? Path { get; set; } // For Type: FileSystemWatcher
|
public string? Path { get; set; } // For Type: FileSystemWatcher
|
||||||
|
public string? Schedule { get; set; } // For Type: Schedule
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ICall
|
public interface ICall
|
||||||
@@ -294,23 +300,96 @@ public class ScheduleCall : ICall
|
|||||||
{
|
{
|
||||||
public bool IsEnabled { get; set; }
|
public bool IsEnabled { get; set; }
|
||||||
public bool IsExecuting { get; set; }
|
public bool IsExecuting { get; set; }
|
||||||
|
public Worker Worker { get; }
|
||||||
|
public JobKey JobKey { get; }
|
||||||
|
public JobDataMap JobDataMap { get; }
|
||||||
public CallConfig CallConfig { get; set; }
|
public CallConfig CallConfig { get; set; }
|
||||||
|
private ILogger _logger { get; }
|
||||||
public DateTime? LastExecution { get; set; }
|
public DateTime? LastExecution { get; set; }
|
||||||
public DateTime? LastSuccessfulExecution { get; set; }
|
public DateTime? LastSuccessfulExecution { get; set; }
|
||||||
|
private StdSchedulerFactory SchedulerFactory { get; }
|
||||||
|
private IScheduler Scheduler { get; }
|
||||||
|
|
||||||
public ScheduleCall(CallConfig callConfig)
|
public ScheduleCall(Worker worker, CallConfig callConfig, ILogger logger)
|
||||||
{
|
{
|
||||||
|
Worker = worker;
|
||||||
CallConfig = callConfig;
|
CallConfig = callConfig;
|
||||||
|
_logger = logger;
|
||||||
IsEnabled = true;
|
IsEnabled = true;
|
||||||
IsExecuting = false;
|
IsExecuting = false;
|
||||||
|
JobKey = new(worker.Name);
|
||||||
|
SchedulerFactory = new();
|
||||||
|
Scheduler = SchedulerFactory.GetScheduler(CancellationToken.None).Result;
|
||||||
|
JobDataMap = [];
|
||||||
|
JobDataMap["action"] = () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DateTime beforeExecution = DateTime.Now;
|
||||||
|
IsExecuting = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
worker.Scriptable.Update(new ScheduleCallbackInfos());
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsExecuting = false;
|
||||||
|
LastExecution = beforeExecution;
|
||||||
|
worker.LastExecution = beforeExecution;
|
||||||
|
}
|
||||||
|
DateTime afterExecution = DateTime.Now;
|
||||||
|
WorkerCollection.UpdateCallAndWorkerTimestamps(this, worker, beforeExecution, afterExecution);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError("Exception occurred in a Call of Worker \"{name}\": \"{ex}\"", worker.Name, ex.Message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
CreateJob().Wait();
|
||||||
|
Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
|
if (!IsExecuting)
|
||||||
|
{
|
||||||
|
Scheduler.Start(CancellationToken.None).Wait();
|
||||||
|
IsExecuting = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
|
Scheduler.PauseAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task CreateJob()
|
||||||
|
{
|
||||||
|
if (CallConfig.Schedule is null)
|
||||||
|
{
|
||||||
|
throw new IndexerConfigurationException($"Interval not set for a Call in Worker \"{Worker.Name}\"");
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
await Scheduler.ScheduleJob(
|
||||||
|
JobBuilder.Create<ActionJob>()
|
||||||
|
.WithIdentity(JobKey)
|
||||||
|
.Build(),
|
||||||
|
TriggerBuilder.Create()
|
||||||
|
.ForJob(JobKey)
|
||||||
|
.WithIdentity(Worker.Name + "-trigger")
|
||||||
|
.UsingJobData(JobDataMap)
|
||||||
|
.WithCronSchedule(CallConfig.Schedule)
|
||||||
|
.Build(),
|
||||||
|
CancellationToken.None);
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
_logger.LogCritical("Malformed Quartz Cron expression! Check your configuration and consult the documentation.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public HealthCheckResult HealthCheck()
|
public HealthCheckResult HealthCheck()
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using ElmahCore;
|
|||||||
using ElmahCore.Mvc;
|
using ElmahCore.Mvc;
|
||||||
using ElmahCore.Mvc.Logger;
|
using ElmahCore.Mvc.Logger;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
using Quartz;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
@@ -30,6 +31,8 @@ builder.Services.AddElmah<XmlFileErrorLog>(Options =>
|
|||||||
Options.LogPath = builder.Configuration.GetValue<string>("EmbeddingsearchIndexer:Elmah:LogFolder") ?? "~/logs";
|
Options.LogPath = builder.Configuration.GetValue<string>("EmbeddingsearchIndexer:Elmah:LogFolder") ?? "~/logs";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.Services.AddQuartz();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
List<string>? allowedIps = builder.Configuration.GetSection("EmbeddingsearchIndexer:Elmah:AllowedHosts")
|
List<string>? allowedIps = builder.Configuration.GetSection("EmbeddingsearchIndexer:Elmah:AllowedHosts")
|
||||||
.Get<List<string>>();
|
.Get<List<string>>();
|
||||||
|
|||||||
Reference in New Issue
Block a user