diff --git a/source/ProcessManager.Core/Application/Registration/IOrchestrationRegister.cs b/source/ProcessManager.Core/Application/Registration/IOrchestrationRegister.cs index fa99dfaa..2bb86965 100644 --- a/source/ProcessManager.Core/Application/Registration/IOrchestrationRegister.cs +++ b/source/ProcessManager.Core/Application/Registration/IOrchestrationRegister.cs @@ -24,14 +24,27 @@ internal interface IOrchestrationRegister Task> GetAllByHostNameAsync(string hostName); /// - /// Durable Functions orchestration host's can use this method to register the orchestrations + /// Determine if is unknown to the register and needs to be registered; + /// or if it was previously disabled and needs to be enabled; + /// or if any refreshable property has changed. + /// + /// Orchestration description as described in the register. + /// Orchestration description as described by the application host. + /// if the orchestration description should be registered or updated; otherwise . + bool ShouldRegisterOrUpdate(OrchestrationDescription? registerDescription, OrchestrationDescription hostDescription); + + /// + /// Durable Functions orchestration host's can use this method to register or update the orchestrations /// they host. /// - Task RegisterAsync(OrchestrationDescription orchestrationDescription, string hostName); + /// Orchestration description as described by the application host. + /// Name of the application host. + Task RegisterOrUpdateAsync(OrchestrationDescription hostDescription, string hostName); /// /// Durable Functions orchestration host's can use this method to disable orchestrations they don't host anymore /// or want to disable for other reasons. /// - Task DeregisterAsync(OrchestrationDescription orchestrationDescription); + /// Orchestration description as described in the register. + Task DeregisterAsync(OrchestrationDescription registerDescription); } diff --git a/source/ProcessManager.Core/Application/Registration/OrchestrationRegisterExtensions.cs b/source/ProcessManager.Core/Application/Registration/OrchestrationRegisterExtensions.cs index e7e067bd..4e39ba18 100644 --- a/source/ProcessManager.Core/Application/Registration/OrchestrationRegisterExtensions.cs +++ b/source/ProcessManager.Core/Application/Registration/OrchestrationRegisterExtensions.cs @@ -55,8 +55,8 @@ public static async Task SynchronizeAsync( .SingleOrDefault(x => x.UniqueName == hostDescription.UniqueName); - if (registerDescription == null || registerDescription.IsEnabled == false) - await register.RegisterAsync(hostDescription, hostName).ConfigureAwait(false); + if (register.ShouldRegisterOrUpdate(registerDescription, hostDescription)) + await register.RegisterOrUpdateAsync(hostDescription, hostName).ConfigureAwait(false); } } } diff --git a/source/ProcessManager.Core/Domain/OrchestrationDescription/OrchestrationDescription.cs b/source/ProcessManager.Core/Domain/OrchestrationDescription/OrchestrationDescription.cs index 9416b17e..35e69b0a 100644 --- a/source/ProcessManager.Core/Domain/OrchestrationDescription/OrchestrationDescription.cs +++ b/source/ProcessManager.Core/Domain/OrchestrationDescription/OrchestrationDescription.cs @@ -87,7 +87,7 @@ public string RecurringCronExpression { if (value != _recurringCronExpression) { - if (CrontabSchedule.TryParse(value) == null) + if (value != string.Empty && CrontabSchedule.TryParse(value) == null) throw new ArgumentOutOfRangeException($"Invalid cron value '{value}'. See https://github.com/atifaziz/NCrontab/wiki/Crontab-Expression for expected format."); _recurringCronExpression = value; diff --git a/source/ProcessManager.Core/Infrastructure/Registration/OrchestrationRegister.cs b/source/ProcessManager.Core/Infrastructure/Registration/OrchestrationRegister.cs index 80b6f388..e3e5b203 100644 --- a/source/ProcessManager.Core/Infrastructure/Registration/OrchestrationRegister.cs +++ b/source/ProcessManager.Core/Infrastructure/Registration/OrchestrationRegister.cs @@ -61,38 +61,66 @@ public async Task> GetAllByHostNam } /// - public async Task RegisterAsync(OrchestrationDescription orchestrationDescription, string hostName) + public bool ShouldRegisterOrUpdate(OrchestrationDescription? registerDescription, OrchestrationDescription hostDescription) { - ArgumentNullException.ThrowIfNull(orchestrationDescription); + return + registerDescription == null + || registerDescription.IsEnabled == false + || AnyRefreshablePropertyHasChanged(registerDescription, hostDescription); + } + + /// + public async Task RegisterOrUpdateAsync(OrchestrationDescription hostDescription, string hostName) + { + ArgumentNullException.ThrowIfNull(hostDescription); ArgumentException.ThrowIfNullOrWhiteSpace(hostName); - var existing = await GetOrDefaultAsync(orchestrationDescription.UniqueName, isEnabled: null).ConfigureAwait(false); - if (existing == null) + var existingDescription = await GetOrDefaultAsync(hostDescription.UniqueName, isEnabled: null).ConfigureAwait(false); + if (existingDescription == null) { - // Enfore certain values - orchestrationDescription.HostName = hostName; - orchestrationDescription.IsEnabled = true; - _context.Add(orchestrationDescription); + // Enforce certain values + hostDescription.HostName = hostName; + hostDescription.IsEnabled = true; + _context.Add(hostDescription); } else { - existing.IsEnabled = true; + existingDescription.IsEnabled = true; + UpdateRefreshableProperties(existingDescription, hostDescription); } await _context.SaveChangesAsync().ConfigureAwait(false); } /// - public async Task DeregisterAsync(OrchestrationDescription orchestrationDescription) + public async Task DeregisterAsync(OrchestrationDescription registerDescription) { - ArgumentNullException.ThrowIfNull(orchestrationDescription); + ArgumentNullException.ThrowIfNull(registerDescription); - var existing = await GetOrDefaultAsync(orchestrationDescription.UniqueName, isEnabled: true).ConfigureAwait(false); - if (existing == null) + var existingDescription = await GetOrDefaultAsync(registerDescription.UniqueName, isEnabled: true).ConfigureAwait(false); + if (existingDescription == null) throw new InvalidOperationException("Orchestration description has not been registered or is not currently enabled."); - existing.IsEnabled = false; + existingDescription.IsEnabled = false; await _context.SaveChangesAsync().ConfigureAwait(false); } + + private static bool AnyRefreshablePropertyHasChanged( + OrchestrationDescription registerDescription, + OrchestrationDescription hostDescription) + { + return registerDescription.RecurringCronExpression != hostDescription.RecurringCronExpression; + } + + /// + /// Properties that can change the behaviour of the orchestation history should not be allowed to + /// change without bumping the version of the orchestration description. + /// + private static void UpdateRefreshableProperties( + OrchestrationDescription registerDescription, + OrchestrationDescription hostDescription) + { + registerDescription.RecurringCronExpression = hostDescription.RecurringCronExpression; + } } diff --git a/source/ProcessManager.Orchestrations/Program.cs b/source/ProcessManager.Orchestrations/Program.cs index 977bac8c..491995dc 100644 --- a/source/ProcessManager.Orchestrations/Program.cs +++ b/source/ProcessManager.Orchestrations/Program.cs @@ -98,8 +98,10 @@ OrchestrationDescription CreateDescription_Brs_021_ElectricalHeatingCalculation_ canBeScheduled: true, functionName: nameof(Orchestration_Brs_021_ElectricalHeatingCalculation_V1)); - // Runs at 12:00 and 17:00 every day - description.RecurringCronExpression = "0 12,17 * * *"; + // DISABLED for now because ElectricalHeatingJob is currently failing + description.RecurringCronExpression = string.Empty; + ////// Runs at 12:00 and 17:00 every day + ////description.RecurringCronExpression = "0 12,17 * * *"; foreach (var step in Orchestration_Brs_021_ElectricalHeatingCalculation_V1.Steps) {