Added Name to Call, Added individual call disabling and enabling, added Call.Dispose() method, Refactored Controllers and result models, fixed config reload bug

This commit is contained in:
2025-08-31 02:37:08 +02:00
parent bb6f7c31fb
commit 2187a59936
8 changed files with 183 additions and 93 deletions

View File

@@ -8,6 +8,7 @@ public class RunOnceCall : ICall
public ILogger _logger; public ILogger _logger;
public bool IsEnabled { get; set; } public bool IsEnabled { get; set; }
public bool IsExecuting { get; set; } public bool IsExecuting { get; set; }
public string Name { get; set; }
public Worker Worker { get; } public Worker Worker { get; }
public CallConfig CallConfig { get; set; } public CallConfig CallConfig { get; set; }
public DateTime? LastExecution { get; set; } public DateTime? LastExecution { get; set; }
@@ -20,6 +21,7 @@ public class RunOnceCall : ICall
CallConfig = callConfig; CallConfig = callConfig;
IsEnabled = true; IsEnabled = true;
IsExecuting = false; IsExecuting = false;
Name = callConfig.Name;
IndexAsync(); IndexAsync();
} }
@@ -34,6 +36,8 @@ public class RunOnceCall : ICall
IsEnabled = false; IsEnabled = false;
} }
public void Dispose() {}
private async void IndexAsync() private async void IndexAsync()
{ {
try try
@@ -73,6 +77,7 @@ public class IntervalCall : ICall
public ILogger _logger; public ILogger _logger;
public bool IsEnabled { get; set; } public bool IsEnabled { get; set; }
public bool IsExecuting { get; set; } public bool IsExecuting { get; set; }
public string Name { get; set; }
public CallConfig CallConfig { get; set; } public CallConfig CallConfig { get; set; }
public DateTime? LastExecution { get; set; } public DateTime? LastExecution { get; set; }
public DateTime? LastSuccessfulExecution { get; set; } public DateTime? LastSuccessfulExecution { get; set; }
@@ -84,6 +89,7 @@ public class IntervalCall : ICall
CallConfig = callConfig; CallConfig = callConfig;
IsEnabled = true; IsEnabled = true;
IsExecuting = false; IsExecuting = false;
Name = callConfig.Name;
if (callConfig.Interval is null) if (callConfig.Interval is null)
{ {
_logger.LogError("Interval not set for a Call in Worker \"{Name}\"", worker.Name); _logger.LogError("Interval not set for a Call in Worker \"{Name}\"", worker.Name);
@@ -134,6 +140,10 @@ public class IntervalCall : ICall
Timer.Stop(); Timer.Stop();
IsEnabled = false; IsEnabled = false;
} }
public void Dispose()
{
Timer.Dispose();
}
public HealthCheckResult HealthCheck() public HealthCheckResult HealthCheck()
{ {
@@ -160,6 +170,7 @@ 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 string Name { get; set; }
public Worker Worker { get; } public Worker Worker { get; }
public JobKey JobKey { get; } public JobKey JobKey { get; }
public JobDataMap JobDataMap { get; } public JobDataMap JobDataMap { get; }
@@ -177,6 +188,7 @@ public class ScheduleCall : ICall
_logger = logger; _logger = logger;
IsEnabled = false; IsEnabled = false;
IsExecuting = false; IsExecuting = false;
Name = callConfig.Name;
JobKey = new(worker.Name); JobKey = new(worker.Name);
SchedulerFactory = new(); SchedulerFactory = new();
Scheduler = SchedulerFactory.GetScheduler(CancellationToken.None).Result; Scheduler = SchedulerFactory.GetScheduler(CancellationToken.None).Result;
@@ -224,6 +236,10 @@ public class ScheduleCall : ICall
IsEnabled = false; IsEnabled = false;
} }
public void Dispose()
{
Scheduler.DeleteJob(JobKey);
}
private async Task CreateJob() private async Task CreateJob()
{ {
@@ -262,6 +278,7 @@ public class FileUpdateCall : ICall
{ {
public bool IsEnabled { get; set; } public bool IsEnabled { get; set; }
public bool IsExecuting { get; set; } public bool IsExecuting { get; set; }
public string Name { get; set; }
public Worker Worker { get; } public Worker Worker { get; }
public CallConfig CallConfig { get; set; } public CallConfig CallConfig { get; set; }
private ILogger _logger { get; } private ILogger _logger { get; }
@@ -276,6 +293,7 @@ public class FileUpdateCall : ICall
_logger = logger; _logger = logger;
IsEnabled = true; IsEnabled = true;
IsExecuting = false; IsExecuting = false;
Name = callConfig.Name;
if (CallConfig.Path is null) if (CallConfig.Path is null)
{ {
throw new IndexerConfigurationException($"Path not set for a Call in Worker \"{Worker.Name}\""); throw new IndexerConfigurationException($"Path not set for a Call in Worker \"{Worker.Name}\"");
@@ -318,6 +336,10 @@ public class FileUpdateCall : ICall
} }
} }
public void Dispose()
{
_watcher.Dispose();
}
private void OnFileChanged(object sender, FileSystemEventArgs e) private void OnFileChanged(object sender, FileSystemEventArgs e)
{ {

View File

@@ -22,17 +22,17 @@ public class CallsController : ControllerBase
} }
[HttpGet("List")] [HttpGet("List")]
public ActionResult<CallListResults> List(string name) public ActionResult<CallListResults> List(string workerName)
{ {
bool success = true; bool success = true;
List<CallListResult> calls = []; List<CallListResult> calls = [];
var configWorkerSection = _config.GetSection("EmbeddingsearchIndexer:Worker"); var configWorkerSection = _config.GetSection("EmbeddingsearchIndexer:Worker");
_workerCollection.Workers.TryGetValue(name, out Worker? worker); _workerCollection.Workers.TryGetValue(workerName, out Worker? worker);
if (worker is null) if (worker is null)
{ {
success = false; success = false;
_logger.LogError("No worker found under the name {name}.", [name]); _logger.LogError("No worker found under the name {name}.", [workerName]);
HttpContext.RaiseError(new Exception($"No worker found under the name {name}")); HttpContext.RaiseError(new Exception($"No worker found under the name {workerName}"));
} }
else else
{ {
@@ -54,70 +54,67 @@ public class CallsController : ControllerBase
} }
[HttpGet("Enable")] [HttpGet("Enable")]
public ActionResult<WorkerStartResult> Enable(string name) public ActionResult<CallEnableResult> Enable(string workerName, string? callName)
{ {
_workerCollection.Workers.TryGetValue(name, out Worker? worker); _workerCollection.Workers.TryGetValue(workerName, out Worker? worker);
if (worker is null) if (worker is null)
{ {
_logger.LogError("Unable to start calls in worker {name} - no running worker with this name.", [name]); _logger.LogError("Unable to start calls in worker {workerName} - no running worker with this name.", [workerName]);
return new WorkerStartResult { Success = false }; return new CallEnableResult { Success = false };
} }
_logger.LogInformation("Starting calls in worker {name}.", [name]); if (callName is null)
{
_logger.LogInformation("Starting calls in worker {workerName}.", [workerName]);
foreach (ICall call in worker.Calls) foreach (ICall call in worker.Calls)
{ {
call.Start(); call.Start();
} }
_logger.LogInformation("Starting calls in worker {name}.", [name]); _logger.LogInformation("Finished starting calls in worker {workerName}.", [workerName]);
return new WorkerStartResult { Success = true }; }
else
{
_logger.LogCritical(worker.Calls.First().Name);
ICall? call = worker.Calls.Where(x => x.Name == callName).SingleOrDefault();
if (call is null)
{
_logger.LogError("Unable to start call {callName} in worker {workerName} - no call with this name.", [callName, workerName]);
return new CallEnableResult { Success = false };
}
_logger.LogInformation("Starting call {callName} in worker {workerName}.", [callName, workerName]);
call.Start();
}
return new CallEnableResult { Success = true };
} }
[HttpGet("Disable")] [HttpGet("Disable")]
public ActionResult<WorkerStopResult> Disable(string name) public ActionResult<CallDisableResult> Disable(string workerName, string? callName)
{ {
_workerCollection.Workers.TryGetValue(name, out Worker? worker); _workerCollection.Workers.TryGetValue(workerName, out Worker? worker);
if (worker is null) if (worker is null)
{ {
_logger.LogError("Unable to stop calls in worker {name} - no running worker with this name.", [name]); _logger.LogError("Unable to stop calls in worker {name} - no running worker with this name.", [workerName]);
return new WorkerStopResult { Success = false }; return new CallDisableResult { Success = false };
} }
_logger.LogInformation("Stopping calls in worker {name}.", [name]); if (callName is null)
{
_logger.LogInformation("Stopping calls in worker {name}.", [workerName]);
foreach (ICall call in worker.Calls) foreach (ICall call in worker.Calls)
{ {
call.Stop(); call.Stop();
} }
_logger.LogInformation("Stopped calls in worker {name}.", [name]); _logger.LogInformation("Stopped calls in worker {name}.", [workerName]);
return new WorkerStopResult { Success = true }; } else
{
_logger.LogCritical(worker.Calls.First().Name);
ICall? call = worker.Calls.Where(x => x.Name == callName).SingleOrDefault();
if (call is null)
{
_logger.LogError("Unable to start call {callName} in worker {workerName} - no call with this name.", [callName, workerName]);
return new CallDisableResult { Success = false };
} }
_logger.LogInformation("Starting call {callName} in worker {workerName}.", [callName, workerName]);
[HttpGet("Reload")]
public ActionResult<WorkerReloadConfigResult> Reload()
{
try
{
_logger.LogInformation("Reloading configuration");
_configurationRoot.Reload();
_logger.LogInformation("Reloaded configuration");
_logger.LogInformation("Destroying workers");
foreach (KeyValuePair<string, Worker> workerKVPair in _workerCollection.Workers)
{
Worker worker = workerKVPair.Value;
foreach (ICall call in worker.Calls)
{
call.Stop(); call.Stop();
} }
_workerCollection.Workers.Remove(workerKVPair.Key); return new CallDisableResult { Success = true };
_logger.LogInformation("Destroyed worker {workerKVPair.Key}", [workerKVPair.Key]);
} }
_logger.LogInformation("Destroyed workers");
_workerCollection.InitializeWorkers();
return new WorkerReloadConfigResult { Success = true };
}
catch (Exception ex)
{
_logger.LogError("Exception {ex.Message} happened while trying to reload the worker configuration.", [ex.Message]);
HttpContext.RaiseError(ex);
return new WorkerReloadConfigResult { Success = false };
}
}
} }

View File

@@ -0,0 +1,58 @@
using ElmahCore;
using Microsoft.AspNetCore.Mvc;
using Indexer.Models;
namespace Indexer.Controllers;
[ApiController]
[Route("[controller]")]
public class ConfigController : ControllerBase
{
private readonly ILogger<WorkerController> _logger;
private readonly IConfiguration _config;
private readonly IConfigurationRoot _configurationRoot;
private readonly WorkerManager _workerCollection;
public ConfigController(ILogger<WorkerController> logger, IConfiguration config, IConfigurationRoot configurationRoot, WorkerManager workerCollection)
{
_logger = logger;
_config = config;
_configurationRoot = configurationRoot;
_workerCollection = workerCollection;
}
[HttpGet("Reload")]
public ActionResult<ConfigReloadResult> Reload()
{
try
{
_logger.LogInformation("Reloading configuration");
_configurationRoot.Reload();
_logger.LogInformation("Reloaded configuration");
_logger.LogInformation("Destroying workers");
foreach (KeyValuePair<string, Worker> workerKVPair in _workerCollection.Workers)
{
Worker worker = workerKVPair.Value;
foreach (ICall call in worker.Calls)
{
call.Stop();
call.Dispose();
}
worker.Calls.Clear();
_workerCollection.Workers.Remove(workerKVPair.Key);
_logger.LogInformation("Destroyed worker {workerKVPair.Key}", [workerKVPair.Key]);
}
_logger.LogInformation("Destroyed workers");
_workerCollection.InitializeWorkers();
return new ConfigReloadResult { Success = true };
}
catch (Exception ex)
{
_logger.LogError("Exception {ex.Message} happened while trying to reload the worker configuration.", [ex.Message]);
HttpContext.RaiseError(ex);
return new ConfigReloadResult { Success = false };
}
}
}

View File

@@ -4,6 +4,7 @@ namespace Indexer.Models;
public class CallConfig public class CallConfig
{ {
public required string Name { get; set; }
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
@@ -17,6 +18,8 @@ public interface ICall
public HealthCheckResult HealthCheck(); public HealthCheckResult HealthCheck();
public void Start(); public void Start();
public void Stop(); public void Stop();
public void Dispose();
public string Name { get; set; }
public bool IsEnabled { get; set; } public bool IsEnabled { get; set; }
public bool IsExecuting { get; set; } public bool IsExecuting { get; set; }
public CallConfig CallConfig { get; set; } public CallConfig CallConfig { get; set; }

View File

@@ -0,0 +1,39 @@
using System.Text.Json.Serialization;
namespace Indexer.Models;
public class CallListResults
{
[JsonPropertyName("Calls")]
public required List<CallListResult> Calls { get; set; }
[JsonPropertyName("Success")]
public required bool Success { get; set; }
}
public class CallListResult
{
[JsonPropertyName("CallConfig")]
public required CallConfig CallConfig { get; set; }
[JsonPropertyName("IsActive")]
public required bool IsActive { get; set; }
[JsonPropertyName("IsExecuting")]
public required bool IsExecuting { get; set; }
[JsonPropertyName("LastExecution")]
public required DateTime? LastExecution { get; set; }
[JsonPropertyName("LastSuccessfulExecution")]
public required DateTime? LastSuccessfulExecution { get; set; }
[JsonPropertyName("HealthStatus")]
public required string HealthStatus { get; set; }
}
public class CallDisableResult
{
[JsonPropertyName("Success")]
public required bool Success { get; set; }
}
public class CallEnableResult
{
[JsonPropertyName("Success")]
public required bool Success { get; set; }
}

View File

@@ -0,0 +1,9 @@
using System.Text.Json.Serialization;
namespace Indexer.Models;
public class ConfigReloadResult
{
[JsonPropertyName("Success")]
public required bool Success { get; set; }
}

View File

@@ -26,50 +26,8 @@ public class WorkerListResult
public required string HealthStatus { get; set; } public required string HealthStatus { get; set; }
} }
public class CallListResults
{
[JsonPropertyName("Calls")]
public required List<CallListResult> Calls { get; set; }
[JsonPropertyName("Success")]
public required bool Success { get; set; }
}
public class CallListResult
{
[JsonPropertyName("CallConfig")]
public required CallConfig CallConfig { get; set; }
[JsonPropertyName("IsActive")]
public required bool IsActive { get; set; }
[JsonPropertyName("IsExecuting")]
public required bool IsExecuting { get; set; }
[JsonPropertyName("LastExecution")]
public required DateTime? LastExecution { get; set; }
[JsonPropertyName("LastSuccessfulExecution")]
public required DateTime? LastSuccessfulExecution { get; set; }
[JsonPropertyName("HealthStatus")]
public required string HealthStatus { get; set; }
}
public class WorkerStopResult
{
[JsonPropertyName("Success")]
public required bool Success { get; set; }
}
public class WorkerStartResult
{
[JsonPropertyName("Success")]
public required bool Success { get; set; }
}
public class WorkerTriggerUpdateResult public class WorkerTriggerUpdateResult
{ {
[JsonPropertyName("Success")] [JsonPropertyName("Success")]
public required bool Success { get; set; } public required bool Success { get; set; }
} }
public class WorkerReloadConfigResult
{
[JsonPropertyName("Success")]
public required bool Success { get; set; }
}

View File

@@ -16,6 +16,7 @@
"Script": "Scripts/example.py", "Script": "Scripts/example.py",
"Calls": [ "Calls": [
{ {
"Name": "intervalCall",
"Type": "interval", "Type": "interval",
"Interval": 30000 "Interval": 30000
} }
@@ -26,13 +27,16 @@
"Script": "Scripts/example.csx", "Script": "Scripts/example.csx",
"Calls": [ "Calls": [
{ {
"Name": "runonceCall",
"Type": "runonce" "Type": "runonce"
}, },
{ {
"Name": "scheduleCall",
"Type": "schedule", "Type": "schedule",
"Schedule": "0 0/5 * * * ?" "Schedule": "0 0/5 * * * ?"
}, },
{ {
"Name": "fileupdateCall",
"Type": "fileupdate", "Type": "fileupdate",
"Path": "./Scripts/example_content", "Path": "./Scripts/example_content",
"Events": ["Created", "Changed", "Deleted", "Renamed"], "Events": ["Created", "Changed", "Deleted", "Renamed"],