From 948cc900a637a639b71c73dfcbab9503edcab214 Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Wed, 30 Aug 2023 12:38:21 +0200 Subject: [PATCH 01/33] First revision with commented code in implementation regarding settings validation --- .../Legacy/AuditRetention.cs | 3 +- .../ConfigFileSettingsReader.cs | 14 ++-- .../EnvironmentVariableSettingsReader.cs | 21 ++--- .../ISettingsReader.cs | 10 +++ .../NullableRegistryReader.cs | 75 ----------------- .../NullableSettingsReader.cs | 30 ------- .../RegistryReader.cs | 14 ++-- .../SettingsReader.cs | 29 +++++-- .../SettingsReaderExtensions.cs | 39 +++++++++ .../CustomChecks/CheckFreeDiskSpace.cs | 54 ++++++------- ...CheckMinimumStorageRequiredForIngestion.cs | 62 +++++++------- .../CustomChecks/CheckRavenDBIndexLag.cs | 23 ++---- .../Expiration/ExpiredDocumentsCleaner.cs | 3 +- .../ExpiredDocumentsCleanerBundle.cs | 40 ++++----- .../RavenBootstrapper.cs | 36 ++++----- .../RavenDBPersisterSettings.cs | 20 +++++ .../RavenDbPersistence.cs | 4 +- .../RavenDbPersistenceConfiguration.cs | 81 +++++++++++++------ .../SagaAudit/AuditRetentionCustomCheck.cs | 21 ++--- .../API/APIApprovals.cs | 13 ++- .../TestPersistenceImpl.cs | 30 +++---- .../IPersistenceConfiguration.cs | 6 +- src/ServiceControl/Bootstrapper.cs | 5 +- src/ServiceControl/Hosting/HostArguments.cs | 2 +- .../Settings/LoggingSettings.cs | 4 +- .../Infrastructure/Settings/Settings.cs | 62 +++++++------- .../Infrastructure/WebApi/RootController.cs | 4 +- src/ServiceControl/MaintenanceBootstrapper.cs | 5 +- .../PersistenceConfigurationFactory.cs | 40 +-------- .../PersistenceHostBuilderExtensions.cs | 4 +- src/ServiceControl/SetupBootstrapper.cs | 5 +- 31 files changed, 348 insertions(+), 411 deletions(-) create mode 100644 src/ServiceControl.Configuration/ISettingsReader.cs delete mode 100644 src/ServiceControl.Configuration/NullableRegistryReader.cs delete mode 100644 src/ServiceControl.Configuration/NullableSettingsReader.cs create mode 100644 src/ServiceControl.Configuration/SettingsReaderExtensions.cs create mode 100644 src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs diff --git a/src/ServiceControl.AcceptanceTests.RavenDB/Legacy/AuditRetention.cs b/src/ServiceControl.AcceptanceTests.RavenDB/Legacy/AuditRetention.cs index 84979bf317..0297c2ccaa 100644 --- a/src/ServiceControl.AcceptanceTests.RavenDB/Legacy/AuditRetention.cs +++ b/src/ServiceControl.AcceptanceTests.RavenDB/Legacy/AuditRetention.cs @@ -14,7 +14,6 @@ using ServiceBus.Management.Infrastructure.Settings; using ServiceControl.AcceptanceTests; using ServiceControl.AcceptanceTests.TestSupport.EndpointTemplates; - using ServiceControl.Persistence; using ServiceControl.Persistence.RavenDb.SagaAudit; using ServiceControl.SagaAudit; @@ -34,7 +33,7 @@ public async Task //Override the configuration of the check in the container in order to make it run more frequently for testing purposes. CustomizeHostBuilder = hostBuilder => hostBuilder.ConfigureServices((ctx, services) => - services.AddTransient(provider => new AuditRetentionCustomCheck(provider.GetRequiredService(), provider.GetRequiredService(), TimeSpan.FromSeconds(10)))); + services.AddTransient(provider => new AuditRetentionCustomCheck(provider.GetRequiredService(), provider.GetRequiredService(), TimeSpan.FromSeconds(10)))); SingleResult customCheckEventEntry = default; bool sagaAudiDataInMainInstanceIsAvailableForQuery = false; diff --git a/src/ServiceControl.Configuration/ConfigFileSettingsReader.cs b/src/ServiceControl.Configuration/ConfigFileSettingsReader.cs index f0a9da3ad4..3e20432e5b 100644 --- a/src/ServiceControl.Configuration/ConfigFileSettingsReader.cs +++ b/src/ServiceControl.Configuration/ConfigFileSettingsReader.cs @@ -3,16 +3,16 @@ namespace ServiceBus.Management.Infrastructure.Settings using System; using System.Configuration; - static class ConfigFileSettingsReader + class ConfigFileSettingsReader : ISettingsReader { - public static T Read(string name, T defaultValue = default) + public object Read(string name, Type type, object defaultValue = default) { - return Read("ServiceControl", name, defaultValue); + return Read("ServiceControl", name, type, defaultValue); } - public static T Read(string root, string name, T defaultValue = default) + public object Read(string root, string name, Type type, object defaultValue = default) { - if (TryRead(root, name, out var value)) + if (TryRead(root, name, type, out var value)) { return value; } @@ -20,7 +20,7 @@ public static T Read(string root, string name, T defaultValue = default) return defaultValue; } - public static bool TryRead(string root, string name, out T value) + public bool TryRead(string root, string name, Type type, out object value) { var fullKey = $"{root}/{name}"; @@ -28,7 +28,7 @@ public static bool TryRead(string root, string name, out T value) if (appSettingValue != null) { appSettingValue = Environment.ExpandEnvironmentVariables(appSettingValue); // TODO: Just added this to have expansing on appsettings to not have hardcoded "temp" paths which are different for everyone. - value = (T)Convert.ChangeType(appSettingValue, typeof(T)); + value = Convert.ChangeType(appSettingValue, type); return true; } diff --git a/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs b/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs index da1e2b53ab..913b81be29 100644 --- a/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs +++ b/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs @@ -2,16 +2,11 @@ namespace ServiceBus.Management.Infrastructure.Settings { using System; - static class EnvironmentVariableSettingsReader + class EnvironmentVariableSettingsReader : ISettingsReader { - public static T Read(string name, T defaultValue = default) + public object Read(string root, string name, Type type, object defaultValue = default) { - return Read("ServiceControl", name, defaultValue); - } - - public static T Read(string root, string name, T defaultValue = default) - { - if (TryRead(root, name, out var value)) + if (TryRead(root, name, type, out var value)) { return value; } @@ -19,14 +14,14 @@ public static T Read(string root, string name, T defaultValue = default) return defaultValue; } - public static bool TryRead(string root, string name, out T value) + public bool TryRead(string root, string name, Type type, out object value) { - if (TryReadVariable(out value, $"{root}/{name}")) + if (TryReadVariable(type, out value, $"{root}/{name}")) { return true; } // Azure container instance compatibility: - if (TryReadVariable(out value, $"{root}_{name}".Replace('.', '_'))) + if (TryReadVariable(type, out value, $"{root}_{name}".Replace('.', '_'))) { return true; } @@ -35,14 +30,14 @@ public static bool TryRead(string root, string name, out T value) return false; } - static bool TryReadVariable(out T value, string fullKey) + static bool TryReadVariable(Type type, out object value, string fullKey) { var environmentValue = Environment.GetEnvironmentVariable(fullKey); if (environmentValue != null) { environmentValue = Environment.ExpandEnvironmentVariables(environmentValue); - value = (T)Convert.ChangeType(environmentValue, typeof(T)); + value = Convert.ChangeType(environmentValue, type); return true; } diff --git a/src/ServiceControl.Configuration/ISettingsReader.cs b/src/ServiceControl.Configuration/ISettingsReader.cs new file mode 100644 index 0000000000..7f9f2f77c1 --- /dev/null +++ b/src/ServiceControl.Configuration/ISettingsReader.cs @@ -0,0 +1,10 @@ +namespace ServiceBus.Management.Infrastructure.Settings +{ + using System; + + interface ISettingsReader + { + object Read(string root, string name, Type type, object defaultValue = null); + bool TryRead(string root, string name, Type type, out object value); + } +} \ No newline at end of file diff --git a/src/ServiceControl.Configuration/NullableRegistryReader.cs b/src/ServiceControl.Configuration/NullableRegistryReader.cs deleted file mode 100644 index 67583e8888..0000000000 --- a/src/ServiceControl.Configuration/NullableRegistryReader.cs +++ /dev/null @@ -1,75 +0,0 @@ -namespace ServiceBus.Management.Infrastructure.Settings -{ - using System; - using Microsoft.Win32; - using NServiceBus.Logging; - - /// - /// Wrapper to read registry keys. - /// - /// The type of the key to retrieve - class NullableRegistryReader where T : struct - { - /// - /// Attempts to read the key from the registry. - /// - /// The subkey to target. - /// The name of the value to retrieve. This string is not case-sensitive. - /// The value to return if does not exist. - /// - /// The value associated with , with any embedded environment variables left unexpanded, or - /// if is not found. - /// - public static T? Read(string subKey, string name, T? defaultValue) - { - var regPath = @"SOFTWARE\ParticularSoftware\" + subKey.Replace("/", "\\"); - try - { - if (Environment.Is64BitOperatingSystem) - { - var rootKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); - - using (var registryKey = rootKey.OpenSubKey(regPath)) - { - var value = registryKey?.GetValue(name); - - if (value != null) - { - return (T)Convert.ChangeType(value, typeof(T)); - } - } - - rootKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32); - - using (var registryKey = rootKey.OpenSubKey(regPath)) - { - if (registryKey != null) - { - return (T)Convert.ChangeType(registryKey.GetValue(name, defaultValue), typeof(T)); - } - } - } - else - { - var rootKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default); - - using (var registryKey = rootKey.OpenSubKey(regPath)) - { - if (registryKey != null) - { - return (T)Convert.ChangeType(registryKey.GetValue(name, defaultValue), typeof(T)); - } - } - } - } - catch (Exception ex) - { - Logger.Warn($@"We couldn't read the registry to retrieve the {name}, from '{regPath}'.", ex); - } - - return defaultValue; - } - - static readonly ILog Logger = LogManager.GetLogger(typeof(NullableRegistryReader)); - } -} \ No newline at end of file diff --git a/src/ServiceControl.Configuration/NullableSettingsReader.cs b/src/ServiceControl.Configuration/NullableSettingsReader.cs deleted file mode 100644 index 4bd4d0420d..0000000000 --- a/src/ServiceControl.Configuration/NullableSettingsReader.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace ServiceBus.Management.Infrastructure.Settings -{ - using System; - using System.Configuration; - - class NullableSettingsReader where T : struct - { - public static T? Read(string name) - { - return Read("ServiceControl", name, null); - } - - public static T? Read(string root, string name, T? defaultValue) - { - var fullKey = root + "/" + name; - - if (EnvironmentVariableSettingsReader.TryRead(root, name, out var environmentVariable)) - { - return environmentVariable; - } - - if (ConfigurationManager.AppSettings[fullKey] != null) - { - return (T)Convert.ChangeType(ConfigurationManager.AppSettings[fullKey], typeof(T)); - } - - return RegistryReader.Read(root, name, defaultValue); - } - } -} \ No newline at end of file diff --git a/src/ServiceControl.Configuration/RegistryReader.cs b/src/ServiceControl.Configuration/RegistryReader.cs index 1f5d02b7ac..21fdf6c779 100644 --- a/src/ServiceControl.Configuration/RegistryReader.cs +++ b/src/ServiceControl.Configuration/RegistryReader.cs @@ -8,7 +8,7 @@ namespace ServiceBus.Management.Infrastructure.Settings /// Wrapper to read registry keys. /// /// The type of the key to retrieve - class RegistryReader + class RegistryReader : ISettingsReader { /// /// Attempts to read the key from the registry. @@ -20,7 +20,7 @@ class RegistryReader /// The value associated with , with any embedded environment variables left unexpanded, or /// if is not found. /// - public static T Read(string subKey, string name, T defaultValue = default) + public object Read(string subKey, string name, Type type, object defaultValue = default) { var regPath = @"SOFTWARE\ParticularSoftware\" + subKey.Replace("/", "\\"); try @@ -35,7 +35,7 @@ public static T Read(string subKey, string name, T defaultValue = default) if (value != null) { - return (T)Convert.ChangeType(value, typeof(T)); + return Convert.ChangeType(value, type); } } @@ -45,7 +45,7 @@ public static T Read(string subKey, string name, T defaultValue = default) { if (registryKey != null) { - return (T)Convert.ChangeType(registryKey.GetValue(name, defaultValue), typeof(T)); + return Convert.ChangeType(registryKey.GetValue(name, defaultValue), type); } } } @@ -57,7 +57,7 @@ public static T Read(string subKey, string name, T defaultValue = default) { if (registryKey != null) { - return (T)Convert.ChangeType(registryKey.GetValue(name, defaultValue), typeof(T)); + return Convert.ChangeType(registryKey.GetValue(name, defaultValue), type); } } } @@ -71,6 +71,8 @@ public static T Read(string subKey, string name, T defaultValue = default) return defaultValue; } - static readonly ILog Logger = LogManager.GetLogger(typeof(RegistryReader)); + public bool TryRead(string root, string name, Type type, out object value) => throw new NotImplementedException(); + + static readonly ILog Logger = LogManager.GetLogger(typeof(RegistryReader)); } } \ No newline at end of file diff --git a/src/ServiceControl.Configuration/SettingsReader.cs b/src/ServiceControl.Configuration/SettingsReader.cs index b8fa78c8f7..e6310b3b5e 100644 --- a/src/ServiceControl.Configuration/SettingsReader.cs +++ b/src/ServiceControl.Configuration/SettingsReader.cs @@ -1,25 +1,42 @@ namespace ServiceBus.Management.Infrastructure.Settings { - class SettingsReader + using System; + + class SettingsReader { - public static T Read(string name, T defaultValue = default) + public static readonly EnvironmentVariableSettingsReader EnvironmentVariable = new EnvironmentVariableSettingsReader(); + public static readonly ConfigFileSettingsReader ConfigFile = new ConfigFileSettingsReader(); + public static readonly RegistryReader Registry = new RegistryReader(); + + public static T Read(string name, T defaultValue = default) { return Read("ServiceControl", name, defaultValue); } - public static T Read(string root, string name, T defaultValue = default) + public static T Read(string root, string name, T defaultValue = default) { - if (EnvironmentVariableSettingsReader.TryRead(root, name, out var envValue)) + return (T)Read(root, name, typeof(T), defaultValue); + } + + //public static object Read(string name, Type type, object defaultValue = default) + //{ + // return Read("ServiceControl", name, type, defaultValue); + //} + + public static object Read(string root, string name, Type type, object defaultValue = default) + { + if (EnvironmentVariable.TryRead(root, name, type, out var envValue)) { return envValue; } - if (ConfigFileSettingsReader.TryRead(root, name, out var value)) + if (ConfigFile.TryRead(root, name, type, out var value)) { return value; } - return RegistryReader.Read(root, name, defaultValue); + return Registry.Read(root, name, type, defaultValue); } + } } \ No newline at end of file diff --git a/src/ServiceControl.Configuration/SettingsReaderExtensions.cs b/src/ServiceControl.Configuration/SettingsReaderExtensions.cs new file mode 100644 index 0000000000..4be1dee942 --- /dev/null +++ b/src/ServiceControl.Configuration/SettingsReaderExtensions.cs @@ -0,0 +1,39 @@ +namespace ServiceBus.Management.Infrastructure.Settings +{ + using System; + + static class SettingsReaderExtensions + { + public static T Read(this ISettingsReader instance, string name, T defaultValue = default) + { + return (T)instance.Read("ServiceControl", name, typeof(T), defaultValue); + } + + public static T Read(this ISettingsReader instance, string root, string name, T defaultValue = default) + { + if (instance.TryRead(root, name, typeof(T), out var value)) + { + return (T)value; + } + + return defaultValue; + } + + public static bool TryRead(this ISettingsReader instance, string root, string name, out T value) + { + if (instance.TryRead(root, name, typeof(T), out object innerValue)) + { + value = (T)innerValue; + return true; + } + + value = default; + return false; + } + + public static object Read(this ISettingsReader instance, string name, Type type, object defaultValue = null) + { + return instance.Read("ServiceControl", name, type, defaultValue); + } + } +} \ No newline at end of file diff --git a/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckFreeDiskSpace.cs b/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckFreeDiskSpace.cs index da9de3e68d..38216a0fe8 100644 --- a/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckFreeDiskSpace.cs +++ b/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckFreeDiskSpace.cs @@ -5,15 +5,13 @@ using System.Threading.Tasks; using NServiceBus.CustomChecks; using NServiceBus.Logging; - using Persistence; - using Persistence.RavenDb; class CheckFreeDiskSpace : CustomCheck { - public CheckFreeDiskSpace(PersistenceSettings settings) : base("ServiceControl database", "Storage space", TimeSpan.FromMinutes(5)) + public CheckFreeDiskSpace(RavenDBPersisterSettings settings) : base("ServiceControl database", "Storage space", TimeSpan.FromMinutes(5)) { - dataPath = settings.PersisterSpecificSettings[RavenDbPersistenceConfiguration.DbPathKey]; - percentageThreshold = GetDataSpaceRemainingThreshold(settings) / 100m; + dataPath = settings.DatabasePath; + percentageThreshold = settings.DataSpaceRemainingThreshold; Logger.Debug($"Check ServiceControl data drive space remaining custom check starting. Threshold {percentageThreshold:P0}"); } @@ -43,37 +41,37 @@ public override Task PerformCheck() : CheckResult.Failed($"{percentRemaining:P0} disk space remaining on data drive '{dataDriveInfo.VolumeLabel} ({dataDriveInfo.RootDirectory})' on '{Environment.MachineName}'."); } - int GetDataSpaceRemainingThreshold(PersistenceSettings settings) - { - var threshold = DataSpaceRemainingThresholdDefault; + //int GetDataSpaceRemainingThreshold(PersistenceSettings settings) + //{ + // var threshold = DataSpaceRemainingThresholdDefault; - if (settings.PersisterSpecificSettings.TryGetValue(RavenDbPersistenceConfiguration.DataSpaceRemainingThresholdKey, out var thresholdValue)) - { - threshold = int.Parse(thresholdValue); - } - string message; + // if (settings.PersisterSpecificSettings.TryGetValue(RavenDbPersistenceConfiguration.DataSpaceRemainingThresholdKey, out var thresholdValue)) + // { + // threshold = int.Parse(thresholdValue); + // } + // string message; - if (threshold < 0) - { - message = $"{RavenDbPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, minimum value is 0."; - Logger.Fatal(message); - throw new Exception(message); - } + // if (threshold < 0) + // { + // message = $"{RavenDbPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, minimum value is 0."; + // Logger.Fatal(message); + // throw new Exception(message); + // } - if (threshold > 100) - { - message = $"{RavenDbPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, maximum value is 100."; - Logger.Fatal(message); - throw new Exception(message); - } + // if (threshold > 100) + // { + // message = $"{RavenDbPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, maximum value is 100."; + // Logger.Fatal(message); + // throw new Exception(message); + // } - return threshold; - } + // return threshold; + //} readonly string dataPath; readonly decimal percentageThreshold; - const int DataSpaceRemainingThresholdDefault = 20; + //const int DataSpaceRemainingThresholdDefault = 20; static readonly ILog Logger = LogManager.GetLogger(typeof(CheckFreeDiskSpace)); } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs b/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs index c87cf2066b..82f2a9921e 100644 --- a/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs +++ b/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs @@ -10,23 +10,25 @@ class CheckMinimumStorageRequiredForIngestion : CustomCheck { - public CheckMinimumStorageRequiredForIngestion(MinimumRequiredStorageState stateHolder, PersistenceSettings settings) + public CheckMinimumStorageRequiredForIngestion( + MinimumRequiredStorageState stateHolder, + RavenDBPersisterSettings settings) : base("Message Ingestion Process", "ServiceControl Health", TimeSpan.FromSeconds(5)) { this.stateHolder = stateHolder; this.settings = settings; - dataPathRoot = Path.GetPathRoot(settings.PersisterSpecificSettings[RavenDbPersistenceConfiguration.DbPathKey]); + dataPathRoot = Path.GetPathRoot(settings.DatabasePath); } public override Task PerformCheck() { - percentageThreshold = GetMinimumStorageLeftRequiredForIngestion(settings) / 100m; + percentageThreshold = settings.MinimumStorageLeftRequiredForIngestion / 100m; if (dataPathRoot == null) { stateHolder.CanIngestMore = true; - return successResult; + return SuccessResult; } Logger.Debug($"Check ServiceControl data drive space starting. Threshold {percentageThreshold:P0}"); @@ -45,7 +47,7 @@ public override Task PerformCheck() if (percentRemaining > percentageThreshold) { stateHolder.CanIngestMore = true; - return successResult; + return SuccessResult; } var message = $"Error message ingestion stopped! {percentRemaining:P0} disk space remaining on data drive '{dataDriveInfo.VolumeLabel} ({dataDriveInfo.RootDirectory})' on '{Environment.MachineName}'. This is less than {percentageThreshold}% - the minimal required space configured. The threshold can be set using the {RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey} configuration setting."; @@ -54,42 +56,42 @@ public override Task PerformCheck() return CheckResult.Failed(message); } - int GetMinimumStorageLeftRequiredForIngestion(PersistenceSettings settings) - { - int threshold = MinimumStorageLeftRequiredForIngestionDefault; + //int GetMinimumStorageLeftRequiredForIngestion(PersistenceSettings settings) + //{ + // int threshold = MinimumStorageLeftRequiredForIngestionDefault; - if (settings.PersisterSpecificSettings.TryGetValue(RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey, out var thresholdValue)) - { - threshold = int.Parse(thresholdValue); - } + // if (settings.PersisterSpecificSettings.TryGetValue(RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey, out var thresholdValue)) + // { + // threshold = int.Parse(thresholdValue); + // } - string message; - if (threshold < 0) - { - message = $"{RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey} is invalid, minimum value is 0."; - Logger.Fatal(message); - throw new Exception(message); - } + // string message; + // if (threshold < 0) + // { + // message = $"{RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey} is invalid, minimum value is 0."; + // Logger.Fatal(message); + // throw new Exception(message); + // } - if (threshold > 100) - { - message = $"{RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey} is invalid, maximum value is 100."; - Logger.Fatal(message); - throw new Exception(message); - } + // if (threshold > 100) + // { + // message = $"{RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey} is invalid, maximum value is 100."; + // Logger.Fatal(message); + // throw new Exception(message); + // } - return threshold; - } + // return threshold; + //} - const int MinimumStorageLeftRequiredForIngestionDefault = 5; + //const int MinimumStorageLeftRequiredForIngestionDefault = 5; readonly MinimumRequiredStorageState stateHolder; - readonly PersistenceSettings settings; + readonly RavenDBPersisterSettings settings; readonly string dataPathRoot; decimal percentageThreshold; - static Task successResult = Task.FromResult(CheckResult.Pass); + static readonly Task SuccessResult = Task.FromResult(CheckResult.Pass); static readonly ILog Logger = LogManager.GetLogger(typeof(CheckMinimumStorageRequiredForIngestion)); } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckRavenDBIndexLag.cs b/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckRavenDBIndexLag.cs index ebec2d73e6..f138e0efa0 100644 --- a/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckRavenDBIndexLag.cs +++ b/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckRavenDBIndexLag.cs @@ -6,20 +6,16 @@ using System.Threading.Tasks; using NServiceBus.CustomChecks; using NServiceBus.Logging; - using Persistence; - using Persistence.RavenDb; using Raven.Client; using Raven.Abstractions.Data; - using Raven.Abstractions.Extensions; using CustomCheck = NServiceBus.CustomChecks.CustomCheck; class CheckRavenDBIndexLag : CustomCheck { - public CheckRavenDBIndexLag(IDocumentStore store, PersistenceSettings settings) + public CheckRavenDBIndexLag(IDocumentStore store) : base("Error Database Index Lag", "ServiceControl Health", TimeSpan.FromMinutes(5)) { this.store = store; - this.settings = settings; } public override Task PerformCheck() @@ -33,9 +29,7 @@ public override Task PerformCheck() if (indexCountWithTooMuchLag > 0) { - var logPath = settings.PersisterSpecificSettings.GetOrDefault(RavenDbPersistenceConfiguration.LogPathKey); - - return CheckResult.Failed($"At least one index significantly stale. Please run maintenance mode if this custom check persists to ensure index(es) can recover. See log file in `{logPath}` for more details. Visit https://docs.particular.net/search?q=servicecontrol+troubleshooting for more information."); + return CheckResult.Failed($"At least one index significantly stale. Please run maintenance mode if this custom check persists to ensure index(es) can recover. See log file for more details. Visit https://docs.particular.net/search?q=servicecontrol+troubleshooting for more information."); } return CheckResult.Pass; @@ -54,12 +48,12 @@ static int CheckAndReportIndexesWithTooMuchIndexLag(IndexStats[] indexes) if (indexLag > IndexLagThresholdError) { indexCountWithTooMuchLag++; - _log.Error($"Index [{indexStats.Name}] IndexingLag {indexLag:n0} is above error threshold ({IndexLagThresholdError:n0}). Launch in maintenance mode to let indexes catch up."); + Log.Error($"Index [{indexStats.Name}] IndexingLag {indexLag:n0} is above error threshold ({IndexLagThresholdError:n0}). Launch in maintenance mode to let indexes catch up."); } else if (indexLag > IndexLagThresholdWarning) { indexCountWithTooMuchLag++; - _log.Warn($"Index [{indexStats.Name}] IndexingLag {indexLag:n0} is above warning threshold ({IndexLagThresholdWarning:n0}). Launch in maintenance mode to let indexes catch up."); + Log.Warn($"Index [{indexStats.Name}] IndexingLag {indexLag:n0} is above warning threshold ({IndexLagThresholdWarning:n0}). Launch in maintenance mode to let indexes catch up."); } } @@ -68,7 +62,7 @@ static int CheckAndReportIndexesWithTooMuchIndexLag(IndexStats[] indexes) static void CreateDiagnosticsLogEntry(DatabaseStatistics statistics, IndexStats[] indexes) { - if (!_log.IsDebugEnabled) + if (!Log.IsDebugEnabled) { return; } @@ -83,14 +77,13 @@ static void CreateDiagnosticsLogEntry(DatabaseStatistics statistics, IndexStats[ indexLag = Math.Abs(indexLag); report.AppendLine($"- Index [{indexStats.Name,-44}] Stale: {statistics.StaleIndexes.Contains(indexStats.Name),-5}, Lag: {indexLag,9:n0}, Valid: {indexStats.IsInvalidIndex,-5}, LastIndexed: {indexStats.LastIndexedTimestamp:u}, LastIndexing: {indexStats.LastIndexingTime:u}"); } - _log.Debug(report.ToString()); + Log.Debug(report.ToString()); } const int IndexLagThresholdWarning = 10000; const int IndexLagThresholdError = 100000; - static ILog _log = LogManager.GetLogger(); + static readonly ILog Log = LogManager.GetLogger(); - IDocumentStore store; - PersistenceSettings settings; + readonly IDocumentStore store; } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleaner.cs b/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleaner.cs index ea0b50f3e9..ec610a1bc1 100644 --- a/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleaner.cs +++ b/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleaner.cs @@ -8,11 +8,10 @@ using Raven.Abstractions; using Raven.Database; using SagaAudit; - using ServiceControl.Persistence; class ExpiredDocumentsCleaner { - public static Task RunCleanup(int deletionBatchSize, DocumentDatabase database, PersistenceSettings settings, CancellationToken cancellationToken = default) + public static Task RunCleanup(int deletionBatchSize, DocumentDatabase database, RavenDBPersisterSettings settings, CancellationToken cancellationToken = default) { var threshold = SystemTime.UtcNow.Add(settings.ErrorRetentionPeriod); diff --git a/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs b/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs index cb0ccb016a..a2f78d3495 100644 --- a/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs +++ b/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs @@ -31,11 +31,11 @@ public void Dispose() var finishedTask = composite.GetAwaiter().GetResult(); if (finishedTask == delayTask) { - logger.Error("Cleanup process did not finish on time. Forcing shutdown."); + Logger.Error("Cleanup process did not finish on time. Forcing shutdown."); } else { - logger.Info("Expired documents cleanup process stopped."); + Logger.Info("Expired documents cleanup process stopped."); } timer = null; @@ -54,41 +54,37 @@ public void Execute(DocumentDatabase database) var due = TimeSpan.FromSeconds(ExpirationProcessTimerInSeconds); var deletionBatchSize = ExpirationProcessBatchSize; - logger.Info($"Running deletion of expired documents every {ExpirationProcessTimerInSeconds} seconds"); - logger.Info($"Deletion batch size set to {deletionBatchSize}"); - logger.Info($"Retention period for errors is {persistenceSettings.ErrorRetentionPeriod}"); + Logger.Info($"Running deletion of expired documents every {ExpirationProcessTimerInSeconds} seconds"); + Logger.Info($"Deletion batch size set to {deletionBatchSize}"); + Logger.Info($"Retention period for errors is {persistenceSettings.ErrorRetentionPeriod}"); var auditRetention = persistenceSettings.AuditRetentionPeriod; if (auditRetention.HasValue) { - logger.InfoFormat("Retention period for audits and saga history is {0}", persistenceSettings.AuditRetentionPeriod); + Logger.InfoFormat("Retention period for audits and saga history is {0}", persistenceSettings.AuditRetentionPeriod); } timer = new TimerJob( - token => ExpiredDocumentsCleaner.RunCleanup(deletionBatchSize, database, persistenceSettings, token), due, due, e => { logger.Error("Error when trying to find expired documents", e); }); + token => ExpiredDocumentsCleaner.RunCleanup(deletionBatchSize, database, persistenceSettings, token), due, due, e => { Logger.Error("Error when trying to find expired documents", e); }); } int ExpirationProcessTimerInSeconds { get { - var expirationProcessTimerInSeconds = ExpirationProcessTimerInSecondsDefault; - - if (RavenBootstrapper.Settings.PersisterSpecificSettings.TryGetValue(RavenBootstrapper.ExpirationProcessTimerInSecondsKey, out var expirationProcessTimerInSecondsString)) - { - expirationProcessTimerInSeconds = int.Parse(expirationProcessTimerInSecondsString); - } + //var expirationProcessTimerInSeconds = ExpirationProcessTimerInSecondsDefault; + var expirationProcessTimerInSeconds = settings.ExpirationProcessTimerInSeconds; if (expirationProcessTimerInSeconds < 0) { - logger.Error($"ExpirationProcessTimerInSeconds cannot be negative. Defaulting to {ExpirationProcessTimerInSecondsDefault}"); + Logger.Error($"ExpirationProcessTimerInSeconds cannot be negative. Defaulting to {ExpirationProcessTimerInSecondsDefault}"); return ExpirationProcessTimerInSecondsDefault; } if (expirationProcessTimerInSeconds > TimeSpan.FromHours(3).TotalSeconds) { - logger.Error($"ExpirationProcessTimerInSeconds cannot be larger than {TimeSpan.FromHours(3).TotalSeconds}. Defaulting to {ExpirationProcessTimerInSecondsDefault}"); + Logger.Error($"ExpirationProcessTimerInSeconds cannot be larger than {TimeSpan.FromHours(3).TotalSeconds}. Defaulting to {ExpirationProcessTimerInSecondsDefault}"); return ExpirationProcessTimerInSecondsDefault; } @@ -100,21 +96,18 @@ public int ExpirationProcessBatchSize { get { - var expirationProcessBatchSize = ExpirationProcessBatchSizeDefault; + //var expirationProcessBatchSize = ExpirationProcessBatchSizeDefault; + var expirationProcessBatchSize = settings.ExpirationProcessBatchSize; - if (RavenBootstrapper.Settings.PersisterSpecificSettings.TryGetValue(RavenBootstrapper.ExpirationProcessBatchSizeKey, out var expirationProcessBatchSizeString)) - { - expirationProcessBatchSize = int.Parse(expirationProcessBatchSizeString); - } if (expirationProcessBatchSize < 1) { - logger.Error($"ExpirationProcessBatchSize cannot be less than 1. Defaulting to {ExpirationProcessBatchSizeDefault}"); + Logger.Error($"ExpirationProcessBatchSize cannot be less than 1. Defaulting to {ExpirationProcessBatchSizeDefault}"); return ExpirationProcessBatchSizeDefault; } if (expirationProcessBatchSize < ExpirationProcessBatchSizeMinimum) { - logger.Error($"ExpirationProcessBatchSize cannot be less than {ExpirationProcessBatchSizeMinimum}. Defaulting to {ExpirationProcessBatchSizeDefault}"); + Logger.Error($"ExpirationProcessBatchSize cannot be less than {ExpirationProcessBatchSizeMinimum}. Defaulting to {ExpirationProcessBatchSizeDefault}"); return ExpirationProcessBatchSizeDefault; } @@ -126,8 +119,9 @@ public int ExpirationProcessBatchSize const int ExpirationProcessBatchSizeDefault = 65512; const int ExpirationProcessBatchSizeMinimum = 10240; + readonly RavenDBPersisterSettings settings = RavenBootstrapper.Settings; - ILog logger = LogManager.GetLogger(typeof(ExpiredDocumentsCleanerBundle)); + static readonly ILog Logger = LogManager.GetLogger(typeof(ExpiredDocumentsCleanerBundle)); TimerJob timer; } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence.RavenDb/RavenBootstrapper.cs b/src/ServiceControl.Persistence.RavenDb/RavenBootstrapper.cs index ea8058d164..873fa29755 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenBootstrapper.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenBootstrapper.cs @@ -24,17 +24,13 @@ static class RavenBootstrapper public const string MinimumStorageLeftRequiredForIngestionKey = "MinimumStorageLeftRequiredForIngestion"; - public static PersistenceSettings Settings { get; private set; } + public static RavenDBPersisterSettings Settings { get; private set; } - public static void Configure(EmbeddableDocumentStore documentStore, PersistenceSettings settings) + public static void Configure(EmbeddableDocumentStore documentStore, RavenDBPersisterSettings settings) { Settings = settings; - var runInMemory = false; - if (settings.PersisterSpecificSettings.TryGetValue(RunInMemoryKey, out var runInMemoryString)) - { - runInMemory = bool.Parse(runInMemoryString); - } + var runInMemory = settings.RunInMemory; if (runInMemory) { @@ -42,7 +38,9 @@ public static void Configure(EmbeddableDocumentStore documentStore, PersistenceS } else { - if (!settings.PersisterSpecificSettings.TryGetValue(DatabasePathKey, out string dbPath)) + var dbPath = settings.DatabasePath; + + if (string.IsNullOrEmpty(dbPath)) { throw new InvalidOperationException($"{DatabasePathKey} is mandatory"); } @@ -54,11 +52,7 @@ public static void Configure(EmbeddableDocumentStore documentStore, PersistenceS documentStore.Listeners.RegisterListener(new SubscriptionsLegacyAddressConverter()); } - var exposeRavenDB = false; - if (settings.PersisterSpecificSettings.TryGetValue(ExposeRavenDBKey, out var exposeRavenDBString)) - { - exposeRavenDB = bool.Parse(exposeRavenDBString); - } + var exposeRavenDB = settings.ExposeRavenDB; documentStore.UseEmbeddedHttpServer = settings.MaintenanceMode || exposeRavenDB; documentStore.EnlistInDistributedTransactions = false; @@ -79,11 +73,7 @@ public static void Configure(EmbeddableDocumentStore documentStore, PersistenceS documentStore.Configuration.Settings["Raven/AnonymousAccess"] = "Admin"; documentStore.Configuration.Settings["Raven/Licensing/AllowAdminAnonymousAccessForCommercialUse"] = "true"; - var runCleanupBundle = true; - if (settings.PersisterSpecificSettings.TryGetValue(RunCleanupBundleKey, out var runCleanupBundleString)) - { - runCleanupBundle = bool.Parse(runCleanupBundleString); - } + var runCleanupBundle = settings.RunCleanupBundle; if (runCleanupBundle) { @@ -93,18 +83,20 @@ public static void Configure(EmbeddableDocumentStore documentStore, PersistenceS documentStore.Configuration.DisableClusterDiscovery = true; documentStore.Configuration.ResetIndexOnUncleanShutdown = true; - if (!settings.PersisterSpecificSettings.TryGetValue(DatabaseMaintenancePortKey, out var databaseMaintenancePort)) + if (settings.DatabaseMaintenancePort == 0) { throw new Exception($"{DatabaseMaintenancePortKey} is mandatory."); } - documentStore.Configuration.Port = int.Parse(databaseMaintenancePort); + documentStore.Configuration.Port = settings.DatabaseMaintenancePort; - if (!settings.PersisterSpecificSettings.TryGetValue(HostNameKey, out var hostName)) + if (string.IsNullOrEmpty(settings.HostName)) { throw new Exception($"{HostNameKey} is mandatory."); } + var hostName = settings.HostName; + documentStore.Configuration.HostName = hostName == "*" || hostName == "+" ? "localhost" : hostName; @@ -152,7 +144,7 @@ static string ReadAllTextWithoutLocking(string path) } } - static SerializationBinder MigratedTypeAwareBinder = new MigratedTypeAwareBinder(); + static readonly SerializationBinder MigratedTypeAwareBinder = new MigratedTypeAwareBinder(); static readonly ILog Logger = LogManager.GetLogger(typeof(RavenBootstrapper)); diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs b/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs new file mode 100644 index 0000000000..a6c2000400 --- /dev/null +++ b/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs @@ -0,0 +1,20 @@ +using System; + +class RavenDBPersisterSettings +{ + public string DatabasePath { get; set; } + public string HostName { get; set; } = "localhost"; + public int DatabaseMaintenancePort { get; set; } = 55554; + public bool ExposeRavenDB { get; set; } + public int ExpirationProcessTimerInSeconds { get; set; } + public int ExpirationProcessBatchSize { get; set; } + public bool RunCleanupBundle { get; set; } + public bool RunInMemory { get; set; } + public int MinimumStorageLeftRequiredForIngestion { get; set; } + public int DataSpaceRemainingThreshold { get; set; } + public TimeSpan ErrorRetentionPeriod { get; set; } + public TimeSpan EventsRetentionPeriod { get; set; } + public TimeSpan? AuditRetentionPeriod { get; set; } + public int ExternalIntegrationsDispatchingBatchSize { get; set; } = 100; + public bool MaintenanceMode { get; set; } +} \ No newline at end of file diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs index 8be713ddf6..a07485b113 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs @@ -19,7 +19,7 @@ class RavenDbPersistence : IPersistence { - public RavenDbPersistence(PersistenceSettings settings, EmbeddableDocumentStore documentStore, RavenStartup ravenStartup) + public RavenDbPersistence(RavenDBPersisterSettings settings, EmbeddableDocumentStore documentStore, RavenStartup ravenStartup) { this.settings = settings; this.documentStore = documentStore; @@ -84,7 +84,7 @@ public IPersistenceInstaller CreateInstaller() } readonly RavenStartup ravenStartup; - readonly PersistenceSettings settings; + readonly RavenDBPersisterSettings settings; readonly EmbeddableDocumentStore documentStore; } } diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs index c457809fa3..ea18824d4f 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs @@ -1,34 +1,63 @@ namespace ServiceControl.Persistence.RavenDb { - using System.Collections.Generic; + using System; using Raven.Client.Embedded; class RavenDbPersistenceConfiguration : IPersistenceConfiguration { - //TODO: figure out what can be strongly typed - public const string LogPathKey = "LogPath"; - public const string DbPathKey = "DbPath"; - public const string DataSpaceRemainingThresholdKey = "DataSpaceRemainingThreshold"; - public const string MinimumStorageLeftRequiredForIngestionKey = "MinimumStorageLeftRequiredForIngestion"; - public const string AuditRetentionPeriodKey = "AuditRetentionPeriod"; - - - public string Name => "RavenDB35"; - - public IEnumerable ConfigurationKeys => new[]{ - RavenBootstrapper.DatabasePathKey, - RavenBootstrapper.HostNameKey, - RavenBootstrapper.DatabaseMaintenancePortKey, - RavenBootstrapper.ExposeRavenDBKey, - RavenBootstrapper.ExpirationProcessTimerInSecondsKey, - RavenBootstrapper.ExpirationProcessBatchSizeKey, - RavenBootstrapper.RunCleanupBundleKey, - RavenBootstrapper.RunInMemoryKey, - RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey, - DataSpaceRemainingThresholdKey - }; - - public IPersistence Create(PersistenceSettings settings) + const string DataSpaceRemainingThresholdKey = "DataSpaceRemainingThreshold"; + const string AuditRetentionPeriodKey = "AuditRetentionPeriod"; + const string ErrorRetentionPeriodKey = "ErrorRetentionPeriod"; + const string EventsRetentionPeriodKey = "EventsRetentionPeriod"; + const string ExternalIntegrationsDispatchingBatchSizeKey = "ExternalIntegrationsDispatchingBatchSize"; + const string MaintenanceModeKey = "MaintenanceMode"; + + public IPersistence Create(Func readSetting) + { + T GetSetting(string key) => (T)readSetting(key, typeof(T)); + + + //TODO: In core previously this happened with the settings: + + // foreach (var keyPair in settings.PersisterSpecificSettings) + // { + // persistenceSettings.PersisterSpecificSettings[keyPair.Key] = keyPair.Value; + // } + + // foreach (var key in persistenceConfiguration.ConfigurationKeys) + // { + // var value = SettingsReader.Read("ServiceControl", key, null); + // if (!string.IsNullOrWhiteSpace(value)) + // { + // persistenceSettings.PersisterSpecificSettings[key] = value; + // } + // } + + + + var settings = new RavenDBPersisterSettings() + { + DatabasePath = GetSetting(RavenBootstrapper.DatabasePathKey), + HostName = GetSetting(RavenBootstrapper.HostNameKey), + DatabaseMaintenancePort = GetSetting(RavenBootstrapper.DatabaseMaintenancePortKey), + ExposeRavenDB = GetSetting(RavenBootstrapper.ExposeRavenDBKey), + ExpirationProcessTimerInSeconds = GetSetting(RavenBootstrapper.ExpirationProcessTimerInSecondsKey), + ExpirationProcessBatchSize = GetSetting(RavenBootstrapper.ExpirationProcessBatchSizeKey), + RunCleanupBundle = GetSetting(RavenBootstrapper.RunCleanupBundleKey), + RunInMemory = GetSetting(RavenBootstrapper.RunInMemoryKey), + MinimumStorageLeftRequiredForIngestion = GetSetting(RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey), + DataSpaceRemainingThreshold = GetSetting(DataSpaceRemainingThresholdKey), + ErrorRetentionPeriod = GetSetting(ErrorRetentionPeriodKey), + EventsRetentionPeriod = GetSetting(EventsRetentionPeriodKey), + AuditRetentionPeriod = GetSetting(AuditRetentionPeriodKey), + ExternalIntegrationsDispatchingBatchSize = GetSetting(ExternalIntegrationsDispatchingBatchSizeKey), + MaintenanceMode = GetSetting(MaintenanceModeKey), + }; + + return Create(settings); + } + + internal IPersistence Create(RavenDBPersisterSettings settings) { var documentStore = new EmbeddableDocumentStore(); RavenBootstrapper.Configure(documentStore, settings); @@ -38,4 +67,4 @@ public IPersistence Create(PersistenceSettings settings) return new RavenDbPersistence(settings, documentStore, ravenStartup); } } -} +} \ No newline at end of file diff --git a/src/ServiceControl.Persistence.RavenDb/SagaAudit/AuditRetentionCustomCheck.cs b/src/ServiceControl.Persistence.RavenDb/SagaAudit/AuditRetentionCustomCheck.cs index 6204aa696d..e671a462df 100644 --- a/src/ServiceControl.Persistence.RavenDb/SagaAudit/AuditRetentionCustomCheck.cs +++ b/src/ServiceControl.Persistence.RavenDb/SagaAudit/AuditRetentionCustomCheck.cs @@ -4,28 +4,29 @@ namespace ServiceControl.Persistence.RavenDb.SagaAudit using System; using System.Threading.Tasks; using NServiceBus.CustomChecks; - using Persistence; - using Persistence.RavenDb; using Raven.Client; using ServiceControl.SagaAudit; // This custom check stays in the Raven3.5 persister becuase the Raven5 persister will not store audit data class AuditRetentionCustomCheck : CustomCheck - { - readonly IDocumentStore _documentStore; - readonly bool _auditRetentionPeriodIsSet; + readonly IDocumentStore documentStore; + readonly bool auditRetentionPeriodIsSet; - public AuditRetentionCustomCheck(IDocumentStore documentStore, PersistenceSettings settings, TimeSpan? repeatAfter = null) + public AuditRetentionCustomCheck( + IDocumentStore documentStore, + RavenDBPersisterSettings settings, + TimeSpan? repeatAfter = null + ) : base("Saga Audit Data Retention", "ServiceControl Health", repeatAfter.HasValue ? repeatAfter : TimeSpan.FromHours(1)) { - _documentStore = documentStore; - _auditRetentionPeriodIsSet = settings.PersisterSpecificSettings.ContainsKey(RavenDbPersistenceConfiguration.AuditRetentionPeriodKey); + this.documentStore = documentStore; + auditRetentionPeriodIsSet = settings.AuditRetentionPeriod != default; } public override async Task PerformCheck() { - if (_auditRetentionPeriodIsSet) + if (auditRetentionPeriodIsSet) { return CheckResult.Pass; } @@ -40,7 +41,7 @@ public override async Task PerformCheck() async Task DetectSagaAuditData() { - using (var session = _documentStore.OpenAsyncSession()) + using (var session = documentStore.OpenAsyncSession()) { return await session.Query().AnyAsync(); } diff --git a/src/ServiceControl.Persistence.Tests.RavenDb/API/APIApprovals.cs b/src/ServiceControl.Persistence.Tests.RavenDb/API/APIApprovals.cs index 33c836cf72..85a08f1220 100644 --- a/src/ServiceControl.Persistence.Tests.RavenDb/API/APIApprovals.cs +++ b/src/ServiceControl.Persistence.Tests.RavenDb/API/APIApprovals.cs @@ -7,7 +7,6 @@ using NServiceBus.CustomChecks; using NUnit.Framework; using Particular.Approvals; - using Persistence; using Persistence.RavenDb; using ServiceBus.Management.Infrastructure.Settings; @@ -36,18 +35,18 @@ static IEnumerable GetCustomChecks() var customCheckTypes = serviceControlTypes.Where(t => typeof(ICustomCheck).IsAssignableFrom(t)); - var objects = new List() + var supportedConstructorArguments = new List() { new Settings(), - new PersistenceSettings(TimeSpan.Zero, TimeSpan.Zero, TimeSpan.Zero, 1, false) + new RavenDBPersisterSettings { - PersisterSpecificSettings = { [RavenDbPersistenceConfiguration.DbPathKey] = "c:/" } + DatabasePath = "c:/" } }; - object MapParam(ParameterInfo pi) + object MapConstructorParameter(ParameterInfo pi) { - foreach (var obj in objects) + foreach (var obj in supportedConstructorArguments) { if (obj.GetType() == pi.ParameterType) { @@ -63,7 +62,7 @@ object MapParam(ParameterInfo pi) { var constructor = customCheckType.GetConstructors().Single(); var constructorParameters = constructor.GetParameters() - .Select(MapParam) + .Select(MapConstructorParameter) .ToArray(); var instance = (ICustomCheck)constructor.Invoke(constructorParameters); yield return instance; diff --git a/src/ServiceControl.Persistence.Tests.RavenDb/TestPersistenceImpl.cs b/src/ServiceControl.Persistence.Tests.RavenDb/TestPersistenceImpl.cs index c803a50d9f..20abc166c5 100644 --- a/src/ServiceControl.Persistence.Tests.RavenDb/TestPersistenceImpl.cs +++ b/src/ServiceControl.Persistence.Tests.RavenDb/TestPersistenceImpl.cs @@ -10,33 +10,30 @@ using NUnit.Framework; using Persistence; using Raven.Client; - using ServiceBus.Management.Infrastructure.Settings; using ServiceControl.Persistence.RavenDb; sealed class TestPersistenceImpl : TestPersistence { + readonly RavenDBPersisterSettings settings = CreateSettings(); IDocumentStore documentStore; - public const string DatabaseMaintenancePort = "55554"; - - static PersistenceSettings CreateSettings() + static RavenDBPersisterSettings CreateSettings() { var retentionPeriod = TimeSpan.FromMinutes(1); - var settings = new PersistenceSettings(retentionPeriod, retentionPeriod, retentionPeriod, 100, false) + + var settings = new RavenDBPersisterSettings { - PersisterSpecificSettings = - { - [RavenBootstrapper.RunInMemoryKey] = bool.TrueString, - [RavenBootstrapper.HostNameKey] = "localhost", - [RavenBootstrapper.DatabaseMaintenancePortKey] = DatabaseMaintenancePort - } + AuditRetentionPeriod = retentionPeriod, + ErrorRetentionPeriod = retentionPeriod, + EventsRetentionPeriod = retentionPeriod, + RunInMemory = true, }; if (Debugger.IsAttached) { Console.WriteLine("If you get 'Access is denied' exception while debugging, comment out this line or create a URLACL reservervation:"); Console.WriteLine("> netsh http add urlacl http://+:55554/ user=Everyone"); - settings.PersisterSpecificSettings[RavenBootstrapper.ExposeRavenDBKey] = bool.TrueString; + settings.ExposeRavenDB = true; } return settings; @@ -44,12 +41,9 @@ static PersistenceSettings CreateSettings() public override void Configure(IServiceCollection services) { - var config = PersistenceConfigurationFactory.LoadPersistenceConfiguration(DataStoreConfig.RavenDB35PersistenceTypeFullyQualifiedName); - var settings = CreateSettings(); - - var instance = config.Create(settings); - PersistenceHostBuilderExtensions.CreatePersisterLifecyle(services, instance); + var persistence = new RavenDbPersistenceConfiguration().Create(CreateSettings()); + PersistenceHostBuilderExtensions.CreatePersisterLifecyle(services, persistence); services.AddHostedService(p => new Wrapper(this, p.GetRequiredService())); } @@ -79,7 +73,7 @@ public override void BlockToInspectDatabase() return; } - var url = $"http://localhost:{DatabaseMaintenancePort}/studio/index.html#databases/documents?&database=%3Csystem%3E"; + var url = $"http://localhost:{settings.DatabaseMaintenancePort}/studio/index.html#databases/documents?&database=%3Csystem%3E"; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { diff --git a/src/ServiceControl.Persistence/IPersistenceConfiguration.cs b/src/ServiceControl.Persistence/IPersistenceConfiguration.cs index e7794196c8..820c174a44 100644 --- a/src/ServiceControl.Persistence/IPersistenceConfiguration.cs +++ b/src/ServiceControl.Persistence/IPersistenceConfiguration.cs @@ -1,11 +1,9 @@ namespace ServiceControl.Persistence { - using System.Collections.Generic; + using System; public interface IPersistenceConfiguration { - string Name { get; } - IEnumerable ConfigurationKeys { get; } - IPersistence Create(PersistenceSettings settings); + IPersistence Create(Func readSetting); } } \ No newline at end of file diff --git a/src/ServiceControl/Bootstrapper.cs b/src/ServiceControl/Bootstrapper.cs index 87cb5ab53b..4864aa61c6 100644 --- a/src/ServiceControl/Bootstrapper.cs +++ b/src/ServiceControl/Bootstrapper.cs @@ -70,9 +70,6 @@ public Bootstrapper(Settings settings, EndpointConfiguration configuration, Logg void CreateHost() { - var persistenceConfiguration = PersistenceConfigurationFactory.LoadPersistenceConfiguration(settings.PersistenceType); - var persistenceSettings = persistenceConfiguration.BuildPersistenceSettings(settings); - RecordStartup(loggingSettings, configuration); if (!string.IsNullOrWhiteSpace(settings.LicenseFileText)) @@ -115,7 +112,7 @@ void CreateHost() services.AddSingleton(sp => HttpClientFactory); }) .UseLicenseCheck() - .SetupPersistence(persistenceSettings, persistenceConfiguration) + .SetupPersistence(settings.PersistenceType) .UseMetrics(settings.PrintMetrics) .UseNServiceBus(context => { diff --git a/src/ServiceControl/Hosting/HostArguments.cs b/src/ServiceControl/Hosting/HostArguments.cs index 0e58cc5fbb..8a3e68f3a6 100644 --- a/src/ServiceControl/Hosting/HostArguments.cs +++ b/src/ServiceControl/Hosting/HostArguments.cs @@ -13,7 +13,7 @@ class HostArguments { public HostArguments(string[] args) { - if (ConfigFileSettingsReader.Read("MaintenanceMode")) + if (SettingsReader.ConfigFile.Read("MaintenanceMode")) { args = args.Concat(new[] { diff --git a/src/ServiceControl/Infrastructure/Settings/LoggingSettings.cs b/src/ServiceControl/Infrastructure/Settings/LoggingSettings.cs index 970d0759b0..986468da2d 100644 --- a/src/ServiceControl/Infrastructure/Settings/LoggingSettings.cs +++ b/src/ServiceControl/Infrastructure/Settings/LoggingSettings.cs @@ -11,7 +11,7 @@ public LoggingSettings(string serviceName, LogLevel defaultLevel = null, LogLeve { LoggingLevel = InitializeLevel("LogLevel", defaultLevel ?? LogLevel.Info); RavenDBLogLevel = InitializeLevel("RavenDBLogLevel", defaultRavenDBLevel ?? LogLevel.Warn); - LogPath = Environment.ExpandEnvironmentVariables(SettingsReader.Read("LogPath", logPath ?? DefaultLogPathForInstance(serviceName))); + LogPath = Environment.ExpandEnvironmentVariables(SettingsReader.Read("LogPath", logPath ?? DefaultLogPathForInstance(serviceName))); LogToConsole = logToConsole; } @@ -25,7 +25,7 @@ public LoggingSettings(string serviceName, LogLevel defaultLevel = null, LogLeve LogLevel InitializeLevel(string key, LogLevel defaultLevel) { - var levelText = SettingsReader.Read(key); + var levelText = SettingsReader.Read(key); if (string.IsNullOrWhiteSpace(levelText)) { return defaultLevel; diff --git a/src/ServiceControl/Infrastructure/Settings/Settings.cs b/src/ServiceControl/Infrastructure/Settings/Settings.cs index ca48c5e0be..bc783ea666 100644 --- a/src/ServiceControl/Infrastructure/Settings/Settings.cs +++ b/src/ServiceControl/Infrastructure/Settings/Settings.cs @@ -24,7 +24,7 @@ public Settings(string serviceName = null, string transportType = null, string p } // Overwrite the service name if it is specified in ENVVAR, reg, or config file - ServiceName = SettingsReader.Read("InternalQueueName", ServiceName); + ServiceName = SettingsReader.Read("InternalQueueName", ServiceName); ErrorQueue = GetErrorQueue(); ErrorLogQueue = GetErrorLogQueue(ErrorQueue); @@ -32,26 +32,26 @@ public Settings(string serviceName = null, string transportType = null, string p TryLoadLicenseFromConfig(); TransportConnectionString = GetConnectionString(); - TransportType = transportType ?? SettingsReader.Read("TransportType"); - PersistenceType = persisterType ?? SettingsReader.Read("PersistenceType"); + TransportType = transportType ?? SettingsReader.Read("TransportType"); + PersistenceType = persisterType ?? SettingsReader.Read("PersistenceType"); AuditRetentionPeriod = GetAuditRetentionPeriod(); ForwardErrorMessages = GetForwardErrorMessages(); ErrorRetentionPeriod = GetErrorRetentionPeriod(); EventsRetentionPeriod = GetEventRetentionPeriod(); - Port = SettingsReader.Read("Port", 33333); - DatabaseMaintenancePort = SettingsReader.Read("DatabaseMaintenancePort", 33334); // TODO: Should not be in Core but in the persister implementation + Port = SettingsReader.Read("Port", 33333); + DatabaseMaintenancePort = SettingsReader.Read("DatabaseMaintenancePort", 33334); // TODO: Should not be in Core but in the persister implementation ProcessRetryBatchesFrequency = TimeSpan.FromSeconds(30); - MaximumConcurrencyLevel = SettingsReader.Read("MaximumConcurrencyLevel", 10); - RetryHistoryDepth = SettingsReader.Read("RetryHistoryDepth", 10); - HttpDefaultConnectionLimit = SettingsReader.Read("HttpDefaultConnectionLimit", 100); - AllowMessageEditing = SettingsReader.Read("AllowMessageEditing"); - NotificationsFilter = SettingsReader.Read("NotificationsFilter"); + MaximumConcurrencyLevel = SettingsReader.Read("MaximumConcurrencyLevel", 10); + RetryHistoryDepth = SettingsReader.Read("RetryHistoryDepth", 10); + HttpDefaultConnectionLimit = SettingsReader.Read("HttpDefaultConnectionLimit", 100); + AllowMessageEditing = SettingsReader.Read("AllowMessageEditing"); + NotificationsFilter = SettingsReader.Read("NotificationsFilter"); RemoteInstances = GetRemoteInstances().ToArray(); DataSpaceRemainingThreshold = GetDataSpaceRemainingThreshold(); DbPath = GetDbPath(); TimeToRestartErrorIngestionAfterFailure = GetTimeToRestartErrorIngestionAfterFailure(); - DisableExternalIntegrationsPublishing = SettingsReader.Read("DisableExternalIntegrationsPublishing", false); - EnableFullTextSearchOnBodies = SettingsReader.Read("EnableFullTextSearchOnBodies", true); + DisableExternalIntegrationsPublishing = SettingsReader.Read("DisableExternalIntegrationsPublishing", false); + EnableFullTextSearchOnBodies = SettingsReader.Read("EnableFullTextSearchOnBodies", true); } public string NotificationsFilter { get; set; } @@ -64,9 +64,9 @@ public Settings(string serviceName = null, string transportType = null, string p //HINT: acceptance tests only public string EmailDropFolder { get; set; } - public bool ValidateConfiguration => SettingsReader.Read("ValidateConfig", true); + public bool ValidateConfiguration => SettingsReader.Read("ValidateConfig", true); - public int ExternalIntegrationsDispatchingBatchSize => SettingsReader.Read("ExternalIntegrationsDispatchingBatchSize", 100); + public int ExternalIntegrationsDispatchingBatchSize => SettingsReader.Read("ExternalIntegrationsDispatchingBatchSize", 100); public bool DisableExternalIntegrationsPublishing { get; set; } @@ -104,10 +104,10 @@ public string RootUrl public Dictionary PersisterSpecificSettings { get; set; } = new Dictionary(); - public bool ExposeRavenDB => SettingsReader.Read("ExposeRavenDB"); // TODO: Should not be in Core but in the persister implementation - public bool PrintMetrics => SettingsReader.Read("PrintMetrics"); - public string Hostname => SettingsReader.Read("Hostname", "localhost"); - public string VirtualDirectory => SettingsReader.Read("VirtualDirectory", string.Empty); + public bool ExposeRavenDB => SettingsReader.Read("ExposeRavenDB"); // TODO: Should not be in Core but in the persister implementation + public bool PrintMetrics => SettingsReader.Read("PrintMetrics"); + public string Hostname => SettingsReader.Read("Hostname", "localhost"); + public string VirtualDirectory => SettingsReader.Read("VirtualDirectory", string.Empty); public TimeSpan HeartbeatGracePeriod { @@ -115,7 +115,7 @@ public TimeSpan HeartbeatGracePeriod { try { - return TimeSpan.Parse(SettingsReader.Read("HeartbeatGracePeriod", "00:00:40")); + return TimeSpan.Parse(SettingsReader.Read("HeartbeatGracePeriod", "00:00:40")); } catch (Exception ex) { @@ -178,7 +178,7 @@ public TransportCustomization LoadTransportCustomization() public string GetConnectionString() { - var settingsValue = SettingsReader.Read("ConnectionString"); + var settingsValue = SettingsReader.Read("ConnectionString"); if (settingsValue != null) { return settingsValue; @@ -190,7 +190,7 @@ public string GetConnectionString() string GetErrorQueue() { - var value = SettingsReader.Read("ServiceBus", "ErrorQueue", "error"); + var value = SettingsReader.Read("ServiceBus", "ErrorQueue", "error"); if (value == null) { @@ -216,7 +216,7 @@ string GetErrorLogQueue(string errorQueue) return null; } - var value = SettingsReader.Read("ServiceBus", "ErrorLogQueue", null); + var value = SettingsReader.Read("ServiceBus", "ErrorLogQueue", null); if (value == null) { @@ -244,12 +244,12 @@ string GetDbPath() var defaultPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Particular", "ServiceControl", dbFolder); - return SettingsReader.Read("DbPath", defaultPath); + return SettingsReader.Read("DbPath", defaultPath); } static bool GetForwardErrorMessages() { - var forwardErrorMessages = NullableSettingsReader.Read("ForwardErrorMessages"); + var forwardErrorMessages = SettingsReader.Read("ForwardErrorMessages"); if (forwardErrorMessages.HasValue) { return forwardErrorMessages.Value; @@ -265,7 +265,7 @@ static string SanitiseFolderName(string folderName) TimeSpan GetEventRetentionPeriod() { - var valueRead = SettingsReader.Read("EventRetentionPeriod"); + var valueRead = SettingsReader.Read("EventRetentionPeriod"); if (valueRead != null) { if (TimeSpan.TryParse(valueRead, out var result)) @@ -295,7 +295,7 @@ TimeSpan GetEventRetentionPeriod() TimeSpan GetErrorRetentionPeriod() { string message; - var valueRead = SettingsReader.Read("ErrorRetentionPeriod"); + var valueRead = SettingsReader.Read("ErrorRetentionPeriod"); if (valueRead == null) { message = "ErrorRetentionPeriod settings is missing, please make sure it is included."; @@ -332,7 +332,7 @@ TimeSpan GetErrorRetentionPeriod() TimeSpan? GetAuditRetentionPeriod() { string message; - var valueRead = SettingsReader.Read("AuditRetentionPeriod"); + var valueRead = SettingsReader.Read("AuditRetentionPeriod"); if (valueRead == null) { return null; @@ -367,7 +367,7 @@ TimeSpan GetErrorRetentionPeriod() TimeSpan GetTimeToRestartErrorIngestionAfterFailure() { string message; - var valueRead = SettingsReader.Read("TimeToRestartErrorIngestionAfterFailure"); + var valueRead = SettingsReader.Read("TimeToRestartErrorIngestionAfterFailure"); if (valueRead == null) { return TimeSpan.FromSeconds(60); @@ -401,7 +401,7 @@ TimeSpan GetTimeToRestartErrorIngestionAfterFailure() static IList GetRemoteInstances() { - var valueRead = SettingsReader.Read("RemoteInstances"); + var valueRead = SettingsReader.Read("RemoteInstances"); if (!string.IsNullOrEmpty(valueRead)) { return ParseRemoteInstances(valueRead); @@ -436,7 +436,7 @@ static string Subscope(string address) int GetDataSpaceRemainingThreshold() { string message; - var threshold = SettingsReader.Read("DataSpaceRemainingThreshold", DataSpaceRemainingThresholdDefault); + var threshold = SettingsReader.Read("DataSpaceRemainingThreshold", DataSpaceRemainingThresholdDefault); if (threshold < 0) { message = $"{nameof(DataSpaceRemainingThreshold)} is invalid, minimum value is 0."; @@ -456,7 +456,7 @@ int GetDataSpaceRemainingThreshold() void TryLoadLicenseFromConfig() { - LicenseFileText = SettingsReader.Read("LicenseText"); + LicenseFileText = SettingsReader.Read("LicenseText"); } ILog logger = LogManager.GetLogger(typeof(Settings)); diff --git a/src/ServiceControl/Infrastructure/WebApi/RootController.cs b/src/ServiceControl/Infrastructure/WebApi/RootController.cs index 0356938396..e671025893 100644 --- a/src/ServiceControl/Infrastructure/WebApi/RootController.cs +++ b/src/ServiceControl/Infrastructure/WebApi/RootController.cs @@ -45,8 +45,8 @@ public OkNegotiatedContentResult Urls() EndpointsMessagesUrl = baseUrl + "endpoints/{name}/messages/{?page,per_page,direction,sort}", AuditCountUrl = baseUrl + "endpoints/{name}/audit-count", - Name = SettingsReader.Read("Name", "ServiceControl"), - Description = SettingsReader.Read("Description", "The management backend for the Particular Service Platform"), + Name = SettingsReader.Read("Name", "ServiceControl"), + Description = SettingsReader.Read("Description", "The management backend for the Particular Service Platform"), LicenseStatus = license.IsValid ? "valid" : "invalid", LicenseDetails = baseUrl + "license", Configuration = baseUrl + "configuration", diff --git a/src/ServiceControl/MaintenanceBootstrapper.cs b/src/ServiceControl/MaintenanceBootstrapper.cs index 805539ea9b..df6c3e5e3a 100644 --- a/src/ServiceControl/MaintenanceBootstrapper.cs +++ b/src/ServiceControl/MaintenanceBootstrapper.cs @@ -11,11 +11,8 @@ static class MaintenanceBootstrapper { public static async Task Run(HostArguments args, Settings settings) { - var persistenceConfiguration = PersistenceConfigurationFactory.LoadPersistenceConfiguration(settings.PersistenceType); - var persistenceSettings = persistenceConfiguration.BuildPersistenceSettings(settings, maintenanceMode: true); - var hostBuilder = new HostBuilder() - .SetupPersistence(persistenceSettings, persistenceConfiguration); + .SetupPersistence(settings.PersistenceType); if (args.RunAsWindowsService) { diff --git a/src/ServiceControl/Persistence/PersistenceConfigurationFactory.cs b/src/ServiceControl/Persistence/PersistenceConfigurationFactory.cs index ef50d5995a..0036e5da9d 100644 --- a/src/ServiceControl/Persistence/PersistenceConfigurationFactory.cs +++ b/src/ServiceControl/Persistence/PersistenceConfigurationFactory.cs @@ -3,53 +3,21 @@ namespace ServiceControl.Persistence using System; using ServiceBus.Management.Infrastructure.Settings; - // Added recently by David - - static class PersistenceConfigurationFactory + static class PersistenceFactory { - public static IPersistenceConfiguration LoadPersistenceConfiguration(string persistenceType) + public static IPersistence Create(string persistenceType, Func readSetting = default) { try { var foundPersistenceType = PersistenceManifestLibrary.Find(persistenceType); - var customizationType = Type.GetType(foundPersistenceType, true); - - return (IPersistenceConfiguration)Activator.CreateInstance(customizationType); + var persistenceConfiguration = (IPersistenceConfiguration)Activator.CreateInstance(customizationType); + return persistenceConfiguration.Create(readSetting ?? SettingsReader.Read); } catch (Exception e) { throw new Exception($"Could not load persistence customization type {persistenceType}.", e); } } - - public static PersistenceSettings BuildPersistenceSettings(this IPersistenceConfiguration persistenceConfiguration, Settings settings, bool maintenanceMode = false) - { - // TODO: Audit instance passed settings.AuditRetentionPeriod, settings.EnableFullTextSearchOnBodies, settings.MaxBodySizeToStore - are those needed? - // And then remove settings parameter if not needed (but it probably is for something) - var persistenceSettings = new PersistenceSettings( - settings.ErrorRetentionPeriod, - settings.EventsRetentionPeriod, - settings.AuditRetentionPeriod, - settings.ExternalIntegrationsDispatchingBatchSize, - maintenanceMode - ); - - foreach (var keyPair in settings.PersisterSpecificSettings) - { - persistenceSettings.PersisterSpecificSettings[keyPair.Key] = keyPair.Value; - } - - foreach (var key in persistenceConfiguration.ConfigurationKeys) - { - var value = SettingsReader.Read("ServiceControl", key, null); - if (!string.IsNullOrWhiteSpace(value)) - { - persistenceSettings.PersisterSpecificSettings[key] = value; - } - } - - return persistenceSettings; - } } } \ No newline at end of file diff --git a/src/ServiceControl/Persistence/PersistenceHostBuilderExtensions.cs b/src/ServiceControl/Persistence/PersistenceHostBuilderExtensions.cs index 0c8942d57a..98def4c67a 100644 --- a/src/ServiceControl/Persistence/PersistenceHostBuilderExtensions.cs +++ b/src/ServiceControl/Persistence/PersistenceHostBuilderExtensions.cs @@ -5,9 +5,9 @@ namespace ServiceControl.Persistence static class PersistenceHostBuilderExtensions { - public static IHostBuilder SetupPersistence(this IHostBuilder hostBuilder, PersistenceSettings persistenceSettings, IPersistenceConfiguration persistenceConfiguration) + public static IHostBuilder SetupPersistence(this IHostBuilder hostBuilder, string persistenceType) { - var persistence = persistenceConfiguration.Create(persistenceSettings); + var persistence = PersistenceFactory.Create(persistenceType); hostBuilder.ConfigureServices(serviceCollection => { diff --git a/src/ServiceControl/SetupBootstrapper.cs b/src/ServiceControl/SetupBootstrapper.cs index d5bdaeadef..1bbe70af9d 100644 --- a/src/ServiceControl/SetupBootstrapper.cs +++ b/src/ServiceControl/SetupBootstrapper.cs @@ -35,9 +35,8 @@ public async Task Run(string username) await installationTask(); } - var persistenceConfiguration = PersistenceConfigurationFactory.LoadPersistenceConfiguration(settings.PersistenceType); - var persistenceSettings = persistenceConfiguration.BuildPersistenceSettings(settings); - var persistence = persistenceConfiguration.Create(persistenceSettings); + var persistence = PersistenceFactory.Create(settings.PersistenceType); + var installer = persistence.CreateInstaller(); await installer.Install(); From 019acb2876b7a0645ea5430163e35b863413359b Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Wed, 30 Aug 2023 12:49:14 +0200 Subject: [PATCH 02/33] Validating some settings values at persister creation and setting default values on settings type --- .../CustomChecks/CheckFreeDiskSpace.cs | 43 +++++++--------- ...CheckMinimumStorageRequiredForIngestion.cs | 49 ++++++++----------- .../RavenDBPersisterSettings.cs | 5 +- .../RavenDbPersistenceConfiguration.cs | 6 ++- 4 files changed, 48 insertions(+), 55 deletions(-) diff --git a/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckFreeDiskSpace.cs b/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckFreeDiskSpace.cs index 38216a0fe8..6211bf9d8c 100644 --- a/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckFreeDiskSpace.cs +++ b/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckFreeDiskSpace.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using NServiceBus.CustomChecks; using NServiceBus.Logging; + using Persistence.RavenDb; class CheckFreeDiskSpace : CustomCheck { @@ -41,37 +42,31 @@ public override Task PerformCheck() : CheckResult.Failed($"{percentRemaining:P0} disk space remaining on data drive '{dataDriveInfo.VolumeLabel} ({dataDriveInfo.RootDirectory})' on '{Environment.MachineName}'."); } - //int GetDataSpaceRemainingThreshold(PersistenceSettings settings) - //{ - // var threshold = DataSpaceRemainingThresholdDefault; - - // if (settings.PersisterSpecificSettings.TryGetValue(RavenDbPersistenceConfiguration.DataSpaceRemainingThresholdKey, out var thresholdValue)) - // { - // threshold = int.Parse(thresholdValue); - // } - // string message; + public static void Validate(RavenDBPersisterSettings settings) + { + var threshold = settings.DataSpaceRemainingThreshold; - // if (threshold < 0) - // { - // message = $"{RavenDbPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, minimum value is 0."; - // Logger.Fatal(message); - // throw new Exception(message); - // } + string message; - // if (threshold > 100) - // { - // message = $"{RavenDbPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, maximum value is 100."; - // Logger.Fatal(message); - // throw new Exception(message); - // } + if (threshold < 0) + { + message = $"{RavenDbPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, minimum value is 0."; + Logger.Fatal(message); + throw new Exception(message); + } - // return threshold; - //} + if (threshold > 100) + { + message = $"{RavenDbPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, maximum value is 100."; + Logger.Fatal(message); + throw new Exception(message); + } + } readonly string dataPath; readonly decimal percentageThreshold; - //const int DataSpaceRemainingThresholdDefault = 20; + public const int DataSpaceRemainingThresholdDefault = 20; static readonly ILog Logger = LogManager.GetLogger(typeof(CheckFreeDiskSpace)); } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs b/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs index 82f2a9921e..ee8e8df469 100644 --- a/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs +++ b/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs @@ -56,34 +56,27 @@ public override Task PerformCheck() return CheckResult.Failed(message); } - //int GetMinimumStorageLeftRequiredForIngestion(PersistenceSettings settings) - //{ - // int threshold = MinimumStorageLeftRequiredForIngestionDefault; - - // if (settings.PersisterSpecificSettings.TryGetValue(RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey, out var thresholdValue)) - // { - // threshold = int.Parse(thresholdValue); - // } - - // string message; - // if (threshold < 0) - // { - // message = $"{RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey} is invalid, minimum value is 0."; - // Logger.Fatal(message); - // throw new Exception(message); - // } - - // if (threshold > 100) - // { - // message = $"{RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey} is invalid, maximum value is 100."; - // Logger.Fatal(message); - // throw new Exception(message); - // } - - // return threshold; - //} - - //const int MinimumStorageLeftRequiredForIngestionDefault = 5; + public static void Validate(RavenDBPersisterSettings settings) + { + int threshold = settings.MinimumStorageLeftRequiredForIngestion; + + string message; + if (threshold < 0) + { + message = $"{RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey} is invalid, minimum value is 0."; + Logger.Fatal(message); + throw new Exception(message); + } + + if (threshold > 100) + { + message = $"{RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey} is invalid, maximum value is 100."; + Logger.Fatal(message); + throw new Exception(message); + } + } + + public const int MinimumStorageLeftRequiredForIngestionDefault = 5; readonly MinimumRequiredStorageState stateHolder; readonly RavenDBPersisterSettings settings; diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs b/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs index a6c2000400..01078eae34 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs @@ -1,4 +1,5 @@ using System; +using ServiceControl.Operations; class RavenDBPersisterSettings { @@ -10,8 +11,8 @@ class RavenDBPersisterSettings public int ExpirationProcessBatchSize { get; set; } public bool RunCleanupBundle { get; set; } public bool RunInMemory { get; set; } - public int MinimumStorageLeftRequiredForIngestion { get; set; } - public int DataSpaceRemainingThreshold { get; set; } + public int MinimumStorageLeftRequiredForIngestion { get; set; } = CheckMinimumStorageRequiredForIngestion.MinimumStorageLeftRequiredForIngestionDefault; + public int DataSpaceRemainingThreshold { get; set; } = CheckFreeDiskSpace.DataSpaceRemainingThresholdDefault; public TimeSpan ErrorRetentionPeriod { get; set; } public TimeSpan EventsRetentionPeriod { get; set; } public TimeSpan? AuditRetentionPeriod { get; set; } diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs index ea18824d4f..a584b531b0 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs @@ -2,10 +2,11 @@ { using System; using Raven.Client.Embedded; + using ServiceControl.Operations; class RavenDbPersistenceConfiguration : IPersistenceConfiguration { - const string DataSpaceRemainingThresholdKey = "DataSpaceRemainingThreshold"; + public const string DataSpaceRemainingThresholdKey = "DataSpaceRemainingThreshold"; const string AuditRetentionPeriodKey = "AuditRetentionPeriod"; const string ErrorRetentionPeriodKey = "ErrorRetentionPeriod"; const string EventsRetentionPeriodKey = "EventsRetentionPeriod"; @@ -54,6 +55,9 @@ public IPersistence Create(Func readSetting) MaintenanceMode = GetSetting(MaintenanceModeKey), }; + CheckFreeDiskSpace.Validate(settings); + CheckMinimumStorageRequiredForIngestion.Validate(settings); + return Create(settings); } From e8074fcbc5a2863e048563229afa1232a05f6f00 Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Wed, 30 Aug 2023 13:38:16 +0200 Subject: [PATCH 03/33] PersisterSettings converted into marker interface to that at runtime the persister specific settings can serialized to JSON for diagnostics output in the REST API --- ...When_critical_storage_threshold_reached.cs | 11 +++---- .../ExternalIntegrationRequestsDataStore.cs | 4 +-- .../RavenDBPersisterSettings.cs | 3 +- .../RavenDbPersistence.cs | 1 + .../IPersistenceSettings.cs | 9 ++++++ .../PersistenceSettings.cs | 31 ------------------- .../API/APIApprovals.cs | 10 ++++-- .../Infrastructure/WebApi/RootController.cs | 4 +-- 8 files changed, 28 insertions(+), 45 deletions(-) create mode 100644 src/ServiceControl.Persistence/IPersistenceSettings.cs delete mode 100644 src/ServiceControl.Persistence/PersistenceSettings.cs diff --git a/src/ServiceControl.AcceptanceTests/Monitoring/CustomChecks/When_critical_storage_threshold_reached.cs b/src/ServiceControl.AcceptanceTests/Monitoring/CustomChecks/When_critical_storage_threshold_reached.cs index 77c357fb30..ef84335a9c 100644 --- a/src/ServiceControl.AcceptanceTests/Monitoring/CustomChecks/When_critical_storage_threshold_reached.cs +++ b/src/ServiceControl.AcceptanceTests/Monitoring/CustomChecks/When_critical_storage_threshold_reached.cs @@ -11,7 +11,6 @@ using NServiceBus; using NServiceBus.AcceptanceTesting; using NUnit.Framework; - using Persistence; using ServiceBus.Management.Infrastructure.Settings; using TestSupport.EndpointTemplates; @@ -30,9 +29,9 @@ public void SetupIngestion() class PersisterSettingsRetriever : IHostedService { - static PersistenceSettings _persistenceSettings; + static RavenDBPersisterSettings _persistenceSettings; - public PersisterSettingsRetriever(PersistenceSettings persistenceSettings, int value) + public PersisterSettingsRetriever(RavenDBPersisterSettings persistenceSettings, int value) { _persistenceSettings = persistenceSettings; SetMinimumStorageLeftForIngestion(value); @@ -41,13 +40,13 @@ public PersisterSettingsRetriever(PersistenceSettings persistenceSettings, int v public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask; public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; - public static void SetMinimumStorageLeftForIngestion(int value) => _persistenceSettings.PersisterSpecificSettings["MinimumStorageLeftRequiredForIngestion"] = value.ToString(); + public static void SetMinimumStorageLeftForIngestion(int value) => _persistenceSettings.MinimumStorageLeftRequiredForIngestion = value; // TODO: Although this is already better we should not need to host trick to access RavenDBPersisterSettings } [Test] public async Task Should_stop_ingestion() { - CustomizeHostBuilder = hostBuilder => hostBuilder.ConfigureServices(services => services.AddHostedService(b => new PersisterSettingsRetriever(b.GetRequiredService(), 0))); + CustomizeHostBuilder = hostBuilder => hostBuilder.ConfigureServices(services => services.AddHostedService(b => new PersisterSettingsRetriever(b.GetRequiredService(), 0))); await Define() .WithEndpoint(b => b @@ -74,7 +73,7 @@ await Define() [Test] public async Task Should_stop_ingestion_and_resume_when_more_space_is_available() { - CustomizeHostBuilder = hostBuilder => hostBuilder.ConfigureServices(services => services.AddHostedService(b => new PersisterSettingsRetriever(b.GetRequiredService(), 0))); + CustomizeHostBuilder = hostBuilder => hostBuilder.ConfigureServices(services => services.AddHostedService(b => new PersisterSettingsRetriever(b.GetRequiredService(), 0))); var ingestionShutdown = false; await Define() diff --git a/src/ServiceControl.Persistence.RavenDb/ExternalIntegrationRequestsDataStore.cs b/src/ServiceControl.Persistence.RavenDb/ExternalIntegrationRequestsDataStore.cs index 10288ff84e..ebb0db036d 100644 --- a/src/ServiceControl.Persistence.RavenDb/ExternalIntegrationRequestsDataStore.cs +++ b/src/ServiceControl.Persistence.RavenDb/ExternalIntegrationRequestsDataStore.cs @@ -20,7 +20,7 @@ class ExternalIntegrationRequestsDataStore , IHostedService , IAsyncDisposable { - public ExternalIntegrationRequestsDataStore(PersistenceSettings settings, IDocumentStore documentStore, CriticalError criticalError) + public ExternalIntegrationRequestsDataStore(RavenDBPersisterSettings settings, IDocumentStore documentStore, CriticalError criticalError) { this.settings = settings; this.documentStore = documentStore; @@ -202,7 +202,7 @@ public async ValueTask DisposeAsync() circuitBreaker?.Dispose(); } - readonly PersistenceSettings settings; + readonly RavenDBPersisterSettings settings; readonly IDocumentStore documentStore; readonly CancellationTokenSource tokenSource = new CancellationTokenSource(); readonly RepeatedFailuresOverTimeCircuitBreaker circuitBreaker; diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs b/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs index 01078eae34..1e23b2957b 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs @@ -1,7 +1,8 @@ using System; using ServiceControl.Operations; +using ServiceControl.Persistence; -class RavenDBPersisterSettings +class RavenDBPersisterSettings : IPersistenceSettings { public string DatabasePath { get; set; } public string HostName { get; set; } = "localhost"; diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs index a07485b113..0672a5739f 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs @@ -29,6 +29,7 @@ public RavenDbPersistence(RavenDBPersisterSettings settings, EmbeddableDocumentS public void Configure(IServiceCollection serviceCollection) { serviceCollection.AddSingleton(settings); + serviceCollection.AddSingleton(settings); serviceCollection.AddSingleton(documentStore); serviceCollection.AddSingleton(); diff --git a/src/ServiceControl.Persistence/IPersistenceSettings.cs b/src/ServiceControl.Persistence/IPersistenceSettings.cs new file mode 100644 index 0000000000..ee2cd352f7 --- /dev/null +++ b/src/ServiceControl.Persistence/IPersistenceSettings.cs @@ -0,0 +1,9 @@ +namespace ServiceControl.Persistence +{ + /// + /// Marker interface used to serialize persister settings in REST API + /// + public interface IPersistenceSettings + { + } +} \ No newline at end of file diff --git a/src/ServiceControl.Persistence/PersistenceSettings.cs b/src/ServiceControl.Persistence/PersistenceSettings.cs deleted file mode 100644 index f037c991e3..0000000000 --- a/src/ServiceControl.Persistence/PersistenceSettings.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace ServiceControl.Persistence -{ - using System; - using System.Collections.Generic; - - public class PersistenceSettings - { - public PersistenceSettings( - TimeSpan errorRetentionPeriod, - TimeSpan eventsRetentionPeriod, - TimeSpan? auditRetentionPeriod, - int externalIntegrationsDispatchingBatchSize, - bool maintenanceMode - ) - { - ErrorRetentionPeriod = errorRetentionPeriod; - EventsRetentionPeriod = eventsRetentionPeriod; - AuditRetentionPeriod = auditRetentionPeriod; - MaintenanceMode = maintenanceMode; - ExternalIntegrationsDispatchingBatchSize = externalIntegrationsDispatchingBatchSize; - } - - public IDictionary PersisterSpecificSettings { get; } = new Dictionary(); - - public bool MaintenanceMode { get; } - public TimeSpan ErrorRetentionPeriod { get; } - public TimeSpan EventsRetentionPeriod { get; } - public TimeSpan? AuditRetentionPeriod { get; } - public int ExternalIntegrationsDispatchingBatchSize { get; } - } -} \ No newline at end of file diff --git a/src/ServiceControl.UnitTests/API/APIApprovals.cs b/src/ServiceControl.UnitTests/API/APIApprovals.cs index d85d09c760..16e8666115 100644 --- a/src/ServiceControl.UnitTests/API/APIApprovals.cs +++ b/src/ServiceControl.UnitTests/API/APIApprovals.cs @@ -17,7 +17,6 @@ using PublicApiGenerator; using ServiceBus.Management.Infrastructure.Settings; using ServiceControl.Infrastructure.WebApi; - using ServiceControl.Persistence; using ServiceControlInstaller.Engine.Instances; [TestFixture] @@ -29,8 +28,13 @@ public void RootPathValue() var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost"); request.Properties.Add(HttpPropertyKeys.RequestContextKey, new HttpRequestContext { VirtualPathRoot = "/" }); - var persistenceSettings = new PersistenceSettings(TimeSpan.Zero, TimeSpan.Zero, TimeSpan.Zero, 1, false); - var controller = new RootController(new ActiveLicense { IsValid = true }, new LoggingSettings("testEndpoint"), new Settings(), persistenceSettings, httpClientFactory: null) + var controller = new RootController( + new ActiveLicense { IsValid = true }, + new LoggingSettings("testEndpoint"), + new Settings(), + null, // TODO: Previously was PersistenceSettings, now persister registers persister specific settings in DI using marker interface to settings can be serialized in API + httpClientFactory: null + ) { Url = new UrlHelper(request) }; diff --git a/src/ServiceControl/Infrastructure/WebApi/RootController.cs b/src/ServiceControl/Infrastructure/WebApi/RootController.cs index e671025893..af1b4de593 100644 --- a/src/ServiceControl/Infrastructure/WebApi/RootController.cs +++ b/src/ServiceControl/Infrastructure/WebApi/RootController.cs @@ -15,7 +15,7 @@ class RootController : ApiController { - public RootController(ActiveLicense license, LoggingSettings loggingSettings, Settings settings, PersistenceSettings persistenceSettings, Func httpClientFactory) + public RootController(ActiveLicense license, LoggingSettings loggingSettings, Settings settings, IPersistenceSettings persistenceSettings, Func httpClientFactory) { this.settings = settings; this.persistenceSettings = persistenceSettings; @@ -157,7 +157,7 @@ public async Task RemoteConfig() readonly LoggingSettings loggingSettings; readonly ActiveLicense license; readonly Settings settings; - readonly PersistenceSettings persistenceSettings; + readonly IPersistenceSettings persistenceSettings; readonly Func httpClientFactory; static readonly JsonSerializer jsonSerializer = JsonSerializer.Create(JsonNetSerializerSettings.CreateDefault()); From 522e22ae47e7751ca81d269e34ef0574043ff4df Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Wed, 30 Aug 2023 14:26:02 +0200 Subject: [PATCH 04/33] Refactorings on SettingsReader --- .../ConfigFileSettingsReader.cs | 11 ++---- .../EnvironmentVariableSettingsReader.cs | 9 ++--- .../RegistryReader.cs | 4 +- .../SettingsReader.cs | 19 +++++---- .../SettingsReaderExtensions.cs | 39 ------------------- 5 files changed, 17 insertions(+), 65 deletions(-) delete mode 100644 src/ServiceControl.Configuration/SettingsReaderExtensions.cs diff --git a/src/ServiceControl.Configuration/ConfigFileSettingsReader.cs b/src/ServiceControl.Configuration/ConfigFileSettingsReader.cs index 3e20432e5b..2c966992dc 100644 --- a/src/ServiceControl.Configuration/ConfigFileSettingsReader.cs +++ b/src/ServiceControl.Configuration/ConfigFileSettingsReader.cs @@ -12,12 +12,9 @@ public object Read(string name, Type type, object defaultValue = default) public object Read(string root, string name, Type type, object defaultValue = default) { - if (TryRead(root, name, type, out var value)) - { - return value; - } - - return defaultValue; + return TryRead(root, name, type, out var value) + ? value + : defaultValue; } public bool TryRead(string root, string name, Type type, out object value) @@ -27,7 +24,7 @@ public bool TryRead(string root, string name, Type type, out object value) var appSettingValue = ConfigurationManager.AppSettings[fullKey]; if (appSettingValue != null) { - appSettingValue = Environment.ExpandEnvironmentVariables(appSettingValue); // TODO: Just added this to have expansing on appsettings to not have hardcoded "temp" paths which are different for everyone. + appSettingValue = Environment.ExpandEnvironmentVariables(appSettingValue); value = Convert.ChangeType(appSettingValue, type); return true; } diff --git a/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs b/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs index 913b81be29..f763a0ee11 100644 --- a/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs +++ b/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs @@ -6,12 +6,9 @@ class EnvironmentVariableSettingsReader : ISettingsReader { public object Read(string root, string name, Type type, object defaultValue = default) { - if (TryRead(root, name, type, out var value)) - { - return value; - } - - return defaultValue; + return TryRead(root, name, type, out var value) + ? value + : defaultValue; } public bool TryRead(string root, string name, Type type, out object value) diff --git a/src/ServiceControl.Configuration/RegistryReader.cs b/src/ServiceControl.Configuration/RegistryReader.cs index 21fdf6c779..bd619ff01b 100644 --- a/src/ServiceControl.Configuration/RegistryReader.cs +++ b/src/ServiceControl.Configuration/RegistryReader.cs @@ -7,7 +7,6 @@ namespace ServiceBus.Management.Infrastructure.Settings /// /// Wrapper to read registry keys. /// - /// The type of the key to retrieve class RegistryReader : ISettingsReader { /// @@ -64,8 +63,7 @@ public object Read(string subKey, string name, Type type, object defaultValue = } catch (Exception ex) { - Logger.Warn( - $@"We couldn't read the registry to retrieve the {name}, from '{regPath}'.", ex); + Logger.Warn($"We couldn't read the registry to retrieve the {name}, from '{regPath}'.", ex); } return defaultValue; diff --git a/src/ServiceControl.Configuration/SettingsReader.cs b/src/ServiceControl.Configuration/SettingsReader.cs index e6310b3b5e..2d0827f71d 100644 --- a/src/ServiceControl.Configuration/SettingsReader.cs +++ b/src/ServiceControl.Configuration/SettingsReader.cs @@ -2,11 +2,11 @@ { using System; - class SettingsReader + static class SettingsReader { - public static readonly EnvironmentVariableSettingsReader EnvironmentVariable = new EnvironmentVariableSettingsReader(); - public static readonly ConfigFileSettingsReader ConfigFile = new ConfigFileSettingsReader(); - public static readonly RegistryReader Registry = new RegistryReader(); + static readonly ISettingsReader EnvironmentVariable = new EnvironmentVariableSettingsReader(); + static readonly ISettingsReader Registry = new RegistryReader(); + public static readonly ISettingsReader ConfigFile = new ConfigFileSettingsReader(); public static T Read(string name, T defaultValue = default) { @@ -18,12 +18,7 @@ public static T Read(string root, string name, T defaultValue = default) return (T)Read(root, name, typeof(T), defaultValue); } - //public static object Read(string name, Type type, object defaultValue = default) - //{ - // return Read("ServiceControl", name, type, defaultValue); - //} - - public static object Read(string root, string name, Type type, object defaultValue = default) + static object Read(string root, string name, Type type, object defaultValue = default) { if (EnvironmentVariable.TryRead(root, name, type, out var envValue)) { @@ -38,5 +33,9 @@ public static object Read(string root, string name, Type type, object defaultVal return Registry.Read(root, name, type, defaultValue); } + public static T Read(this ISettingsReader instance, string name, T defaultValue = default) + { + return (T)instance.Read("ServiceControl", name, typeof(T), defaultValue); + } } } \ No newline at end of file diff --git a/src/ServiceControl.Configuration/SettingsReaderExtensions.cs b/src/ServiceControl.Configuration/SettingsReaderExtensions.cs deleted file mode 100644 index 4be1dee942..0000000000 --- a/src/ServiceControl.Configuration/SettingsReaderExtensions.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace ServiceBus.Management.Infrastructure.Settings -{ - using System; - - static class SettingsReaderExtensions - { - public static T Read(this ISettingsReader instance, string name, T defaultValue = default) - { - return (T)instance.Read("ServiceControl", name, typeof(T), defaultValue); - } - - public static T Read(this ISettingsReader instance, string root, string name, T defaultValue = default) - { - if (instance.TryRead(root, name, typeof(T), out var value)) - { - return (T)value; - } - - return defaultValue; - } - - public static bool TryRead(this ISettingsReader instance, string root, string name, out T value) - { - if (instance.TryRead(root, name, typeof(T), out object innerValue)) - { - value = (T)innerValue; - return true; - } - - value = default; - return false; - } - - public static object Read(this ISettingsReader instance, string name, Type type, object defaultValue = null) - { - return instance.Read("ServiceControl", name, type, defaultValue); - } - } -} \ No newline at end of file From 4c549c39ec8ddb98a9f240dd5069afecaad83f21 Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Wed, 30 Aug 2023 21:14:57 +0200 Subject: [PATCH 05/33] =?UTF-8?q?=E2=9A=9C=EF=B8=8F=20Code=20readability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AcceptanceTest.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ServiceControl.MultiInstance.AcceptanceTests/AcceptanceTest.cs b/src/ServiceControl.MultiInstance.AcceptanceTests/AcceptanceTest.cs index c83cffbe63..0a3e140f3d 100644 --- a/src/ServiceControl.MultiInstance.AcceptanceTests/AcceptanceTest.cs +++ b/src/ServiceControl.MultiInstance.AcceptanceTests/AcceptanceTest.cs @@ -71,7 +71,14 @@ public void Setup() DataStoreTypeName = "RavenDB35" }; - serviceControlRunnerBehavior = new ServiceControlComponentBehavior(TransportIntegration, DataStoreConfiguration, c => CustomEndpointConfiguration(c), c => CustomAuditEndpointConfiguration(c), s => CustomServiceControlSettings(s), s => CustomServiceControlAuditSettings(s)); + serviceControlRunnerBehavior = new ServiceControlComponentBehavior( + TransportIntegration, + DataStoreConfiguration, + c => CustomEndpointConfiguration(c), + c => CustomAuditEndpointConfiguration(c), + s => CustomServiceControlSettings(s), + s => CustomServiceControlAuditSettings(s) + ); } [TearDown] From efe504eefcc8c76f76916a869a45ac79ef407554 Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Wed, 30 Aug 2023 21:18:09 +0200 Subject: [PATCH 06/33] =?UTF-8?q?=F0=9F=94=A8=20Settings=20readers=20now?= =?UTF-8?q?=20using=20`TypeDescriptor.GetConverter`=20as=20=20`Convert.Cha?= =?UTF-8?q?ngeType`=20can't=20convert=20to=20TimeSpan=20and=20now=20suppor?= =?UTF-8?q?ts=20TryRead=20on=20on=20SettingsReader=20to=20deal=20with=20no?= =?UTF-8?q?n-nulleable=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ConfigFileSettingsReader.cs | 13 +++--- .../EnvironmentVariableSettingsReader.cs | 2 +- .../RegistryReader.cs | 14 ++++--- .../SettingsReader.cs | 40 ++++++++++++++++++- 4 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/ServiceControl.Configuration/ConfigFileSettingsReader.cs b/src/ServiceControl.Configuration/ConfigFileSettingsReader.cs index 2c966992dc..9d9a5fc7a5 100644 --- a/src/ServiceControl.Configuration/ConfigFileSettingsReader.cs +++ b/src/ServiceControl.Configuration/ConfigFileSettingsReader.cs @@ -5,11 +5,6 @@ namespace ServiceBus.Management.Infrastructure.Settings class ConfigFileSettingsReader : ISettingsReader { - public object Read(string name, Type type, object defaultValue = default) - { - return Read("ServiceControl", name, type, defaultValue); - } - public object Read(string root, string name, Type type, object defaultValue = default) { return TryRead(root, name, type, out var value) @@ -25,7 +20,13 @@ public bool TryRead(string root, string name, Type type, out object value) if (appSettingValue != null) { appSettingValue = Environment.ExpandEnvironmentVariables(appSettingValue); - value = Convert.ChangeType(appSettingValue, type); + + var underlyingType = Nullable.GetUnderlyingType(type); + + var destinationType = underlyingType ?? type; + + value = SettingsReader.ConvertFrom(appSettingValue, destinationType); + return true; } diff --git a/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs b/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs index f763a0ee11..92a567cb03 100644 --- a/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs +++ b/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs @@ -34,7 +34,7 @@ static bool TryReadVariable(Type type, out object value, string fullKey) if (environmentValue != null) { environmentValue = Environment.ExpandEnvironmentVariables(environmentValue); - value = Convert.ChangeType(environmentValue, type); + value = SettingsReader.ConvertFrom(environmentValue, type); return true; } diff --git a/src/ServiceControl.Configuration/RegistryReader.cs b/src/ServiceControl.Configuration/RegistryReader.cs index bd619ff01b..50b03178f0 100644 --- a/src/ServiceControl.Configuration/RegistryReader.cs +++ b/src/ServiceControl.Configuration/RegistryReader.cs @@ -34,7 +34,7 @@ public object Read(string subKey, string name, Type type, object defaultValue = if (value != null) { - return Convert.ChangeType(value, type); + return SettingsReader.ConvertFrom(value, type); } } @@ -44,7 +44,7 @@ public object Read(string subKey, string name, Type type, object defaultValue = { if (registryKey != null) { - return Convert.ChangeType(registryKey.GetValue(name, defaultValue), type); + return SettingsReader.ConvertFrom(registryKey.GetValue(name, defaultValue), type); } } } @@ -56,20 +56,24 @@ public object Read(string subKey, string name, Type type, object defaultValue = { if (registryKey != null) { - return Convert.ChangeType(registryKey.GetValue(name, defaultValue), type); + return SettingsReader.ConvertFrom(registryKey.GetValue(name, defaultValue), type); } } } } catch (Exception ex) { - Logger.Warn($"We couldn't read the registry to retrieve the {name}, from '{regPath}'.", ex); + Logger.Warn($"Couldn't read the registry to retrieve the {name}, from '{regPath}'.", ex); } return defaultValue; } - public bool TryRead(string root, string name, Type type, out object value) => throw new NotImplementedException(); + public bool TryRead(string root, string name, Type type, out object value) + { + value = Read(root, name, type); + return value != null; + } static readonly ILog Logger = LogManager.GetLogger(typeof(RegistryReader)); } diff --git a/src/ServiceControl.Configuration/SettingsReader.cs b/src/ServiceControl.Configuration/SettingsReader.cs index 2d0827f71d..58a94aa6cb 100644 --- a/src/ServiceControl.Configuration/SettingsReader.cs +++ b/src/ServiceControl.Configuration/SettingsReader.cs @@ -1,16 +1,18 @@ namespace ServiceBus.Management.Infrastructure.Settings { using System; + using System.ComponentModel; static class SettingsReader { + const string Namespace = "ServiceControl"; static readonly ISettingsReader EnvironmentVariable = new EnvironmentVariableSettingsReader(); static readonly ISettingsReader Registry = new RegistryReader(); public static readonly ISettingsReader ConfigFile = new ConfigFileSettingsReader(); public static T Read(string name, T defaultValue = default) { - return Read("ServiceControl", name, defaultValue); + return Read(Namespace, name, defaultValue); } public static T Read(string root, string name, T defaultValue = default) @@ -33,9 +35,43 @@ static object Read(string root, string name, Type type, object defaultValue = de return Registry.Read(root, name, type, defaultValue); } + public static bool TryRead(string name, Type type, out object value) + { + var root = Namespace; + + if (EnvironmentVariable.TryRead(root, name, type, out var envValue)) + { + value = envValue; + return true; + } + + if (ConfigFile.TryRead(root, name, type, out var configValue)) + { + value = configValue; + return true; + } + + if (Registry.TryRead(root, name, type, out var regValue)) + { + value = regValue; + return true; + } + + value = null; + return false; + } + public static T Read(this ISettingsReader instance, string name, T defaultValue = default) { - return (T)instance.Read("ServiceControl", name, typeof(T), defaultValue); + return (T)instance.Read(Namespace, name, typeof(T), defaultValue); } + + public static object ConvertFrom(object sourceValue, Type destinationType) + { + var converter = TypeDescriptor.GetConverter(destinationType); + object value = converter.ConvertFrom(sourceValue); + return value; + } + } } \ No newline at end of file From 9ab432ead50ca293412dda70b58e0824ae5b0f3c Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Wed, 30 Aug 2023 21:19:46 +0200 Subject: [PATCH 07/33] =?UTF-8?q?=E2=9A=9C=EF=B8=8F=20Improved=20readabili?= =?UTF-8?q?ty=20of=20free/total=20storage=20log=20entries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CustomChecks/CheckFreeDiskSpace.cs | 2 +- .../CustomChecks/CheckMinimumStorageRequiredForIngestion.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckFreeDiskSpace.cs b/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckFreeDiskSpace.cs index 6211bf9d8c..14fb9ae8d9 100644 --- a/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckFreeDiskSpace.cs +++ b/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckFreeDiskSpace.cs @@ -34,7 +34,7 @@ public override Task PerformCheck() if (Logger.IsDebugEnabled) { - Logger.Debug($"Free space: {availableFreeSpace} | Total: {totalSpace} | Percent remaining {percentRemaining:P0}"); + Logger.Debug($"Free space: {availableFreeSpace:N0}B | Total: {totalSpace:N0}B | Percent remaining {percentRemaining:P1}"); } return percentRemaining > percentageThreshold diff --git a/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs b/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs index ee8e8df469..a696a1d45d 100644 --- a/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs +++ b/src/ServiceControl.Persistence.RavenDb/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs @@ -41,7 +41,7 @@ public override Task PerformCheck() if (Logger.IsDebugEnabled) { - Logger.Debug($"Free space: {availableFreeSpace} | Total: {totalSpace} | Percent remaining {percentRemaining:P0}"); + Logger.Debug($"Free space: {availableFreeSpace:N0}B | Total: {totalSpace:N0}B | Percent remaining {percentRemaining:P1}"); } if (percentRemaining > percentageThreshold) From c39879a4e77263a0e5ddf86a0199295ebe07628e Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Wed, 30 Aug 2023 21:34:48 +0200 Subject: [PATCH 08/33] `PersistenceFactory.Create` now only takes a dependency on `Settings` which has its `PersisterSettings property change from Dictionary to IPersisterSettings. This allows for a persister specific implementation type to be passed as value. This means that in core for propagating settings only the `Settings` object is needed. The `PersistenceFactory` will create a persister specific settings implementation via the resolved persister configuration type if core settings yet. This makes it easy to initialized settings type-safe in tests. --- .../ServiceControlComponentRunner.cs | 7 +- .../ExpiredDocumentsCleanerBundle.cs | 4 +- .../InternalsVisibleTo.cs | 1 + .../RavenDBPersisterSettings.cs | 5 +- .../RavenDbPersistenceConfiguration.cs | 87 +++++++++++++------ .../IPersistenceConfiguration.cs | 3 +- src/ServiceControl/Bootstrapper.cs | 2 +- .../Infrastructure/Settings/Settings.cs | 4 +- src/ServiceControl/MaintenanceBootstrapper.cs | 2 +- .../PersistenceConfigurationFactory.cs | 23 ----- .../Persistence/PersistenceFactory.cs | 45 ++++++++++ .../PersistenceHostBuilderExtensions.cs | 5 +- src/ServiceControl/SetupBootstrapper.cs | 2 +- 13 files changed, 125 insertions(+), 65 deletions(-) delete mode 100644 src/ServiceControl/Persistence/PersistenceConfigurationFactory.cs create mode 100644 src/ServiceControl/Persistence/PersistenceFactory.cs diff --git a/src/ServiceControl.MultiInstance.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs b/src/ServiceControl.MultiInstance.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs index 993bb7da20..44c1cce4dc 100644 --- a/src/ServiceControl.MultiInstance.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs +++ b/src/ServiceControl.MultiInstance.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs @@ -84,11 +84,10 @@ async Task InitializeServiceControl(ScenarioContext context, int instancePort, i var settings = new Settings(instanceName, transportToUse.TypeName, dataStoreConfiguration.DataStoreTypeName) { Port = instancePort, - PersisterSpecificSettings = new Dictionary() + PersisterSpecificSettings = new RavenDBPersisterSettings { - { "HostName", "localhost" }, - { "RavenDB35/RunInMemory", "true" }, - { "DatabaseMaintenancePort", maintenancePort.ToString() } + RunInMemory = true, + DatabaseMaintenancePort = maintenancePort }, ForwardErrorMessages = false, TransportType = transportToUse.TypeName, diff --git a/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs b/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs index a2f78d3495..2f81489214 100644 --- a/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs +++ b/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs @@ -115,8 +115,8 @@ public int ExpirationProcessBatchSize } } - const int ExpirationProcessTimerInSecondsDefault = 600; - const int ExpirationProcessBatchSizeDefault = 65512; + public const int ExpirationProcessTimerInSecondsDefault = 600; + public const int ExpirationProcessBatchSizeDefault = 65512; const int ExpirationProcessBatchSizeMinimum = 10240; readonly RavenDBPersisterSettings settings = RavenBootstrapper.Settings; diff --git a/src/ServiceControl.Persistence.RavenDb/InternalsVisibleTo.cs b/src/ServiceControl.Persistence.RavenDb/InternalsVisibleTo.cs index f923d470ad..cbd15879aa 100644 --- a/src/ServiceControl.Persistence.RavenDb/InternalsVisibleTo.cs +++ b/src/ServiceControl.Persistence.RavenDb/InternalsVisibleTo.cs @@ -4,3 +4,4 @@ [assembly: InternalsVisibleTo("ServiceControl.PersistenceTests")] [assembly: InternalsVisibleTo("ServiceControl.Persistence.Tests.RavenDb")] [assembly: InternalsVisibleTo("ServiceControl.AcceptanceTests.RavenDB")] +[assembly: InternalsVisibleTo("ServiceControl.MultiInstance.AcceptanceTests")] diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs b/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs index 1e23b2957b..0903c1a8b5 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs @@ -1,4 +1,5 @@ using System; +using ServiceControl.Infrastructure.RavenDB.Expiration; using ServiceControl.Operations; using ServiceControl.Persistence; @@ -8,8 +9,8 @@ class RavenDBPersisterSettings : IPersistenceSettings public string HostName { get; set; } = "localhost"; public int DatabaseMaintenancePort { get; set; } = 55554; public bool ExposeRavenDB { get; set; } - public int ExpirationProcessTimerInSeconds { get; set; } - public int ExpirationProcessBatchSize { get; set; } + public int ExpirationProcessTimerInSeconds { get; set; } = ExpiredDocumentsCleanerBundle.ExpirationProcessTimerInSecondsDefault; + public int ExpirationProcessBatchSize { get; set; } = ExpiredDocumentsCleanerBundle.ExpirationProcessBatchSizeDefault; public bool RunCleanupBundle { get; set; } public bool RunInMemory { get; set; } public int MinimumStorageLeftRequiredForIngestion { get; set; } = CheckMinimumStorageRequiredForIngestion.MinimumStorageLeftRequiredForIngestionDefault; diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs index a584b531b0..166bacbce0 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs @@ -2,6 +2,7 @@ { using System; using Raven.Client.Embedded; + using ServiceControl.Infrastructure.RavenDB.Expiration; using ServiceControl.Operations; class RavenDbPersistenceConfiguration : IPersistenceConfiguration @@ -13,10 +14,40 @@ class RavenDbPersistenceConfiguration : IPersistenceConfiguration const string ExternalIntegrationsDispatchingBatchSizeKey = "ExternalIntegrationsDispatchingBatchSize"; const string MaintenanceModeKey = "MaintenanceMode"; - public IPersistence Create(Func readSetting) + public IPersistenceSettings CreateSettings(Func tryReadSetting) { - T GetSetting(string key) => (T)readSetting(key, typeof(T)); + T GetRequiredSetting(string key) + { + var (exists, value) = tryReadSetting(key, typeof(T)); + + if (exists) + { + return (T)value; + } + + throw new Exception($"Setting {key} of type {typeof(T)} is required"); + } + + T GetSetting(string key, T defaultValue) + { + var (exists, value) = tryReadSetting(key, typeof(T)); + + if (exists) + { + return (T)value; + } + else + { + return defaultValue; + } + } + + /* + + + + */ //TODO: In core previously this happened with the settings: @@ -34,41 +65,45 @@ public IPersistence Create(Func readSetting) // } // } - - - var settings = new RavenDBPersisterSettings() + var settings = new RavenDBPersisterSettings { - DatabasePath = GetSetting(RavenBootstrapper.DatabasePathKey), - HostName = GetSetting(RavenBootstrapper.HostNameKey), - DatabaseMaintenancePort = GetSetting(RavenBootstrapper.DatabaseMaintenancePortKey), - ExposeRavenDB = GetSetting(RavenBootstrapper.ExposeRavenDBKey), - ExpirationProcessTimerInSeconds = GetSetting(RavenBootstrapper.ExpirationProcessTimerInSecondsKey), - ExpirationProcessBatchSize = GetSetting(RavenBootstrapper.ExpirationProcessBatchSizeKey), - RunCleanupBundle = GetSetting(RavenBootstrapper.RunCleanupBundleKey), - RunInMemory = GetSetting(RavenBootstrapper.RunInMemoryKey), - MinimumStorageLeftRequiredForIngestion = GetSetting(RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey), - DataSpaceRemainingThreshold = GetSetting(DataSpaceRemainingThresholdKey), - ErrorRetentionPeriod = GetSetting(ErrorRetentionPeriodKey), - EventsRetentionPeriod = GetSetting(EventsRetentionPeriodKey), - AuditRetentionPeriod = GetSetting(AuditRetentionPeriodKey), - ExternalIntegrationsDispatchingBatchSize = GetSetting(ExternalIntegrationsDispatchingBatchSizeKey), - MaintenanceMode = GetSetting(MaintenanceModeKey), + DatabasePath = GetSetting(RavenBootstrapper.DatabasePathKey, default), + HostName = GetSetting(RavenBootstrapper.HostNameKey, "localhost"), + DatabaseMaintenancePort = GetSetting(RavenBootstrapper.DatabaseMaintenancePortKey, default), + ExposeRavenDB = GetSetting(RavenBootstrapper.ExposeRavenDBKey, false), + ExpirationProcessTimerInSeconds = GetSetting(RavenBootstrapper.ExpirationProcessTimerInSecondsKey, ExpiredDocumentsCleanerBundle.ExpirationProcessTimerInSecondsDefault), + ExpirationProcessBatchSize = GetSetting(RavenBootstrapper.ExpirationProcessBatchSizeKey, ExpiredDocumentsCleanerBundle.ExpirationProcessBatchSizeDefault), + RunCleanupBundle = GetSetting(RavenBootstrapper.RunCleanupBundleKey, true), + RunInMemory = GetSetting(RavenBootstrapper.RunInMemoryKey, false), + MinimumStorageLeftRequiredForIngestion = GetSetting(RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey, CheckMinimumStorageRequiredForIngestion.MinimumStorageLeftRequiredForIngestionDefault), + DataSpaceRemainingThreshold = GetSetting(DataSpaceRemainingThresholdKey, CheckFreeDiskSpace.DataSpaceRemainingThresholdDefault), + ErrorRetentionPeriod = GetRequiredSetting(ErrorRetentionPeriodKey), + EventsRetentionPeriod = GetSetting(EventsRetentionPeriodKey, TimeSpan.FromDays(14)), + AuditRetentionPeriod = GetSetting(AuditRetentionPeriodKey, TimeSpan.Zero), + ExternalIntegrationsDispatchingBatchSize = GetSetting(ExternalIntegrationsDispatchingBatchSizeKey, 100), + MaintenanceMode = GetSetting(MaintenanceModeKey, false), }; CheckFreeDiskSpace.Validate(settings); CheckMinimumStorageRequiredForIngestion.Validate(settings); - - return Create(settings); + return settings; } - internal IPersistence Create(RavenDBPersisterSettings settings) + //public IPersistenceSettingstence Create(Func tryReadSetting) + //{ + // var settings = CreateSettings(tryReadSetting); + // return Create(settings); + //} + + public IPersistence Create(IPersistenceSettings settings) { + var specificSettings = (RavenDBPersisterSettings)settings; + var documentStore = new EmbeddableDocumentStore(); - RavenBootstrapper.Configure(documentStore, settings); + RavenBootstrapper.Configure(documentStore, specificSettings); var ravenStartup = new RavenStartup(); - - return new RavenDbPersistence(settings, documentStore, ravenStartup); + return new RavenDbPersistence(specificSettings, documentStore, ravenStartup); } } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/IPersistenceConfiguration.cs b/src/ServiceControl.Persistence/IPersistenceConfiguration.cs index 820c174a44..1160cee2ba 100644 --- a/src/ServiceControl.Persistence/IPersistenceConfiguration.cs +++ b/src/ServiceControl.Persistence/IPersistenceConfiguration.cs @@ -4,6 +4,7 @@ public interface IPersistenceConfiguration { - IPersistence Create(Func readSetting); + IPersistenceSettings CreateSettings(Func tryReadSetting); + IPersistence Create(IPersistenceSettings settings); } } \ No newline at end of file diff --git a/src/ServiceControl/Bootstrapper.cs b/src/ServiceControl/Bootstrapper.cs index 4864aa61c6..db63d46609 100644 --- a/src/ServiceControl/Bootstrapper.cs +++ b/src/ServiceControl/Bootstrapper.cs @@ -112,7 +112,7 @@ void CreateHost() services.AddSingleton(sp => HttpClientFactory); }) .UseLicenseCheck() - .SetupPersistence(settings.PersistenceType) + .SetupPersistence(settings) .UseMetrics(settings.PrintMetrics) .UseNServiceBus(context => { diff --git a/src/ServiceControl/Infrastructure/Settings/Settings.cs b/src/ServiceControl/Infrastructure/Settings/Settings.cs index bc783ea666..1c79c1a8a2 100644 --- a/src/ServiceControl/Infrastructure/Settings/Settings.cs +++ b/src/ServiceControl/Infrastructure/Settings/Settings.cs @@ -10,6 +10,7 @@ namespace ServiceBus.Management.Infrastructure.Settings using NServiceBus.Logging; using NServiceBus.Transport; using ServiceControl.Infrastructure.WebApi; + using ServiceControl.Persistence; using ServiceControl.Transports; public class Settings @@ -102,9 +103,8 @@ public string RootUrl public string LicenseFileText { get; set; } - public Dictionary PersisterSpecificSettings { get; set; } = new Dictionary(); + public IPersistenceSettings PersisterSpecificSettings { get; set; } - public bool ExposeRavenDB => SettingsReader.Read("ExposeRavenDB"); // TODO: Should not be in Core but in the persister implementation public bool PrintMetrics => SettingsReader.Read("PrintMetrics"); public string Hostname => SettingsReader.Read("Hostname", "localhost"); public string VirtualDirectory => SettingsReader.Read("VirtualDirectory", string.Empty); diff --git a/src/ServiceControl/MaintenanceBootstrapper.cs b/src/ServiceControl/MaintenanceBootstrapper.cs index df6c3e5e3a..4927df2d46 100644 --- a/src/ServiceControl/MaintenanceBootstrapper.cs +++ b/src/ServiceControl/MaintenanceBootstrapper.cs @@ -12,7 +12,7 @@ static class MaintenanceBootstrapper public static async Task Run(HostArguments args, Settings settings) { var hostBuilder = new HostBuilder() - .SetupPersistence(settings.PersistenceType); + .SetupPersistence(settings); if (args.RunAsWindowsService) { diff --git a/src/ServiceControl/Persistence/PersistenceConfigurationFactory.cs b/src/ServiceControl/Persistence/PersistenceConfigurationFactory.cs deleted file mode 100644 index 0036e5da9d..0000000000 --- a/src/ServiceControl/Persistence/PersistenceConfigurationFactory.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace ServiceControl.Persistence -{ - using System; - using ServiceBus.Management.Infrastructure.Settings; - - static class PersistenceFactory - { - public static IPersistence Create(string persistenceType, Func readSetting = default) - { - try - { - var foundPersistenceType = PersistenceManifestLibrary.Find(persistenceType); - var customizationType = Type.GetType(foundPersistenceType, true); - var persistenceConfiguration = (IPersistenceConfiguration)Activator.CreateInstance(customizationType); - return persistenceConfiguration.Create(readSetting ?? SettingsReader.Read); - } - catch (Exception e) - { - throw new Exception($"Could not load persistence customization type {persistenceType}.", e); - } - } - } -} \ No newline at end of file diff --git a/src/ServiceControl/Persistence/PersistenceFactory.cs b/src/ServiceControl/Persistence/PersistenceFactory.cs new file mode 100644 index 0000000000..cf94b47ef5 --- /dev/null +++ b/src/ServiceControl/Persistence/PersistenceFactory.cs @@ -0,0 +1,45 @@ +namespace ServiceControl.Persistence +{ + using System; + using ServiceBus.Management.Infrastructure.Settings; + + static class PersistenceFactory + { + public static IPersistence Create(Settings settings) + { + var persistenceConfiguration = CreatePersistenceConfiguration(settings.PersistenceType); + + (bool, object) TryRead(string name, Type type) + { + var exists = SettingsReader.TryRead(name, type: type, out object value); + return (exists, value); + }; + + var persistenceSettings = settings.PersisterSpecificSettings; + + if (persistenceSettings == null) + { + persistenceSettings = persistenceConfiguration.CreateSettings(TryRead); + settings.PersisterSpecificSettings = persistenceSettings; + } + + var persistence = persistenceConfiguration.Create(persistenceSettings); + return persistence; + } + + static IPersistenceConfiguration CreatePersistenceConfiguration(string persistenceType) + { + try + { + var foundPersistenceType = PersistenceManifestLibrary.Find(persistenceType); + var customizationType = Type.GetType(foundPersistenceType, true); + var persistenceConfiguration = (IPersistenceConfiguration)Activator.CreateInstance(customizationType); + return persistenceConfiguration; + } + catch (Exception e) + { + throw new Exception($"Could not load persistence customization type {persistenceType}.", e); + } + } + } +} \ No newline at end of file diff --git a/src/ServiceControl/Persistence/PersistenceHostBuilderExtensions.cs b/src/ServiceControl/Persistence/PersistenceHostBuilderExtensions.cs index 98def4c67a..d3ee31e963 100644 --- a/src/ServiceControl/Persistence/PersistenceHostBuilderExtensions.cs +++ b/src/ServiceControl/Persistence/PersistenceHostBuilderExtensions.cs @@ -2,12 +2,13 @@ namespace ServiceControl.Persistence { using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; + using ServiceBus.Management.Infrastructure.Settings; static class PersistenceHostBuilderExtensions { - public static IHostBuilder SetupPersistence(this IHostBuilder hostBuilder, string persistenceType) + public static IHostBuilder SetupPersistence(this IHostBuilder hostBuilder, Settings settings) { - var persistence = PersistenceFactory.Create(persistenceType); + var persistence = PersistenceFactory.Create(settings); hostBuilder.ConfigureServices(serviceCollection => { diff --git a/src/ServiceControl/SetupBootstrapper.cs b/src/ServiceControl/SetupBootstrapper.cs index 1bbe70af9d..dffadfa0aa 100644 --- a/src/ServiceControl/SetupBootstrapper.cs +++ b/src/ServiceControl/SetupBootstrapper.cs @@ -35,7 +35,7 @@ public async Task Run(string username) await installationTask(); } - var persistence = PersistenceFactory.Create(settings.PersistenceType); + var persistence = PersistenceFactory.Create(settings); var installer = persistence.CreateInstaller(); From 2e56ab72952407ef0d2a853969a3744c2b4b77fc Mon Sep 17 00:00:00 2001 From: Tomek Masternak Date: Thu, 31 Aug 2023 11:54:39 +0200 Subject: [PATCH 09/33] small tweaks --- .../Persistence/PersistenceFactory.cs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/ServiceControl/Persistence/PersistenceFactory.cs b/src/ServiceControl/Persistence/PersistenceFactory.cs index cf94b47ef5..975266f73d 100644 --- a/src/ServiceControl/Persistence/PersistenceFactory.cs +++ b/src/ServiceControl/Persistence/PersistenceFactory.cs @@ -9,21 +9,19 @@ public static IPersistence Create(Settings settings) { var persistenceConfiguration = CreatePersistenceConfiguration(settings.PersistenceType); - (bool, object) TryRead(string name, Type type) + //HINT: This is false when executed from acceptance tests + if (settings.PersisterSpecificSettings == null) { - var exists = SettingsReader.TryRead(name, type: type, out object value); - return (exists, value); - }; + (bool, object) TryRead(string name, Type type) + { + var exists = SettingsReader.TryRead(name, type: type, out object value); + return (exists, value); + }; - var persistenceSettings = settings.PersisterSpecificSettings; - - if (persistenceSettings == null) - { - persistenceSettings = persistenceConfiguration.CreateSettings(TryRead); - settings.PersisterSpecificSettings = persistenceSettings; + settings.PersisterSpecificSettings = persistenceConfiguration.CreateSettings(TryRead); } - var persistence = persistenceConfiguration.Create(persistenceSettings); + var persistence = persistenceConfiguration.Create(settings.PersisterSpecificSettings); return persistence; } From 7f53ccd6dcef4e40e97cc00bbb7ad7ab77feece1 Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Thu, 31 Aug 2023 11:00:59 +0200 Subject: [PATCH 10/33] =?UTF-8?q?=E2=9A=9C=EF=B8=8FCode=20readability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TestSupport/AcceptanceTest.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ServiceControl.AcceptanceTests/TestSupport/AcceptanceTest.cs b/src/ServiceControl.AcceptanceTests/TestSupport/AcceptanceTest.cs index 30d7dfbfbd..2410c9ab75 100644 --- a/src/ServiceControl.AcceptanceTests/TestSupport/AcceptanceTest.cs +++ b/src/ServiceControl.AcceptanceTests/TestSupport/AcceptanceTest.cs @@ -72,7 +72,16 @@ public async Task Setup() await StorageConfiguration.Configure(); - serviceControlRunnerBehavior = new ServiceControlComponentBehavior(TransportIntegration, StorageConfiguration, s => SetSettings(s), s => CustomConfiguration(s), hb => CustomizeHostBuilder(hb)); + serviceControlRunnerBehavior = new ServiceControlComponentBehavior( + TransportIntegration, + StorageConfiguration, + s => + { + SetSettings(s); + }, + s => CustomConfiguration(s), + hb => CustomizeHostBuilder(hb) + ); } [TearDown] From f35962bc7ee814e7025a252eb9c7e1a5c50fd28d Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Thu, 31 Aug 2023 12:59:08 +0200 Subject: [PATCH 11/33] =?UTF-8?q?=F0=9F=94=A8=20=20Removed=20settings=20fr?= =?UTF-8?q?om=20app=20config=20by=20extending=20settings=20constructor=20w?= =?UTF-8?q?ith=20optional=20arguments=20for=20these=20and=20have=20the=20t?= =?UTF-8?q?est=20runner=20pass=20explicit=20values=20for=20these=20removin?= =?UTF-8?q?g=20the=20need=20to=20copy=20settings=20runtime=20values=20back?= =?UTF-8?q?=20to=20ConfigurationManager.AppSettings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AcceptanceTestStorageConfiguration.cs | 16 +++++++++------- .../App.config | 3 --- .../ServiceControlComponentRunner.cs | 18 +++++------------- .../Infrastructure/Settings/Settings.cs | 12 +++++++++--- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/ServiceControl.AcceptanceTests.RavenDB/AcceptanceTestStorageConfiguration.cs b/src/ServiceControl.AcceptanceTests.RavenDB/AcceptanceTestStorageConfiguration.cs index c0ec329d4b..a8471d1370 100644 --- a/src/ServiceControl.AcceptanceTests.RavenDB/AcceptanceTestStorageConfiguration.cs +++ b/src/ServiceControl.AcceptanceTests.RavenDB/AcceptanceTestStorageConfiguration.cs @@ -1,23 +1,25 @@ namespace ServiceControl.AcceptanceTests { - using System.Collections.Generic; + using System; using System.Linq; using System.Net.NetworkInformation; using System.Threading.Tasks; using Persistence.RavenDb; + using ServiceBus.Management.Infrastructure.Settings; class AcceptanceTestStorageConfiguration { public string PersistenceType { get; protected set; } - public Task> CustomizeSettings() + public void CustomizeSettings(Settings settings) { - return Task.FromResult>(new Dictionary + settings.PersisterSpecificSettings = new RavenDBPersisterSettings { - { "RavenDB35/RunInMemory", bool.TrueString}, - { "DatabaseMaintenancePort", FindAvailablePort(33334).ToString()}, - { "HostName", "localhost" } - }); + RunInMemory = true, + DatabaseMaintenancePort = FindAvailablePort(33334), + DatabasePath = settings.DbPath, + ErrorRetentionPeriod = TimeSpan.FromDays(10), + }; } public Task Configure() diff --git a/src/ServiceControl.AcceptanceTests.RavenDB/App.config b/src/ServiceControl.AcceptanceTests.RavenDB/App.config index c2987b05e6..4149eda6a7 100644 --- a/src/ServiceControl.AcceptanceTests.RavenDB/App.config +++ b/src/ServiceControl.AcceptanceTests.RavenDB/App.config @@ -1,10 +1,7 @@  - - - diff --git a/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs b/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs index 97795026d1..59dc6f244e 100644 --- a/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs +++ b/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs @@ -76,13 +76,10 @@ async Task InitializeServiceControl(ScenarioContext context) // TODO: ⬇️ This is required for the PERSISTER implementation to actually retrieve the DBPath settings are persister isn't using this type-safe settings class var dbPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - ConfigurationManager.AppSettings.Set("ServiceControl/DBPath", dbPath); // TODO: Tests should not use static ConfigurationManager.AppSettings - ConfigurationManager.AppSettings.Set("ServiceControl/DatabaseMaintenancePort", "33434"); - ConfigurationManager.AppSettings.Set("ServiceControl/ExposeRavenDB", "False"); - // TODO: ⬆️ - var settings = new Settings(instanceName, transportToUse.TypeName, persistenceToUse.PersistenceType) + var settings = new Settings(instanceName, transportToUse.TypeName, persistenceToUse.PersistenceType, forwardErrorMessages: false, errorRetentionPeriod: TimeSpan.FromDays(10)) { + AllowMessageEditing = true, Port = instancePort, DatabaseMaintenancePort = maintenancePort, DbPath = dbPath, @@ -124,19 +121,14 @@ async Task InitializeServiceControl(ScenarioContext context) } return false; - } + }, }; + persistenceToUse.CustomizeSettings(settings); + setSettings(settings); Settings = settings; - var persisterSpecificSettings = await persistenceToUse.CustomizeSettings(); - - foreach (var persisterSpecificSetting in persisterSpecificSettings) - { - ConfigurationManager.AppSettings.Set($"ServiceControl/{persisterSpecificSetting.Key}", persisterSpecificSetting.Value); - } - using (new DiagnosticTimer($"Creating infrastructure for {instanceName}")) { diff --git a/src/ServiceControl/Infrastructure/Settings/Settings.cs b/src/ServiceControl/Infrastructure/Settings/Settings.cs index 1c79c1a8a2..a89add2497 100644 --- a/src/ServiceControl/Infrastructure/Settings/Settings.cs +++ b/src/ServiceControl/Infrastructure/Settings/Settings.cs @@ -15,7 +15,13 @@ namespace ServiceBus.Management.Infrastructure.Settings public class Settings { - public Settings(string serviceName = null, string transportType = null, string persisterType = null) + public Settings( + string serviceName = null, + string transportType = null, + string persisterType = null, + bool? forwardErrorMessages = default, + TimeSpan? errorRetentionPeriod = default + ) { ServiceName = serviceName; @@ -36,8 +42,8 @@ public Settings(string serviceName = null, string transportType = null, string p TransportType = transportType ?? SettingsReader.Read("TransportType"); PersistenceType = persisterType ?? SettingsReader.Read("PersistenceType"); AuditRetentionPeriod = GetAuditRetentionPeriod(); - ForwardErrorMessages = GetForwardErrorMessages(); - ErrorRetentionPeriod = GetErrorRetentionPeriod(); + ForwardErrorMessages = forwardErrorMessages ?? GetForwardErrorMessages(); + ErrorRetentionPeriod = errorRetentionPeriod ?? GetErrorRetentionPeriod(); EventsRetentionPeriod = GetEventRetentionPeriod(); Port = SettingsReader.Read("Port", 33333); DatabaseMaintenancePort = SettingsReader.Read("DatabaseMaintenancePort", 33334); // TODO: Should not be in Core but in the persister implementation From 986b35ef349db612d9e82c9948cdc8dac17f035f Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Thu, 31 Aug 2023 13:30:48 +0200 Subject: [PATCH 12/33] =?UTF-8?q?=F0=9F=A9=B9=20Fix=20test,=20specify=20re?= =?UTF-8?q?quired=20setting=20values?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ServiceControl.AcceptanceTests/RootControllerTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ServiceControl.AcceptanceTests/RootControllerTests.cs b/src/ServiceControl.AcceptanceTests/RootControllerTests.cs index 72d1ddfad6..b370736363 100644 --- a/src/ServiceControl.AcceptanceTests/RootControllerTests.cs +++ b/src/ServiceControl.AcceptanceTests/RootControllerTests.cs @@ -24,7 +24,7 @@ public async Task Should_gather_remote_data() { hostBuilder.ConfigureServices((hostBuilderContext, services) => { - services.AddSingleton(new Settings(serviceName) + services.AddSingleton(new Settings(serviceName, forwardErrorMessages: false, errorRetentionPeriod: TimeSpan.FromDays(10)) { RemoteInstances = new[] { From c455f9288677f1eb09a35ccc1b450f40da68ddab Mon Sep 17 00:00:00 2001 From: Tomek Masternak Date: Thu, 31 Aug 2023 14:49:06 +0200 Subject: [PATCH 13/33] fixing maintenance mode startup --- .../RavenDBPersisterSettings.cs | 3 +-- .../RavenDbPersistence.cs | 7 ++++++- .../RavenDbPersistenceConfiguration.cs | 4 ++-- .../IPersistenceConfiguration.cs | 4 ++-- .../{IPersistenceSettings.cs => PersistenceSettings.cs} | 3 ++- src/ServiceControl/Infrastructure/Settings/Settings.cs | 2 +- src/ServiceControl/Infrastructure/WebApi/RootController.cs | 4 ++-- src/ServiceControl/MaintenanceBootstrapper.cs | 2 +- src/ServiceControl/Persistence/PersistenceFactory.cs | 4 +++- .../Persistence/PersistenceHostBuilderExtensions.cs | 4 ++-- 10 files changed, 22 insertions(+), 15 deletions(-) rename src/ServiceControl.Persistence/{IPersistenceSettings.cs => PersistenceSettings.cs} (63%) diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs b/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs index 0903c1a8b5..0b288b1314 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs @@ -3,7 +3,7 @@ using ServiceControl.Operations; using ServiceControl.Persistence; -class RavenDBPersisterSettings : IPersistenceSettings +class RavenDBPersisterSettings : PersistenceSettings { public string DatabasePath { get; set; } public string HostName { get; set; } = "localhost"; @@ -19,5 +19,4 @@ class RavenDBPersisterSettings : IPersistenceSettings public TimeSpan EventsRetentionPeriod { get; set; } public TimeSpan? AuditRetentionPeriod { get; set; } public int ExternalIntegrationsDispatchingBatchSize { get; set; } = 100; - public bool MaintenanceMode { get; set; } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs index 0672a5739f..73c6ce72b4 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs @@ -28,8 +28,13 @@ public RavenDbPersistence(RavenDBPersisterSettings settings, EmbeddableDocumentS public void Configure(IServiceCollection serviceCollection) { + if (settings.MaintenanceMode) + { + return; + } + serviceCollection.AddSingleton(settings); - serviceCollection.AddSingleton(settings); + serviceCollection.AddSingleton(settings); serviceCollection.AddSingleton(documentStore); serviceCollection.AddSingleton(); diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs index 166bacbce0..5a4220c7b3 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs @@ -14,7 +14,7 @@ class RavenDbPersistenceConfiguration : IPersistenceConfiguration const string ExternalIntegrationsDispatchingBatchSizeKey = "ExternalIntegrationsDispatchingBatchSize"; const string MaintenanceModeKey = "MaintenanceMode"; - public IPersistenceSettings CreateSettings(Func tryReadSetting) + public PersistenceSettings CreateSettings(Func tryReadSetting) { T GetRequiredSetting(string key) { @@ -95,7 +95,7 @@ T GetSetting(string key, T defaultValue) // return Create(settings); //} - public IPersistence Create(IPersistenceSettings settings) + public IPersistence Create(PersistenceSettings settings) { var specificSettings = (RavenDBPersisterSettings)settings; diff --git a/src/ServiceControl.Persistence/IPersistenceConfiguration.cs b/src/ServiceControl.Persistence/IPersistenceConfiguration.cs index 1160cee2ba..2f5645fbcd 100644 --- a/src/ServiceControl.Persistence/IPersistenceConfiguration.cs +++ b/src/ServiceControl.Persistence/IPersistenceConfiguration.cs @@ -4,7 +4,7 @@ public interface IPersistenceConfiguration { - IPersistenceSettings CreateSettings(Func tryReadSetting); - IPersistence Create(IPersistenceSettings settings); + PersistenceSettings CreateSettings(Func tryReadSetting); + IPersistence Create(PersistenceSettings settings); } } \ No newline at end of file diff --git a/src/ServiceControl.Persistence/IPersistenceSettings.cs b/src/ServiceControl.Persistence/PersistenceSettings.cs similarity index 63% rename from src/ServiceControl.Persistence/IPersistenceSettings.cs rename to src/ServiceControl.Persistence/PersistenceSettings.cs index ee2cd352f7..cb9290844f 100644 --- a/src/ServiceControl.Persistence/IPersistenceSettings.cs +++ b/src/ServiceControl.Persistence/PersistenceSettings.cs @@ -3,7 +3,8 @@ /// /// Marker interface used to serialize persister settings in REST API /// - public interface IPersistenceSettings + public abstract class PersistenceSettings { + public bool MaintenanceMode { get; set; } } } \ No newline at end of file diff --git a/src/ServiceControl/Infrastructure/Settings/Settings.cs b/src/ServiceControl/Infrastructure/Settings/Settings.cs index a89add2497..50b33bc537 100644 --- a/src/ServiceControl/Infrastructure/Settings/Settings.cs +++ b/src/ServiceControl/Infrastructure/Settings/Settings.cs @@ -109,7 +109,7 @@ public string RootUrl public string LicenseFileText { get; set; } - public IPersistenceSettings PersisterSpecificSettings { get; set; } + public PersistenceSettings PersisterSpecificSettings { get; set; } public bool PrintMetrics => SettingsReader.Read("PrintMetrics"); public string Hostname => SettingsReader.Read("Hostname", "localhost"); diff --git a/src/ServiceControl/Infrastructure/WebApi/RootController.cs b/src/ServiceControl/Infrastructure/WebApi/RootController.cs index af1b4de593..e671025893 100644 --- a/src/ServiceControl/Infrastructure/WebApi/RootController.cs +++ b/src/ServiceControl/Infrastructure/WebApi/RootController.cs @@ -15,7 +15,7 @@ class RootController : ApiController { - public RootController(ActiveLicense license, LoggingSettings loggingSettings, Settings settings, IPersistenceSettings persistenceSettings, Func httpClientFactory) + public RootController(ActiveLicense license, LoggingSettings loggingSettings, Settings settings, PersistenceSettings persistenceSettings, Func httpClientFactory) { this.settings = settings; this.persistenceSettings = persistenceSettings; @@ -157,7 +157,7 @@ public async Task RemoteConfig() readonly LoggingSettings loggingSettings; readonly ActiveLicense license; readonly Settings settings; - readonly IPersistenceSettings persistenceSettings; + readonly PersistenceSettings persistenceSettings; readonly Func httpClientFactory; static readonly JsonSerializer jsonSerializer = JsonSerializer.Create(JsonNetSerializerSettings.CreateDefault()); diff --git a/src/ServiceControl/MaintenanceBootstrapper.cs b/src/ServiceControl/MaintenanceBootstrapper.cs index 4927df2d46..5ac2ad228d 100644 --- a/src/ServiceControl/MaintenanceBootstrapper.cs +++ b/src/ServiceControl/MaintenanceBootstrapper.cs @@ -12,7 +12,7 @@ static class MaintenanceBootstrapper public static async Task Run(HostArguments args, Settings settings) { var hostBuilder = new HostBuilder() - .SetupPersistence(settings); + .SetupPersistence(settings, maintenanceMode: true); if (args.RunAsWindowsService) { diff --git a/src/ServiceControl/Persistence/PersistenceFactory.cs b/src/ServiceControl/Persistence/PersistenceFactory.cs index 975266f73d..72d7311169 100644 --- a/src/ServiceControl/Persistence/PersistenceFactory.cs +++ b/src/ServiceControl/Persistence/PersistenceFactory.cs @@ -5,7 +5,7 @@ namespace ServiceControl.Persistence static class PersistenceFactory { - public static IPersistence Create(Settings settings) + public static IPersistence Create(Settings settings, bool maintenanceMode = false) { var persistenceConfiguration = CreatePersistenceConfiguration(settings.PersistenceType); @@ -21,6 +21,8 @@ public static IPersistence Create(Settings settings) settings.PersisterSpecificSettings = persistenceConfiguration.CreateSettings(TryRead); } + settings.PersisterSpecificSettings.MaintenanceMode = maintenanceMode; + var persistence = persistenceConfiguration.Create(settings.PersisterSpecificSettings); return persistence; } diff --git a/src/ServiceControl/Persistence/PersistenceHostBuilderExtensions.cs b/src/ServiceControl/Persistence/PersistenceHostBuilderExtensions.cs index d3ee31e963..ef34ba16d2 100644 --- a/src/ServiceControl/Persistence/PersistenceHostBuilderExtensions.cs +++ b/src/ServiceControl/Persistence/PersistenceHostBuilderExtensions.cs @@ -6,9 +6,9 @@ namespace ServiceControl.Persistence static class PersistenceHostBuilderExtensions { - public static IHostBuilder SetupPersistence(this IHostBuilder hostBuilder, Settings settings) + public static IHostBuilder SetupPersistence(this IHostBuilder hostBuilder, Settings settings, bool maintenanceMode = false) { - var persistence = PersistenceFactory.Create(settings); + var persistence = PersistenceFactory.Create(settings, maintenanceMode); hostBuilder.ConfigureServices(serviceCollection => { From f4fb20e661b47dd52486c82052f716b20048f5f9 Mon Sep 17 00:00:00 2001 From: Tomek Masternak Date: Thu, 31 Aug 2023 15:43:56 +0200 Subject: [PATCH 14/33] maintenance mode test --- .../StartupModeTests.cs | 51 +++++++++++++++++++ .../Commands/ImportFailedErrorsCommand.cs | 3 +- .../Hosting/Commands/MaintCommand.cs | 13 ----- .../Commands/MaintenanceModeCommand.cs | 34 +++++++++++++ src/ServiceControl/Hosting/HostArguments.cs | 2 +- src/ServiceControl/MaintenanceBootstrapper.cs | 29 ++--------- 6 files changed, 93 insertions(+), 39 deletions(-) create mode 100644 src/ServiceControl.AcceptanceTests.RavenDB/StartupModeTests.cs delete mode 100644 src/ServiceControl/Hosting/Commands/MaintCommand.cs create mode 100644 src/ServiceControl/Hosting/Commands/MaintenanceModeCommand.cs diff --git a/src/ServiceControl.AcceptanceTests.RavenDB/StartupModeTests.cs b/src/ServiceControl.AcceptanceTests.RavenDB/StartupModeTests.cs new file mode 100644 index 0000000000..d26fee35ad --- /dev/null +++ b/src/ServiceControl.AcceptanceTests.RavenDB/StartupModeTests.cs @@ -0,0 +1,51 @@ +namespace ServiceControl.AcceptanceTests.RavenDB +{ + using System; + using System.Threading.Tasks; + using Hosting.Commands; + using NUnit.Framework; + using Particular.ServiceControl; + using Particular.ServiceControl.Hosting; + using Persistence.RavenDb; + using ServiceBus.Management.Infrastructure.Settings; + + class StartupModeTests : AcceptanceTest + { + Settings settings; + + [SetUp] + public void InitializeSettings() + { + settings = new Settings( + forwardErrorMessages: false, + errorRetentionPeriod: TimeSpan.FromDays(1), + persisterType: typeof(RavenDbPersistenceConfiguration).AssemblyQualifiedName) + { + PersisterSpecificSettings = new RavenDBPersisterSettings + { + ErrorRetentionPeriod = TimeSpan.FromDays(1), + RunInMemory = true + }, + TransportType = TransportIntegration.TypeName, + TransportConnectionString = TransportIntegration.ConnectionString + }; + } + + [Test] + public async Task CanRunMaintenanceMode() + { + var bootstrapper = new MaintenanceBootstrapper(settings); + + var host = bootstrapper.HostBuilder.Build(); + + await host.StartAsync(); + await host.StopAsync(); + } + + //[Test] + //public async Task CanRunImportFailedMessagesMode() + //{ + // await new ImportFailedErrorsCommand().Execute(new HostArguments(Array.Empty()), settings); + //} + } +} \ No newline at end of file diff --git a/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs b/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs index 53ba63a40c..e6fd33fed7 100644 --- a/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs +++ b/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs @@ -23,7 +23,6 @@ public override async Task Execute(HostArguments args, Settings settings) var busConfiguration = new EndpointConfiguration(settings.ServiceName); var assemblyScanner = busConfiguration.AssemblyScanner(); assemblyScanner.ExcludeAssemblies("ServiceControl.Plugin"); - var tokenSource = new CancellationTokenSource(); var loggingSettings = new LoggingSettings(settings.ServiceName, LogLevel.Info, LogLevel.Info); var bootstrapper = new Bootstrapper(settings, busConfiguration, loggingSettings); @@ -32,6 +31,8 @@ public override async Task Execute(HostArguments args, Settings settings) var importFailedErrors = host.Services.GetRequiredService(); + var tokenSource = new CancellationTokenSource(); + Console.CancelKeyPress += (sender, eventArgs) => { tokenSource.Cancel(); }; try diff --git a/src/ServiceControl/Hosting/Commands/MaintCommand.cs b/src/ServiceControl/Hosting/Commands/MaintCommand.cs deleted file mode 100644 index 5b4648765d..0000000000 --- a/src/ServiceControl/Hosting/Commands/MaintCommand.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace ServiceControl.Hosting.Commands -{ - using System.Threading.Tasks; - using Particular.ServiceControl; - using Particular.ServiceControl.Commands; - using Particular.ServiceControl.Hosting; - using ServiceBus.Management.Infrastructure.Settings; - - class MaintCommand : AbstractCommand - { - public override Task Execute(HostArguments args, Settings settings) => MaintenanceBootstrapper.Run(args, settings); - } -} \ No newline at end of file diff --git a/src/ServiceControl/Hosting/Commands/MaintenanceModeCommand.cs b/src/ServiceControl/Hosting/Commands/MaintenanceModeCommand.cs new file mode 100644 index 0000000000..4603ae810c --- /dev/null +++ b/src/ServiceControl/Hosting/Commands/MaintenanceModeCommand.cs @@ -0,0 +1,34 @@ +namespace ServiceControl.Hosting.Commands +{ + using System; + using System.Threading.Tasks; + using Microsoft.Extensions.Hosting; + using Particular.ServiceControl; + using Particular.ServiceControl.Commands; + using Particular.ServiceControl.Hosting; + using ServiceBus.Management.Infrastructure.Settings; + + class MaintenanceModeCommand : AbstractCommand + { + public override async Task Execute(HostArguments args, Settings settings) + { + var bootstrapper = new MaintenanceBootstrapper(settings); + var hostBuilder = bootstrapper.HostBuilder; + + if (args.RunAsWindowsService) + { + hostBuilder.UseWindowsService(); + } + else + { + await Console.Out.WriteLineAsync($"RavenDB is now accepting requests on {settings.DatabaseMaintenanceUrl}"); + await Console.Out.WriteLineAsync("RavenDB Maintenance Mode - Press CTRL+C to exit"); + + hostBuilder.UseConsoleLifetime(); + } + + await hostBuilder.Build().RunAsync(); + + } + } +} \ No newline at end of file diff --git a/src/ServiceControl/Hosting/HostArguments.cs b/src/ServiceControl/Hosting/HostArguments.cs index 8a3e68f3a6..4cfb3615e5 100644 --- a/src/ServiceControl/Hosting/HostArguments.cs +++ b/src/ServiceControl/Hosting/HostArguments.cs @@ -41,7 +41,7 @@ public HostArguments(string[] args) { Commands = new List { - typeof(MaintCommand) + typeof(MaintenanceModeCommand) }; executionMode = ExecutionMode.Maintenance; } diff --git a/src/ServiceControl/MaintenanceBootstrapper.cs b/src/ServiceControl/MaintenanceBootstrapper.cs index 5ac2ad228d..c1062f14a7 100644 --- a/src/ServiceControl/MaintenanceBootstrapper.cs +++ b/src/ServiceControl/MaintenanceBootstrapper.cs @@ -1,34 +1,15 @@ namespace Particular.ServiceControl { - using System; - using System.Threading.Tasks; using global::ServiceControl.Persistence; - using Hosting; using Microsoft.Extensions.Hosting; using ServiceBus.Management.Infrastructure.Settings; - static class MaintenanceBootstrapper + class MaintenanceBootstrapper { - public static async Task Run(HostArguments args, Settings settings) - { - var hostBuilder = new HostBuilder() - .SetupPersistence(settings, maintenanceMode: true); - - if (args.RunAsWindowsService) - { - hostBuilder.UseWindowsService(); - - await hostBuilder.Build().RunAsync(); - } - else - { - await Console.Out.WriteLineAsync($"RavenDB is now accepting requests on {settings.DatabaseMaintenanceUrl}"); - await Console.Out.WriteLineAsync("RavenDB Maintenance Mode - Press CTRL+C to exit"); + public IHostBuilder HostBuilder { get; set; } - hostBuilder.UseConsoleLifetime(); - - await hostBuilder.Build().RunAsync(); - } - } + public MaintenanceBootstrapper(Settings settings) => + HostBuilder = new HostBuilder() + .SetupPersistence(settings, maintenanceMode: true); } } \ No newline at end of file From 668e498d88e69fceed53d3fa32c8e50009bc027f Mon Sep 17 00:00:00 2001 From: Tomek Masternak Date: Fri, 1 Sep 2023 09:54:41 +0200 Subject: [PATCH 15/33] startup mode test --- .../StartupModeTests.cs | 32 +++++++++++++++---- .../Commands/ImportFailedErrorsCommand.cs | 13 ++++++-- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/ServiceControl.AcceptanceTests.RavenDB/StartupModeTests.cs b/src/ServiceControl.AcceptanceTests.RavenDB/StartupModeTests.cs index d26fee35ad..83b9c668da 100644 --- a/src/ServiceControl.AcceptanceTests.RavenDB/StartupModeTests.cs +++ b/src/ServiceControl.AcceptanceTests.RavenDB/StartupModeTests.cs @@ -3,11 +3,13 @@ using System; using System.Threading.Tasks; using Hosting.Commands; + using NServiceBus; using NUnit.Framework; using Particular.ServiceControl; using Particular.ServiceControl.Hosting; using Persistence.RavenDb; using ServiceBus.Management.Infrastructure.Settings; + using ServiceControl.AcceptanceTesting.InfrastructureConfig; class StartupModeTests : AcceptanceTest { @@ -16,6 +18,7 @@ class StartupModeTests : AcceptanceTest [SetUp] public void InitializeSettings() { + var transportIntegration = new ConfigureEndpointLearningTransport(); settings = new Settings( forwardErrorMessages: false, errorRetentionPeriod: TimeSpan.FromDays(1), @@ -26,8 +29,8 @@ public void InitializeSettings() ErrorRetentionPeriod = TimeSpan.FromDays(1), RunInMemory = true }, - TransportType = TransportIntegration.TypeName, - TransportConnectionString = TransportIntegration.ConnectionString + TransportType = transportIntegration.TypeName, + TransportConnectionString = transportIntegration.ConnectionString }; } @@ -42,10 +45,25 @@ public async Task CanRunMaintenanceMode() await host.StopAsync(); } - //[Test] - //public async Task CanRunImportFailedMessagesMode() - //{ - // await new ImportFailedErrorsCommand().Execute(new HostArguments(Array.Empty()), settings); - //} + [Test] + public async Task CanRunImportFailedMessagesMode() + { + await new TestableImportFailedErrorsCommand().Execute(new HostArguments(Array.Empty()), settings); + } + + class TestableImportFailedErrorsCommand : ImportFailedErrorsCommand + { + protected override EndpointConfiguration CreateEndpointConfiguration(Settings settings) + { + var configuration = base.CreateEndpointConfiguration(settings); + + //HINT: we want to exclude this assembly to prevent loading features that are part of the acceptance testing framework + var thisAssembly = new[] { typeof(StartupModeTests).Assembly.GetName().Name }; + + configuration.AssemblyScanner().ExcludeAssemblies(thisAssembly); + + return configuration; + } + } } } \ No newline at end of file diff --git a/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs b/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs index e6fd33fed7..2aa7f8db7d 100644 --- a/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs +++ b/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs @@ -20,9 +20,7 @@ public override async Task Execute(HostArguments args, Settings settings) settings.RunRetryProcessor = false; settings.DisableHealthChecks = true; - var busConfiguration = new EndpointConfiguration(settings.ServiceName); - var assemblyScanner = busConfiguration.AssemblyScanner(); - assemblyScanner.ExcludeAssemblies("ServiceControl.Plugin"); + EndpointConfiguration busConfiguration = CreateEndpointConfiguration(settings); var loggingSettings = new LoggingSettings(settings.ServiceName, LogLevel.Info, LogLevel.Info); var bootstrapper = new Bootstrapper(settings, busConfiguration, loggingSettings); @@ -48,5 +46,14 @@ public override async Task Execute(HostArguments args, Settings settings) await host.StopAsync(CancellationToken.None); } } + + protected virtual EndpointConfiguration CreateEndpointConfiguration(Settings settings) + { + var busConfiguration = new EndpointConfiguration(settings.ServiceName); + var assemblyScanner = busConfiguration.AssemblyScanner(); + assemblyScanner.ExcludeAssemblies("ServiceControl.Plugin"); + + return busConfiguration; + } } } \ No newline at end of file From 3d0941cb07816a9a37ade8f50c27664ead703248 Mon Sep 17 00:00:00 2001 From: Tomek Masternak Date: Fri, 1 Sep 2023 10:13:15 +0200 Subject: [PATCH 16/33] customcheck tests using persister settings directly --- ...When_critical_storage_threshold_reached.cs | 30 ++++--------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/src/ServiceControl.AcceptanceTests/Monitoring/CustomChecks/When_critical_storage_threshold_reached.cs b/src/ServiceControl.AcceptanceTests/Monitoring/CustomChecks/When_critical_storage_threshold_reached.cs index ef84335a9c..b622d5e300 100644 --- a/src/ServiceControl.AcceptanceTests/Monitoring/CustomChecks/When_critical_storage_threshold_reached.cs +++ b/src/ServiceControl.AcceptanceTests/Monitoring/CustomChecks/When_critical_storage_threshold_reached.cs @@ -1,13 +1,10 @@ -namespace ServiceControl.AcceptanceTests.Monitoring.CustomChecks +namespace ServiceControl.AcceptanceTests.Monitoring.CustomChecks { using System; using System.Linq; - using System.Threading; using System.Threading.Tasks; using AcceptanceTesting; using MessageFailures.Api; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Hosting; using NServiceBus; using NServiceBus.AcceptanceTesting; using NUnit.Framework; @@ -27,27 +24,11 @@ public void SetupIngestion() }; } - class PersisterSettingsRetriever : IHostedService - { - static RavenDBPersisterSettings _persistenceSettings; - - public PersisterSettingsRetriever(RavenDBPersisterSettings persistenceSettings, int value) - { - _persistenceSettings = persistenceSettings; - SetMinimumStorageLeftForIngestion(value); - } - - public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask; - public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; - - public static void SetMinimumStorageLeftForIngestion(int value) => _persistenceSettings.MinimumStorageLeftRequiredForIngestion = value; // TODO: Although this is already better we should not need to host trick to access RavenDBPersisterSettings - } + RavenDBPersisterSettings PersisterSettings => (RavenDBPersisterSettings)Settings.PersisterSpecificSettings; [Test] public async Task Should_stop_ingestion() { - CustomizeHostBuilder = hostBuilder => hostBuilder.ConfigureServices(services => services.AddHostedService(b => new PersisterSettingsRetriever(b.GetRequiredService(), 0))); - await Define() .WithEndpoint(b => b .When(context => @@ -55,7 +36,7 @@ await Define() return context.Logs.ToArray().Any(i => i.Message.StartsWith("Ensure started. Infrastructure started")); }, (_, __) => { - PersisterSettingsRetriever.SetMinimumStorageLeftForIngestion(100); + PersisterSettings.MinimumStorageLeftRequiredForIngestion = 100; return Task.CompletedTask; }) .When(context => @@ -73,7 +54,6 @@ await Define() [Test] public async Task Should_stop_ingestion_and_resume_when_more_space_is_available() { - CustomizeHostBuilder = hostBuilder => hostBuilder.ConfigureServices(services => services.AddHostedService(b => new PersisterSettingsRetriever(b.GetRequiredService(), 0))); var ingestionShutdown = false; await Define() @@ -85,7 +65,7 @@ await Define() "Ensure started. Infrastructure started")); }, (session, context) => { - PersisterSettingsRetriever.SetMinimumStorageLeftForIngestion(100); + PersisterSettings.MinimumStorageLeftRequiredForIngestion = 100; return Task.CompletedTask; }) .When(context => @@ -102,7 +82,7 @@ await Define() }) .When(c => ingestionShutdown, (session, context) => { - PersisterSettingsRetriever.SetMinimumStorageLeftForIngestion(0); + PersisterSettings.MinimumStorageLeftRequiredForIngestion = 0; return Task.CompletedTask; }) .DoNotFailOnErrorMessages()) From 643f729dc35264f677c50aaaf0d0c3b9b23a579e Mon Sep 17 00:00:00 2001 From: Tomek Masternak Date: Fri, 1 Sep 2023 10:13:34 +0200 Subject: [PATCH 17/33] bringing back the done condition in custom check tests --- .../CustomChecks/When_critical_storage_threshold_reached.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ServiceControl.AcceptanceTests/Monitoring/CustomChecks/When_critical_storage_threshold_reached.cs b/src/ServiceControl.AcceptanceTests/Monitoring/CustomChecks/When_critical_storage_threshold_reached.cs index b622d5e300..4acd4bdce5 100644 --- a/src/ServiceControl.AcceptanceTests/Monitoring/CustomChecks/When_critical_storage_threshold_reached.cs +++ b/src/ServiceControl.AcceptanceTests/Monitoring/CustomChecks/When_critical_storage_threshold_reached.cs @@ -1,4 +1,4 @@ -namespace ServiceControl.AcceptanceTests.Monitoring.CustomChecks +namespace ServiceControl.AcceptanceTests.Monitoring.CustomChecks { using System; using System.Linq; @@ -47,7 +47,7 @@ await Define() }, (bus, c) => bus.SendLocal(new MyMessage()) ) .DoNotFailOnErrorMessages()) - //.Done(async c => await this.TryGetSingle("/api/errors") == false) + .Done(async c => await this.TryGetSingle("/api/errors") == false) .Run(); } From 1df88254b859839451c7ab179dd6352a2491444e Mon Sep 17 00:00:00 2001 From: Tomek Masternak Date: Fri, 1 Sep 2023 10:21:14 +0200 Subject: [PATCH 18/33] removing obsolete todos --- .../TestSupport/ServiceControlComponentRunner.cs | 1 - .../Expiration/ExpiredDocumentsCleanerBundle.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs b/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs index 59dc6f244e..dc618c5b68 100644 --- a/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs +++ b/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs @@ -74,7 +74,6 @@ async Task InitializeServiceControl(ScenarioContext context) ConfigurationManager.AppSettings.Set("ServiceControl/TransportType", transportToUse.TypeName); - // TODO: ⬇️ This is required for the PERSISTER implementation to actually retrieve the DBPath settings are persister isn't using this type-safe settings class var dbPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); var settings = new Settings(instanceName, transportToUse.TypeName, persistenceToUse.PersistenceType, forwardErrorMessages: false, errorRetentionPeriod: TimeSpan.FromDays(10)) diff --git a/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs b/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs index 2f81489214..a7ba63e8d8 100644 --- a/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs +++ b/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs @@ -73,7 +73,6 @@ int ExpirationProcessTimerInSeconds { get { - //var expirationProcessTimerInSeconds = ExpirationProcessTimerInSecondsDefault; var expirationProcessTimerInSeconds = settings.ExpirationProcessTimerInSeconds; if (expirationProcessTimerInSeconds < 0) From 5b25df5c7b7bbedc99cc8fda0e16211b2f3e3fc1 Mon Sep 17 00:00:00 2001 From: Tomek Masternak Date: Fri, 1 Sep 2023 10:27:54 +0200 Subject: [PATCH 19/33] no generic persistence settings registration in DI --- .../RavenDbPersistence.cs | 1 - src/ServiceControl/Infrastructure/WebApi/RootController.cs | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs index 73c6ce72b4..bab32ac5bd 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs @@ -34,7 +34,6 @@ public void Configure(IServiceCollection serviceCollection) } serviceCollection.AddSingleton(settings); - serviceCollection.AddSingleton(settings); serviceCollection.AddSingleton(documentStore); serviceCollection.AddSingleton(); diff --git a/src/ServiceControl/Infrastructure/WebApi/RootController.cs b/src/ServiceControl/Infrastructure/WebApi/RootController.cs index e671025893..7c32151a81 100644 --- a/src/ServiceControl/Infrastructure/WebApi/RootController.cs +++ b/src/ServiceControl/Infrastructure/WebApi/RootController.cs @@ -15,10 +15,9 @@ class RootController : ApiController { - public RootController(ActiveLicense license, LoggingSettings loggingSettings, Settings settings, PersistenceSettings persistenceSettings, Func httpClientFactory) + public RootController(ActiveLicense license, LoggingSettings loggingSettings, Settings settings, Func httpClientFactory) { this.settings = settings; - this.persistenceSettings = persistenceSettings; this.license = license; this.loggingSettings = loggingSettings; this.httpClientFactory = httpClientFactory; @@ -87,7 +86,7 @@ public OkNegotiatedContentResult Config() settings.HttpDefaultConnectionLimit, settings.ExternalIntegrationsDispatchingBatchSize }, - PersistenceSettings = persistenceSettings, + PersistenceSettings = settings.PersisterSpecificSettings, Transport = new { settings.TransportType, @@ -157,7 +156,6 @@ public async Task RemoteConfig() readonly LoggingSettings loggingSettings; readonly ActiveLicense license; readonly Settings settings; - readonly PersistenceSettings persistenceSettings; readonly Func httpClientFactory; static readonly JsonSerializer jsonSerializer = JsonSerializer.Create(JsonNetSerializerSettings.CreateDefault()); From f56a39288dd6b337c1a573f6cf9aba77fc9c2156 Mon Sep 17 00:00:00 2001 From: Tomek Masternak Date: Fri, 1 Sep 2023 10:28:03 +0200 Subject: [PATCH 20/33] cleanup --- .../Expiration/ExpiredDocumentsCleanerBundle.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs b/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs index a7ba63e8d8..bcaf23c33b 100644 --- a/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs +++ b/src/ServiceControl.Persistence.RavenDb/Expiration/ExpiredDocumentsCleanerBundle.cs @@ -95,7 +95,6 @@ public int ExpirationProcessBatchSize { get { - //var expirationProcessBatchSize = ExpirationProcessBatchSizeDefault; var expirationProcessBatchSize = settings.ExpirationProcessBatchSize; if (expirationProcessBatchSize < 1) From 937f273d4c303c75ee01334f2342ae6ac3373c91 Mon Sep 17 00:00:00 2001 From: Tomek Masternak Date: Fri, 1 Sep 2023 10:29:26 +0200 Subject: [PATCH 21/33] cleanup --- .../RavenDbPersistenceConfiguration.cs | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs index 5a4220c7b3..e34046ca4f 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs @@ -42,29 +42,6 @@ T GetSetting(string key, T defaultValue) } } - /* - - - - - */ - - //TODO: In core previously this happened with the settings: - - // foreach (var keyPair in settings.PersisterSpecificSettings) - // { - // persistenceSettings.PersisterSpecificSettings[keyPair.Key] = keyPair.Value; - // } - - // foreach (var key in persistenceConfiguration.ConfigurationKeys) - // { - // var value = SettingsReader.Read("ServiceControl", key, null); - // if (!string.IsNullOrWhiteSpace(value)) - // { - // persistenceSettings.PersisterSpecificSettings[key] = value; - // } - // } - var settings = new RavenDBPersisterSettings { DatabasePath = GetSetting(RavenBootstrapper.DatabasePathKey, default), @@ -89,12 +66,6 @@ T GetSetting(string key, T defaultValue) return settings; } - //public IPersistenceSettingstence Create(Func tryReadSetting) - //{ - // var settings = CreateSettings(tryReadSetting); - // return Create(settings); - //} - public IPersistence Create(PersistenceSettings settings) { var specificSettings = (RavenDBPersisterSettings)settings; From a0e2fca62af41804d20256c96aeea8184ad181b1 Mon Sep 17 00:00:00 2001 From: Tomek Masternak Date: Fri, 1 Sep 2023 10:34:27 +0200 Subject: [PATCH 22/33] tweaking approval tests for custom checks --- .../API/APIApprovals.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ServiceControl.Persistence.Tests.RavenDb/API/APIApprovals.cs b/src/ServiceControl.Persistence.Tests.RavenDb/API/APIApprovals.cs index 85a08f1220..2b8ca6fe9b 100644 --- a/src/ServiceControl.Persistence.Tests.RavenDb/API/APIApprovals.cs +++ b/src/ServiceControl.Persistence.Tests.RavenDb/API/APIApprovals.cs @@ -40,7 +40,7 @@ static IEnumerable GetCustomChecks() new Settings(), new RavenDBPersisterSettings { - DatabasePath = "c:/" + DatabasePath = "%TEMP%" } }; From c863d94b343e8b0945b8ea05558970542669b091 Mon Sep 17 00:00:00 2001 From: Tomek Masternak Date: Fri, 1 Sep 2023 10:37:52 +0200 Subject: [PATCH 23/33] removing todo --- src/ServiceControl.UnitTests/API/APIApprovals.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ServiceControl.UnitTests/API/APIApprovals.cs b/src/ServiceControl.UnitTests/API/APIApprovals.cs index 16e8666115..09f503c3de 100644 --- a/src/ServiceControl.UnitTests/API/APIApprovals.cs +++ b/src/ServiceControl.UnitTests/API/APIApprovals.cs @@ -32,7 +32,6 @@ public void RootPathValue() new ActiveLicense { IsValid = true }, new LoggingSettings("testEndpoint"), new Settings(), - null, // TODO: Previously was PersistenceSettings, now persister registers persister specific settings in DI using marker interface to settings can be serialized in API httpClientFactory: null ) { From 12a51f468ea8c1aa66c4070b5d74a215d8ebd9d9 Mon Sep 17 00:00:00 2001 From: Tomek Masternak Date: Fri, 1 Sep 2023 10:39:28 +0200 Subject: [PATCH 24/33] import failed messages command fixing using --- .../Commands/ImportFailedErrorsCommand.cs | 29 ++++++++++--------- .../Infrastructure/WebApi/RootController.cs | 1 - 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs b/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs index 2aa7f8db7d..a0e69e2709 100644 --- a/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs +++ b/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs @@ -29,21 +29,22 @@ public override async Task Execute(HostArguments args, Settings settings) var importFailedErrors = host.Services.GetRequiredService(); - var tokenSource = new CancellationTokenSource(); - - Console.CancelKeyPress += (sender, eventArgs) => { tokenSource.Cancel(); }; - - try - { - await importFailedErrors.Run(tokenSource.Token); - } - catch (OperationCanceledException) - { - // no-op - } - finally + using (var tokenSource = new CancellationTokenSource()) { - await host.StopAsync(CancellationToken.None); + Console.CancelKeyPress += (sender, eventArgs) => { tokenSource.Cancel(); }; + + try + { + await importFailedErrors.Run(tokenSource.Token); + } + catch (OperationCanceledException) + { + // no-op + } + finally + { + await host.StopAsync(CancellationToken.None); + } } } diff --git a/src/ServiceControl/Infrastructure/WebApi/RootController.cs b/src/ServiceControl/Infrastructure/WebApi/RootController.cs index 7c32151a81..f4d7022bf7 100644 --- a/src/ServiceControl/Infrastructure/WebApi/RootController.cs +++ b/src/ServiceControl/Infrastructure/WebApi/RootController.cs @@ -10,7 +10,6 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Particular.ServiceControl.Licensing; - using Persistence; using ServiceBus.Management.Infrastructure.Settings; class RootController : ApiController From f32009546aa368dbd9fdc2e1cf13166fedd045ce Mon Sep 17 00:00:00 2001 From: Tomek Masternak Date: Fri, 1 Sep 2023 10:56:48 +0200 Subject: [PATCH 25/33] maintenance settings moved to the ravendb specific settings class --- .../TestSupport/ServiceControlComponentRunner.cs | 1 - src/ServiceControl.Persistence.RavenDb/RavenBootstrapper.cs | 5 +++++ .../RavenDBPersisterSettings.cs | 5 ++++- .../RavenDbPersistenceConfiguration.cs | 2 +- src/ServiceControl/Bootstrapper.cs | 3 +-- .../Hosting/Commands/MaintenanceModeCommand.cs | 1 - src/ServiceControl/Infrastructure/Settings/Settings.cs | 4 ---- 7 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs b/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs index dc618c5b68..740f51f735 100644 --- a/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs +++ b/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs @@ -80,7 +80,6 @@ async Task InitializeServiceControl(ScenarioContext context) { AllowMessageEditing = true, Port = instancePort, - DatabaseMaintenancePort = maintenancePort, DbPath = dbPath, ForwardErrorMessages = false, TransportConnectionString = transportToUse.ConnectionString, diff --git a/src/ServiceControl.Persistence.RavenDb/RavenBootstrapper.cs b/src/ServiceControl.Persistence.RavenDb/RavenBootstrapper.cs index 873fa29755..ef98b88816 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenBootstrapper.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenBootstrapper.cs @@ -124,6 +124,11 @@ public static void Configure(EmbeddableDocumentStore documentStore, RavenDBPersi return clrtype; }; + + if (settings.MaintenanceMode) + { + Logger.InfoFormat($"RavenDB is now accepting requests on {settings.DatabaseMaintenanceUrl}"); + } } public static string ReadLicense() diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs b/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs index 0b288b1314..286fc3618d 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs @@ -7,7 +7,8 @@ class RavenDBPersisterSettings : PersistenceSettings { public string DatabasePath { get; set; } public string HostName { get; set; } = "localhost"; - public int DatabaseMaintenancePort { get; set; } = 55554; + public int DatabaseMaintenancePort { get; set; } = DatabaseMaintenancePortDefault; + public string DatabaseMaintenanceUrl => $"http://{HostName}:{DatabaseMaintenancePort}"; public bool ExposeRavenDB { get; set; } public int ExpirationProcessTimerInSeconds { get; set; } = ExpiredDocumentsCleanerBundle.ExpirationProcessTimerInSecondsDefault; public int ExpirationProcessBatchSize { get; set; } = ExpiredDocumentsCleanerBundle.ExpirationProcessBatchSizeDefault; @@ -19,4 +20,6 @@ class RavenDBPersisterSettings : PersistenceSettings public TimeSpan EventsRetentionPeriod { get; set; } public TimeSpan? AuditRetentionPeriod { get; set; } public int ExternalIntegrationsDispatchingBatchSize { get; set; } = 100; + + public const int DatabaseMaintenancePortDefault = 33334; } \ No newline at end of file diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs index e34046ca4f..640e732654 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDbPersistenceConfiguration.cs @@ -46,7 +46,7 @@ T GetSetting(string key, T defaultValue) { DatabasePath = GetSetting(RavenBootstrapper.DatabasePathKey, default), HostName = GetSetting(RavenBootstrapper.HostNameKey, "localhost"), - DatabaseMaintenancePort = GetSetting(RavenBootstrapper.DatabaseMaintenancePortKey, default), + DatabaseMaintenancePort = GetSetting(RavenBootstrapper.DatabaseMaintenancePortKey, RavenDBPersisterSettings.DatabaseMaintenancePortDefault), ExposeRavenDB = GetSetting(RavenBootstrapper.ExposeRavenDBKey, false), ExpirationProcessTimerInSeconds = GetSetting(RavenBootstrapper.ExpirationProcessTimerInSecondsKey, ExpiredDocumentsCleanerBundle.ExpirationProcessTimerInSecondsDefault), ExpirationProcessBatchSize = GetSetting(RavenBootstrapper.ExpirationProcessBatchSizeKey, ExpiredDocumentsCleanerBundle.ExpirationProcessBatchSizeDefault), diff --git a/src/ServiceControl/Bootstrapper.cs b/src/ServiceControl/Bootstrapper.cs index db63d46609..14963cd101 100644 --- a/src/ServiceControl/Bootstrapper.cs +++ b/src/ServiceControl/Bootstrapper.cs @@ -221,7 +221,6 @@ Audit Retention Period (optional): {settings.AuditRetentionPeriod} Settings = new { settings.ApiUrl, - settings.DatabaseMaintenancePort, settings.ErrorLogQueue, settings.DataSpaceRemainingThreshold, settings.DbPath, @@ -239,7 +238,7 @@ Audit Retention Period (optional): {settings.AuditRetentionPeriod} settings.SkipQueueCreation, settings.EnableFullTextSearchOnBodies, settings.TransportType, - settings.AllowMessageEditing + settings.AllowMessageEditing, }, LoggingSettings = loggingSettings }); diff --git a/src/ServiceControl/Hosting/Commands/MaintenanceModeCommand.cs b/src/ServiceControl/Hosting/Commands/MaintenanceModeCommand.cs index 4603ae810c..3b7438ea0c 100644 --- a/src/ServiceControl/Hosting/Commands/MaintenanceModeCommand.cs +++ b/src/ServiceControl/Hosting/Commands/MaintenanceModeCommand.cs @@ -21,7 +21,6 @@ public override async Task Execute(HostArguments args, Settings settings) } else { - await Console.Out.WriteLineAsync($"RavenDB is now accepting requests on {settings.DatabaseMaintenanceUrl}"); await Console.Out.WriteLineAsync("RavenDB Maintenance Mode - Press CTRL+C to exit"); hostBuilder.UseConsoleLifetime(); diff --git a/src/ServiceControl/Infrastructure/Settings/Settings.cs b/src/ServiceControl/Infrastructure/Settings/Settings.cs index 50b33bc537..c96dfc57fd 100644 --- a/src/ServiceControl/Infrastructure/Settings/Settings.cs +++ b/src/ServiceControl/Infrastructure/Settings/Settings.cs @@ -46,7 +46,6 @@ public Settings( ErrorRetentionPeriod = errorRetentionPeriod ?? GetErrorRetentionPeriod(); EventsRetentionPeriod = GetEventRetentionPeriod(); Port = SettingsReader.Read("Port", 33333); - DatabaseMaintenancePort = SettingsReader.Read("DatabaseMaintenancePort", 33334); // TODO: Should not be in Core but in the persister implementation ProcessRetryBatchesFrequency = TimeSpan.FromSeconds(30); MaximumConcurrencyLevel = SettingsReader.Read("MaximumConcurrencyLevel", 10); RetryHistoryDepth = SettingsReader.Read("RetryHistoryDepth", 10); @@ -96,8 +95,6 @@ public string RootUrl } } - public string DatabaseMaintenanceUrl => $"http://{Hostname}:{DatabaseMaintenancePort}"; - public string ApiUrl => $"{RootUrl}api"; public string StorageUrl => $"{RootUrl}storage"; @@ -105,7 +102,6 @@ public string RootUrl public string StagingQueue => $"{ServiceName}.staging"; public int Port { get; set; } - public int DatabaseMaintenancePort { get; set; } // TODO: Should not be in Core but in the persister implementation public string LicenseFileText { get; set; } From d80197afcd7e8afd99033e497f75381f94bfcdee Mon Sep 17 00:00:00 2001 From: Tomek Masternak Date: Fri, 1 Sep 2023 10:56:48 +0200 Subject: [PATCH 26/33] maintenance settings moved to the ravendb specific settings class # Conflicts: # src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs # src/ServiceControl.Persistence.RavenDb/RavenBootstrapper.cs --- .../AcceptanceTestStorageConfiguration.cs | 5 +- .../ServiceControlComponentRunner.cs | 6 +- .../RavenBootstrapper.cs | 75 +++++++++++++++++++ .../RavenDBPersisterSettings.cs | 1 - .../ServiceControl.Persistence.RavenDb.csproj | 1 + .../PersistenceSettings.cs | 2 + src/ServiceControl/Bootstrapper.cs | 63 +--------------- .../Infrastructure/Settings/Settings.cs | 27 ------- .../Infrastructure/WebApi/RootController.cs | 1 - .../Persistence/PersistenceFactory.cs | 30 ++++++++ src/ServiceControl/ServiceControl.csproj | 1 - 11 files changed, 113 insertions(+), 99 deletions(-) diff --git a/src/ServiceControl.AcceptanceTests.RavenDB/AcceptanceTestStorageConfiguration.cs b/src/ServiceControl.AcceptanceTests.RavenDB/AcceptanceTestStorageConfiguration.cs index a8471d1370..6c747a5677 100644 --- a/src/ServiceControl.AcceptanceTests.RavenDB/AcceptanceTestStorageConfiguration.cs +++ b/src/ServiceControl.AcceptanceTests.RavenDB/AcceptanceTestStorageConfiguration.cs @@ -1,6 +1,7 @@ namespace ServiceControl.AcceptanceTests { using System; + using System.IO; using System.Linq; using System.Net.NetworkInformation; using System.Threading.Tasks; @@ -16,8 +17,8 @@ public void CustomizeSettings(Settings settings) settings.PersisterSpecificSettings = new RavenDBPersisterSettings { RunInMemory = true, - DatabaseMaintenancePort = FindAvailablePort(33334), - DatabasePath = settings.DbPath, + DatabaseMaintenancePort = FindAvailablePort(settings.Port + 1), + DatabasePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()), ErrorRetentionPeriod = TimeSpan.FromDays(10), }; } diff --git a/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs b/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs index 740f51f735..7202c5fdeb 100644 --- a/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs +++ b/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs @@ -70,17 +70,13 @@ static int FindAvailablePort(int startPort) async Task InitializeServiceControl(ScenarioContext context) { var instancePort = FindAvailablePort(33333); - var maintenancePort = FindAvailablePort(instancePort + 1); ConfigurationManager.AppSettings.Set("ServiceControl/TransportType", transportToUse.TypeName); - var dbPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - var settings = new Settings(instanceName, transportToUse.TypeName, persistenceToUse.PersistenceType, forwardErrorMessages: false, errorRetentionPeriod: TimeSpan.FromDays(10)) { AllowMessageEditing = true, Port = instancePort, - DbPath = dbPath, ForwardErrorMessages = false, TransportConnectionString = transportToUse.ConnectionString, ProcessRetryBatchesFrequency = TimeSpan.FromSeconds(2), @@ -213,7 +209,7 @@ public override async Task Stop() await host.StopAsync(); HttpClient.Dispose(); Handler.Dispose(); - DirectoryDeleter.Delete(Settings.DbPath); + DirectoryDeleter.Delete(Settings.PersisterSpecificSettings.DatabasePath); } bootstrapper = null; diff --git a/src/ServiceControl.Persistence.RavenDb/RavenBootstrapper.cs b/src/ServiceControl.Persistence.RavenDb/RavenBootstrapper.cs index ef98b88816..16863103e5 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenBootstrapper.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenBootstrapper.cs @@ -2,8 +2,10 @@ { using System; using System.ComponentModel.Composition.Hosting; + using System.Globalization; using System.IO; using System.Runtime.Serialization; + using ByteSizeLib; using NServiceBus.Logging; using Raven.Abstractions.Data; using Raven.Client.Document; @@ -129,6 +131,25 @@ public static void Configure(EmbeddableDocumentStore documentStore, RavenDBPersi { Logger.InfoFormat($"RavenDB is now accepting requests on {settings.DatabaseMaintenanceUrl}"); } + + if (settings.RunInMemory == false) + { + RecordStartup(); + } + } + + static void RecordStartup() + { + var dataSize = DataSize(); + var folderSize = FolderSize(); + + var startupMessage = $@" +------------------------------------------------------------- +Database Size: {ByteSize.FromBytes(dataSize).ToString("#.##", CultureInfo.InvariantCulture)} +Database Folder Size: {ByteSize.FromBytes(folderSize).ToString("#.##", CultureInfo.InvariantCulture)} +-------------------------------------------------------------"; + + Logger.Info(startupMessage); } public static string ReadLicense() @@ -149,6 +170,60 @@ static string ReadAllTextWithoutLocking(string path) } } + static long DataSize() + { + var datafilePath = Path.Combine(Settings.DatabasePath, "data"); + + try + { + var info = new FileInfo(datafilePath); + if (!info.Exists) + { + return -1; + } + return info.Length; + } + catch + { + return -1; + } + } + + static long FolderSize() + { + try + { + var dir = new DirectoryInfo(Settings.DatabasePath); + var dirSize = DirSize(dir); + return dirSize; + } + catch + { + return -1; + } + } + + static long DirSize(DirectoryInfo d) + { + long size = 0; + if (d.Exists) + { + FileInfo[] fis = d.GetFiles(); + foreach (FileInfo fi in fis) + { + size += fi.Length; + } + + DirectoryInfo[] dis = d.GetDirectories(); + foreach (DirectoryInfo di in dis) + { + size += DirSize(di); + } + } + + return size; + } + static readonly SerializationBinder MigratedTypeAwareBinder = new MigratedTypeAwareBinder(); static readonly ILog Logger = LogManager.GetLogger(typeof(RavenBootstrapper)); diff --git a/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs b/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs index 286fc3618d..a15e1e4e3f 100644 --- a/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs +++ b/src/ServiceControl.Persistence.RavenDb/RavenDBPersisterSettings.cs @@ -5,7 +5,6 @@ class RavenDBPersisterSettings : PersistenceSettings { - public string DatabasePath { get; set; } public string HostName { get; set; } = "localhost"; public int DatabaseMaintenancePort { get; set; } = DatabaseMaintenancePortDefault; public string DatabaseMaintenanceUrl => $"http://{HostName}:{DatabaseMaintenancePort}"; diff --git a/src/ServiceControl.Persistence.RavenDb/ServiceControl.Persistence.RavenDb.csproj b/src/ServiceControl.Persistence.RavenDb/ServiceControl.Persistence.RavenDb.csproj index fc725447dd..35addd01a2 100644 --- a/src/ServiceControl.Persistence.RavenDb/ServiceControl.Persistence.RavenDb.csproj +++ b/src/ServiceControl.Persistence.RavenDb/ServiceControl.Persistence.RavenDb.csproj @@ -15,6 +15,7 @@ + diff --git a/src/ServiceControl.Persistence/PersistenceSettings.cs b/src/ServiceControl.Persistence/PersistenceSettings.cs index cb9290844f..6ee4345f73 100644 --- a/src/ServiceControl.Persistence/PersistenceSettings.cs +++ b/src/ServiceControl.Persistence/PersistenceSettings.cs @@ -6,5 +6,7 @@ public abstract class PersistenceSettings { public bool MaintenanceMode { get; set; } + //HINT: This needs to be here so that ServerControl instance can add an instance specific metadata to tweak the DatabasePath value + public string DatabasePath { get; set; } } } \ No newline at end of file diff --git a/src/ServiceControl/Bootstrapper.cs b/src/ServiceControl/Bootstrapper.cs index 14963cd101..ef32019750 100644 --- a/src/ServiceControl/Bootstrapper.cs +++ b/src/ServiceControl/Bootstrapper.cs @@ -3,13 +3,10 @@ namespace Particular.ServiceControl using System; using System.Collections.Generic; using System.Diagnostics; - using System.Globalization; - using System.IO; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Reflection; - using ByteSizeLib; using global::ServiceControl.CustomChecks; using global::ServiceControl.ExternalIntegrations; using global::ServiceControl.Infrastructure.BackgroundTasks; @@ -141,65 +138,10 @@ TransportSettings MapSettings(Settings settings) return transportSettings; } - long DataSize() - { - var datafilePath = Path.Combine(settings.DbPath, "data"); - - try - { - var info = new FileInfo(datafilePath); - if (!info.Exists) - { - return -1; - } - return info.Length; - } - catch - { - return -1; - } - } - - long FolderSize() - { - try - { - var dir = new DirectoryInfo(settings.DbPath); - var dirSize = DirSize(dir); - return dirSize; - } - catch - { - return -1; - } - } - - static long DirSize(DirectoryInfo d) - { - long size = 0; - if (d.Exists) - { - FileInfo[] fis = d.GetFiles(); - foreach (FileInfo fi in fis) - { - size += fi.Length; - } - - DirectoryInfo[] dis = d.GetDirectories(); - foreach (DirectoryInfo di in dis) - { - size += DirSize(di); - } - } - - return size; - } - void RecordStartup(LoggingSettings loggingSettings, EndpointConfiguration endpointConfiguration) { var version = FileVersionInfo.GetVersionInfo(typeof(Bootstrapper).Assembly.Location).ProductVersion; - var dataSize = DataSize(); - var folderSize = FolderSize(); + var startupMessage = $@" ------------------------------------------------------------- ServiceControl Version: {version} @@ -207,8 +149,6 @@ Audit Retention Period (optional): {settings.AuditRetentionPeriod} Error Retention Period: {settings.ErrorRetentionPeriod} Ingest Error Messages: {settings.IngestErrorMessages} Forwarding Error Messages: {settings.ForwardErrorMessages} -Database Size: {ByteSize.FromBytes(dataSize).ToString("#.##", CultureInfo.InvariantCulture)} -Database Folder Size: {ByteSize.FromBytes(folderSize).ToString("#.##", CultureInfo.InvariantCulture)} ServiceControl Logging Level: {loggingSettings.LoggingLevel} RavenDB Logging Level: {loggingSettings.RavenDBLogLevel} Selected Transport Customization: {settings.TransportType} @@ -223,7 +163,6 @@ Audit Retention Period (optional): {settings.AuditRetentionPeriod} settings.ApiUrl, settings.ErrorLogQueue, settings.DataSpaceRemainingThreshold, - settings.DbPath, settings.ErrorQueue, settings.ForwardErrorMessages, settings.HttpDefaultConnectionLimit, diff --git a/src/ServiceControl/Infrastructure/Settings/Settings.cs b/src/ServiceControl/Infrastructure/Settings/Settings.cs index c96dfc57fd..aa2f5e46a1 100644 --- a/src/ServiceControl/Infrastructure/Settings/Settings.cs +++ b/src/ServiceControl/Infrastructure/Settings/Settings.cs @@ -54,7 +54,6 @@ public Settings( NotificationsFilter = SettingsReader.Read("NotificationsFilter"); RemoteInstances = GetRemoteInstances().ToArray(); DataSpaceRemainingThreshold = GetDataSpaceRemainingThreshold(); - DbPath = GetDbPath(); TimeToRestartErrorIngestionAfterFailure = GetTimeToRestartErrorIngestionAfterFailure(); DisableExternalIntegrationsPublishing = SettingsReader.Read("DisableExternalIntegrationsPublishing", false); EnableFullTextSearchOnBodies = SettingsReader.Read("EnableFullTextSearchOnBodies", true); @@ -129,7 +128,6 @@ public TimeSpan HeartbeatGracePeriod public string TransportType { get; set; } public string PersistenceType { get; private set; } - public string DbPath { get; set; } // TODO: Should not be in Core but in the persister implementation public string ErrorLogQueue { get; set; } public string ErrorQueue { get; set; } @@ -229,26 +227,6 @@ string GetErrorLogQueue(string errorQueue) return value; } - string GetDbPath() - { - var host = Hostname; - if (host == "*") - { - host = "%"; - } - - var dbFolder = $"{host}-{Port}"; - - if (!string.IsNullOrEmpty(VirtualDirectory)) - { - dbFolder += $"-{SanitiseFolderName(VirtualDirectory)}"; - } - - var defaultPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Particular", "ServiceControl", dbFolder); - - return SettingsReader.Read("DbPath", defaultPath); - } - static bool GetForwardErrorMessages() { var forwardErrorMessages = SettingsReader.Read("ForwardErrorMessages"); @@ -260,11 +238,6 @@ static bool GetForwardErrorMessages() throw new Exception("ForwardErrorMessages settings is missing, please make sure it is included."); } - static string SanitiseFolderName(string folderName) - { - return Path.GetInvalidPathChars().Aggregate(folderName, (current, c) => current.Replace(c, '-')); - } - TimeSpan GetEventRetentionPeriod() { var valueRead = SettingsReader.Read("EventRetentionPeriod"); diff --git a/src/ServiceControl/Infrastructure/WebApi/RootController.cs b/src/ServiceControl/Infrastructure/WebApi/RootController.cs index f4d7022bf7..02573ffd22 100644 --- a/src/ServiceControl/Infrastructure/WebApi/RootController.cs +++ b/src/ServiceControl/Infrastructure/WebApi/RootController.cs @@ -67,7 +67,6 @@ public OkNegotiatedContentResult Config() Host = new { settings.ServiceName, - RavenDBPath = settings.DbPath, Logging = new { loggingSettings.LogPath, diff --git a/src/ServiceControl/Persistence/PersistenceFactory.cs b/src/ServiceControl/Persistence/PersistenceFactory.cs index 72d7311169..9345e97b56 100644 --- a/src/ServiceControl/Persistence/PersistenceFactory.cs +++ b/src/ServiceControl/Persistence/PersistenceFactory.cs @@ -1,6 +1,9 @@ namespace ServiceControl.Persistence { using System; + using System.IO; + using System.Linq; + using System.Web.Hosting; using ServiceBus.Management.Infrastructure.Settings; static class PersistenceFactory @@ -22,6 +25,7 @@ public static IPersistence Create(Settings settings, bool maintenanceMode = fals } settings.PersisterSpecificSettings.MaintenanceMode = maintenanceMode; + settings.PersisterSpecificSettings.DatabasePath = BuildDataBasePath(settings); var persistence = persistenceConfiguration.Create(settings.PersisterSpecificSettings); return persistence; @@ -41,5 +45,31 @@ static IPersistenceConfiguration CreatePersistenceConfiguration(string persisten throw new Exception($"Could not load persistence customization type {persistenceType}.", e); } } + + static string BuildDataBasePath(Settings settings) + { + var host = settings.Hostname; + if (host == "*") + { + host = "%"; + } + + var dbFolder = $"{host}-{settings.Port}"; + + if (!string.IsNullOrEmpty(settings.VirtualDirectory)) + { + dbFolder += $"-{SanitiseFolderName(settings.VirtualDirectory)}"; + } + + var defaultPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Particular", "ServiceControl", dbFolder); + + return SettingsReader.Read("DbPath", defaultPath); + } + + static string SanitiseFolderName(string folderName) + { + return Path.GetInvalidPathChars().Aggregate(folderName, (current, c) => current.Replace(c, '-')); + } + } } \ No newline at end of file diff --git a/src/ServiceControl/ServiceControl.csproj b/src/ServiceControl/ServiceControl.csproj index a9be8ce4b8..20b548d915 100644 --- a/src/ServiceControl/ServiceControl.csproj +++ b/src/ServiceControl/ServiceControl.csproj @@ -30,7 +30,6 @@ - From 785c03eba307fca74f77bb461130ce7bad409212 Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Fri, 1 Sep 2023 13:11:19 +0200 Subject: [PATCH 27/33] Enabled test ServiceControl.UnitTests.API.APIApprovals::PlatformSampleSettings --- src/ServiceControl.UnitTests/API/APIApprovals.cs | 2 +- .../APIApprovals.PlatformSampleSettings.approved.txt | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/ServiceControl.UnitTests/API/APIApprovals.cs b/src/ServiceControl.UnitTests/API/APIApprovals.cs index 09f503c3de..ba104d11f9 100644 --- a/src/ServiceControl.UnitTests/API/APIApprovals.cs +++ b/src/ServiceControl.UnitTests/API/APIApprovals.cs @@ -135,7 +135,7 @@ public void TransportNames() Approver.Verify(publicTransportNames); } - [Test, Ignore("TODO: Deal with this once persister settings are properly managed")] + [Test] public void PlatformSampleSettings() { //HINT: Particular.PlatformSample includes a parameterized version of the ServiceControl.exe.config file. diff --git a/src/ServiceControl.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt b/src/ServiceControl.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt index 136ca67478..918b30ef20 100644 --- a/src/ServiceControl.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt +++ b/src/ServiceControl.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt @@ -2,7 +2,6 @@ "NotificationsFilter": null, "AllowMessageEditing": false, "MessageFilter": null, - "RunInMemory": false, "EmailDropFolder": null, "ValidateConfiguration": true, "ExternalIntegrationsDispatchingBatchSize": 100, @@ -10,30 +9,25 @@ "SkipQueueCreation": false, "RunCleanupBundle": false, "RootUrl": "http://localhost:8888/", - "DatabaseMaintenanceUrl": "http://localhost:33334", "ApiUrl": "http://localhost:8888/api", "StorageUrl": "http://localhost:8888/storage", "StagingQueue": "Particular.ServiceControl.staging", "Port": 8888, - "DatabaseMaintenancePort": 33334, "LicenseFileText": null, - "ExposeRavenDB": false, + "PersisterSpecificSettings": null, "PrintMetrics": false, "Hostname": "localhost", "VirtualDirectory": "", "HeartbeatGracePeriod": "00:00:40", "TransportType": "ServiceControl.Transports.Learning.LearningTransportCustomization, ServiceControl.Transports.Learning", - "DbPath": "C:\\DB", "ErrorLogQueue": "error.log", "ErrorQueue": "error", "ForwardErrorMessages": false, "IngestErrorMessages": true, "RunRetryProcessor": true, - "ExpirationProcessTimerInSeconds": 600, "AuditRetentionPeriod": null, "ErrorRetentionPeriod": "10.00:00:00", "EventsRetentionPeriod": "14.00:00:00", - "ExpirationProcessBatchSize": 65512, "ServiceName": "Particular.ServiceControl", "HttpDefaultConnectionLimit": 100, "TransportConnectionString": null, @@ -43,8 +37,7 @@ "RetryHistoryDepth": 10, "RemoteInstances": [], "DataSpaceRemainingThreshold": 20, - "MinimumStorageLeftRequiredForIngestion": 5, "EnableFullTextSearchOnBodies": true, "DisableHealthChecks": false, - "ExposeApi": true, + "ExposeApi": true } \ No newline at end of file From 52ed580cb6f4fc7737b1d84a33bd7a591376bc62 Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Fri, 1 Sep 2023 14:13:32 +0200 Subject: [PATCH 28/33] =?UTF-8?q?=E2=9A=9C=EF=B8=8F=20Appsettings=20cleanu?= =?UTF-8?q?p,=20applied=20formatting.=20XML=20files=20seem=20to=20be=20usi?= =?UTF-8?q?ng=20tab=20indentation=20so=20aligned=20all=20files.=20Removed?= =?UTF-8?q?=20all=20references=20to=20c:\=20and=20use=20%TEMP%?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ServiceControl.Audit/App.config | 23 ++-- src/ServiceControl.Monitoring/App.config | 12 +- src/ServiceControl.UnitTests/app.config | 5 +- src/ServiceControl/App.config | 136 +++++++++++------------ 4 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/ServiceControl.Audit/App.config b/src/ServiceControl.Audit/App.config index f489462dec..5d596184ef 100644 --- a/src/ServiceControl.Audit/App.config +++ b/src/ServiceControl.Audit/App.config @@ -9,7 +9,7 @@ These settings are only here so that we can debug ServiceControl while developin - + - + - + - + - + @@ -63,8 +63,7 @@ These settings are only here so that we can debug ServiceControl while developin - - + diff --git a/src/ServiceControl.Monitoring/App.config b/src/ServiceControl.Monitoring/App.config index eb0ea335e8..1ed00b3cf4 100644 --- a/src/ServiceControl.Monitoring/App.config +++ b/src/ServiceControl.Monitoring/App.config @@ -4,7 +4,7 @@ - + + --> - + - - + + @@ -36,7 +36,7 @@ - + diff --git a/src/ServiceControl.UnitTests/app.config b/src/ServiceControl.UnitTests/app.config index 03d8dcadf7..6b43ce87f0 100644 --- a/src/ServiceControl.UnitTests/app.config +++ b/src/ServiceControl.UnitTests/app.config @@ -1,8 +1,9 @@ - + + - + diff --git a/src/ServiceControl/App.config b/src/ServiceControl/App.config index fada510b70..0f471c0fbc 100644 --- a/src/ServiceControl/App.config +++ b/src/ServiceControl/App.config @@ -5,71 +5,71 @@ These settings are only here so that we can debug ServiceControl while developin --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From c1c31b71a7b006593feee6f7b416ff586f0b53ca Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Fri, 1 Sep 2023 14:29:06 +0200 Subject: [PATCH 29/33] =?UTF-8?q?=E2=9A=9C=EF=B8=8F=20Strange=20editor=20c?= =?UTF-8?q?onfig=20references?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ServiceControl.Infrastructure.Tests.csproj | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj b/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj index 877509b573..ed0859a57a 100644 --- a/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj +++ b/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj @@ -9,7 +9,6 @@ - @@ -17,12 +16,4 @@ - - - - - - - - \ No newline at end of file From 971aba233b2db45700226312f62f4b2c298c1883 Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Mon, 4 Sep 2023 22:41:01 +0200 Subject: [PATCH 30/33] =?UTF-8?q?=F0=9F=90=9B=20Envvar=20settings=20reader?= =?UTF-8?q?=20behavior=20aligned=20to=20all=20support=20underscore=20repla?= =?UTF-8?q?cements=20for=20slash=20'/'=20and=20dot=20'.'=20in=20key=20name?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EnvironmentVariableSettingsReader.cs | 5 +++++ .../Settings/EnvironmentVariableSettingsReader.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs b/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs index 92a567cb03..22824a6270 100644 --- a/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs +++ b/src/ServiceControl.Configuration/EnvironmentVariableSettingsReader.cs @@ -22,6 +22,11 @@ public bool TryRead(string root, string name, Type type, out object value) { return true; } + // container images and env files compatibility: + if (TryReadVariable(type, out value, $"{root}_{name}".Replace('.', '_').Replace('/', '_'))) + { + return true; + } value = default; return false; diff --git a/src/ServiceControl.Monitoring/Infrastructure/Settings/EnvironmentVariableSettingsReader.cs b/src/ServiceControl.Monitoring/Infrastructure/Settings/EnvironmentVariableSettingsReader.cs index 94802ea930..c63249f83e 100644 --- a/src/ServiceControl.Monitoring/Infrastructure/Settings/EnvironmentVariableSettingsReader.cs +++ b/src/ServiceControl.Monitoring/Infrastructure/Settings/EnvironmentVariableSettingsReader.cs @@ -30,6 +30,11 @@ public static bool TryRead(string root, string name, out T value) { return true; } + // container images and env files compatibility: + if (TryReadVariable(out value, $"{root}_{name}".Replace('.', '_').Replace('/', '_'))) + { + return true; + } value = default; return false; From 6ffc71baf9c5f64ae6b18ad383f6a5d9cea8fc5d Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Mon, 4 Sep 2023 22:40:08 +0200 Subject: [PATCH 31/33] =?UTF-8?q?=F0=9F=A9=B9=20Validate=20if=20connection?= =?UTF-8?q?=20string=20value=20exists?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RabbitMQConventionalRoutingTransportCustomization.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ServiceControl.Transports.RabbitMQ/RabbitMQConventionalRoutingTransportCustomization.cs b/src/ServiceControl.Transports.RabbitMQ/RabbitMQConventionalRoutingTransportCustomization.cs index 0fb42bd93b..e2d7518a8c 100644 --- a/src/ServiceControl.Transports.RabbitMQ/RabbitMQConventionalRoutingTransportCustomization.cs +++ b/src/ServiceControl.Transports.RabbitMQ/RabbitMQConventionalRoutingTransportCustomization.cs @@ -1,5 +1,6 @@ namespace ServiceControl.Transports.RabbitMQ { + using System; using NServiceBus; using NServiceBus.Raw; @@ -61,6 +62,10 @@ static void CustomizeRawEndpoint(RawEndpointConfiguration endpointConfig, Transp static void ConfigureTransport(TransportExtensions transport, TransportSettings transportSettings, QueueType queueType) { + if (transportSettings.ConnectionString == null) + { + throw new InvalidOperationException("Connection string not configured"); + } transport.UseConventionalRoutingTopology(queueType); transport.Transactions(TransportTransactionMode.ReceiveOnly); transport.ApplyConnectionString(transportSettings.ConnectionString); From 4d53a3b0b75e1e452f9c1908073258da41ee1575 Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Fri, 1 Sep 2023 14:29:17 +0200 Subject: [PATCH 32/33] Removed C:\ usage --- .../RunEngineTasksExplicitly.cs | 4 +- .../Validation/PathsValidationTests.cs | 40 ++++++++++--------- .../Instances/ServiceControlCoreTransports.cs | 2 +- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/ServiceControlInstaller.Engine.UnitTests/RunEngineTasksExplicitly.cs b/src/ServiceControlInstaller.Engine.UnitTests/RunEngineTasksExplicitly.cs index 3024ef0aa4..e9d3ee7eb5 100644 --- a/src/ServiceControlInstaller.Engine.UnitTests/RunEngineTasksExplicitly.cs +++ b/src/ServiceControlInstaller.Engine.UnitTests/RunEngineTasksExplicitly.cs @@ -48,7 +48,7 @@ public async Task CreateInstanceMSMQ() { var installer = new UnattendServiceControlInstaller(new TestLogger(), DeploymentCache); var instanceName = "Test.ServiceControl.Msmq"; - var root = Path.Combine(@"c:\Test", instanceName); + var root = Path.Combine(Path.GetTempPath(), instanceName); var details = ServiceControlNewInstance.CreateWithDefaultPersistence(DeploymentCache); details.DisplayName = instanceName.Replace(".", " "); @@ -108,7 +108,7 @@ public async Task ChangeConfigTests() logger.Info("Changing LogPath"); msmqTestInstance = InstanceFinder.ServiceControlInstances().First(p => p.Name.Equals("Test.ServiceControl.MSMQ", StringComparison.OrdinalIgnoreCase)); - msmqTestInstance.LogPath = @"c:\temp\testloggingchange"; + msmqTestInstance.LogPath = Path.Combine(Path.GetTempPath(), "testloggingchange"); await installer.Update(msmqTestInstance, true).ConfigureAwait(false); Assert.IsTrue(msmqTestInstance.Service.Status == ServiceControllerStatus.Running, "Update Logging changed failed"); diff --git a/src/ServiceControlInstaller.Engine.UnitTests/Validation/PathsValidationTests.cs b/src/ServiceControlInstaller.Engine.UnitTests/Validation/PathsValidationTests.cs index dd25273aa2..a7f784757d 100644 --- a/src/ServiceControlInstaller.Engine.UnitTests/Validation/PathsValidationTests.cs +++ b/src/ServiceControlInstaller.Engine.UnitTests/Validation/PathsValidationTests.cs @@ -9,14 +9,16 @@ [TestFixture] public class PathsValidationTests { + static readonly string BasePath = Path.Combine(Path.GetTempPath(), "PathsValidationTests"); + [Test] public void CheckPathsAreUnique_ShouldThrow() { var newInstance = ServiceControlNewInstance.CreateWithDefaultPersistence(); - newInstance.InstallPath = @"c:\test\1\bin"; - newInstance.LogPath = @"c:\test\1\bin"; - newInstance.DBPath = @"c:\test\1\bin"; + newInstance.InstallPath = BasePath + "bin"; + newInstance.LogPath = BasePath + @"bin"; + newInstance.DBPath = BasePath + "bin"; var p = new PathsValidator(newInstance); @@ -29,9 +31,9 @@ public void CheckPathsAreUnique_ShouldSucceed() { var newInstance = ServiceControlNewInstance.CreateWithDefaultPersistence(); - newInstance.InstallPath = @"c:\test\1\bin"; - newInstance.LogPath = @"c:\test\1\log"; - newInstance.DBPath = @"c:\test\1\db"; + newInstance.InstallPath = BasePath + @"bin"; + newInstance.LogPath = BasePath + @"log"; + newInstance.DBPath = BasePath + @"bin"; var p = new PathsValidator(newInstance); Assert.DoesNotThrow(() => p.CheckPathsAreUnique()); @@ -43,9 +45,9 @@ public void CheckPathsAreValid_ShouldSucceed() { var newInstance = ServiceControlNewInstance.CreateWithDefaultPersistence(); - newInstance.InstallPath = @"c:\test\1\bin"; - newInstance.LogPath = @"c:\test\1\bin"; - newInstance.DBPath = @"c:\test\1\bin"; + newInstance.InstallPath = BasePath + "bin"; + newInstance.LogPath = BasePath + "bin"; + newInstance.DBPath = BasePath + "bin"; var p = new PathsValidator(newInstance); Assert.DoesNotThrow(() => p.CheckPathsAreValid()); @@ -56,7 +58,7 @@ public void CheckPathsAreValid_ShouldThrow() { //Invalid path var instance = ServiceControlNewInstance.CreateWithDefaultPersistence(); - instance.InstallPath = @"?>c:\test\1\bin"; + instance.InstallPath = @"?>" + BasePath + @"bin"; var p = new PathsValidator(instance); var ex = Assert.Throws(() => p.CheckPathsAreValid()); @@ -82,9 +84,9 @@ public void CheckNoNestedPaths_ShouldThrow() { var newInstance = ServiceControlNewInstance.CreateWithDefaultPersistence(); - newInstance.InstallPath = @"c:\test\1"; - newInstance.LogPath = @"c:\test\1\log"; - newInstance.DBPath = @"c:\test\1\db"; + newInstance.InstallPath = BasePath; + newInstance.LogPath = BasePath + "log"; + newInstance.DBPath = BasePath + "db"; var p = new PathsValidator(newInstance); var ex = Assert.Throws(() => p.CheckNoNestedPaths()); @@ -96,9 +98,9 @@ public void CheckNoNestedSiblingPaths_ShouldSucceed() { var newInstance = ServiceControlNewInstance.CreateWithDefaultPersistence(); - newInstance.InstallPath = @"c:\test\1\servicecontrol"; - newInstance.LogPath = @"c:\test\1\servicecontrollog"; - newInstance.DBPath = @"c:\test\1\servicecontroldb"; + newInstance.InstallPath = BasePath + "servicecontrol"; + newInstance.LogPath = BasePath + "servicecontrollog"; + newInstance.DBPath = BasePath + "servicecontroldb"; var p = new PathsValidator(newInstance); Assert.DoesNotThrow(() => p.CheckNoNestedPaths()); @@ -109,9 +111,9 @@ public void CheckNoNestedPaths_ShouldSucceed() { var newInstance = ServiceControlNewInstance.CreateWithDefaultPersistence(); - newInstance.InstallPath = @"c:\test\1\bin"; - newInstance.LogPath = @"c:\test\1\log"; - newInstance.DBPath = @"c:\test\1\db"; + newInstance.InstallPath = BasePath + "bin"; + newInstance.LogPath = BasePath + "log"; + newInstance.DBPath = BasePath + "db"; var p = new PathsValidator(newInstance); diff --git a/src/ServiceControlInstaller.Engine/Instances/ServiceControlCoreTransports.cs b/src/ServiceControlInstaller.Engine/Instances/ServiceControlCoreTransports.cs index 0d14b025c8..e9ab522232 100644 --- a/src/ServiceControlInstaller.Engine/Instances/ServiceControlCoreTransports.cs +++ b/src/ServiceControlInstaller.Engine/Instances/ServiceControlCoreTransports.cs @@ -188,7 +188,7 @@ public class ServiceControlCoreTransports Name = TransportNames.LearningTransport, TypeName = "ServiceControl.Transports.Learning.LearningTransportCustomization, ServiceControl.Transports.Learning", ZipName = "LearningTransport", - SampleConnectionString = "C:\\tmp\\", + SampleConnectionString = "%TEMP%\\.learningtransport", AvailableInSCMU = IncludeLearningTransport(), Matches = name => name.Equals(TransportNames.LearningTransport, StringComparison.OrdinalIgnoreCase) || name.Equals("ServiceControl.Transports.Learning.LearningTransportCustomization, ServiceControl.Transports.Learning", StringComparison.OrdinalIgnoreCase) From 6768d6989fc0983edfb6fdb63f741e9d4a7f0962 Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Tue, 5 Sep 2023 14:38:12 +0200 Subject: [PATCH 33/33] Fix commit b4944bbd2114cd0f14d6543141742f919cfe8656. --- .../RunEngineTasksExplicitly.cs | 2 +- .../Validation/PathsValidationTests.cs | 36 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/ServiceControlInstaller.Engine.UnitTests/RunEngineTasksExplicitly.cs b/src/ServiceControlInstaller.Engine.UnitTests/RunEngineTasksExplicitly.cs index e9d3ee7eb5..56f972e49e 100644 --- a/src/ServiceControlInstaller.Engine.UnitTests/RunEngineTasksExplicitly.cs +++ b/src/ServiceControlInstaller.Engine.UnitTests/RunEngineTasksExplicitly.cs @@ -139,6 +139,6 @@ void RemoveAltMSMQQueues() } } - const string DeploymentCache = @"..\..\..\..\Zip"; + const string DeploymentCache = @"..\..\..\..\..\Zip"; } } \ No newline at end of file diff --git a/src/ServiceControlInstaller.Engine.UnitTests/Validation/PathsValidationTests.cs b/src/ServiceControlInstaller.Engine.UnitTests/Validation/PathsValidationTests.cs index a7f784757d..f627c15cd4 100644 --- a/src/ServiceControlInstaller.Engine.UnitTests/Validation/PathsValidationTests.cs +++ b/src/ServiceControlInstaller.Engine.UnitTests/Validation/PathsValidationTests.cs @@ -16,9 +16,9 @@ public void CheckPathsAreUnique_ShouldThrow() { var newInstance = ServiceControlNewInstance.CreateWithDefaultPersistence(); - newInstance.InstallPath = BasePath + "bin"; - newInstance.LogPath = BasePath + @"bin"; - newInstance.DBPath = BasePath + "bin"; + newInstance.InstallPath = Path.Combine(BasePath, "bin"); + newInstance.LogPath = Path.Combine(BasePath, "bin"); + newInstance.DBPath = Path.Combine(BasePath, "bin"); var p = new PathsValidator(newInstance); @@ -31,9 +31,9 @@ public void CheckPathsAreUnique_ShouldSucceed() { var newInstance = ServiceControlNewInstance.CreateWithDefaultPersistence(); - newInstance.InstallPath = BasePath + @"bin"; - newInstance.LogPath = BasePath + @"log"; - newInstance.DBPath = BasePath + @"bin"; + newInstance.InstallPath = Path.Combine(BasePath, "bin"); + newInstance.LogPath = Path.Combine(BasePath, "log"); + newInstance.DBPath = Path.Combine(BasePath, "db"); var p = new PathsValidator(newInstance); Assert.DoesNotThrow(() => p.CheckPathsAreUnique()); @@ -45,9 +45,9 @@ public void CheckPathsAreValid_ShouldSucceed() { var newInstance = ServiceControlNewInstance.CreateWithDefaultPersistence(); - newInstance.InstallPath = BasePath + "bin"; - newInstance.LogPath = BasePath + "bin"; - newInstance.DBPath = BasePath + "bin"; + newInstance.InstallPath = Path.Combine(BasePath, "bin"); + newInstance.LogPath = Path.Combine(BasePath, "bin"); + newInstance.DBPath = Path.Combine(BasePath, "bin"); var p = new PathsValidator(newInstance); Assert.DoesNotThrow(() => p.CheckPathsAreValid()); @@ -58,7 +58,7 @@ public void CheckPathsAreValid_ShouldThrow() { //Invalid path var instance = ServiceControlNewInstance.CreateWithDefaultPersistence(); - instance.InstallPath = @"?>" + BasePath + @"bin"; + instance.InstallPath = @"?>" + Path.Combine(BasePath, "bin"); var p = new PathsValidator(instance); var ex = Assert.Throws(() => p.CheckPathsAreValid()); @@ -85,8 +85,8 @@ public void CheckNoNestedPaths_ShouldThrow() var newInstance = ServiceControlNewInstance.CreateWithDefaultPersistence(); newInstance.InstallPath = BasePath; - newInstance.LogPath = BasePath + "log"; - newInstance.DBPath = BasePath + "db"; + newInstance.LogPath = Path.Combine(BasePath, "log"); + newInstance.DBPath = Path.Combine(BasePath, "db"); var p = new PathsValidator(newInstance); var ex = Assert.Throws(() => p.CheckNoNestedPaths()); @@ -98,9 +98,9 @@ public void CheckNoNestedSiblingPaths_ShouldSucceed() { var newInstance = ServiceControlNewInstance.CreateWithDefaultPersistence(); - newInstance.InstallPath = BasePath + "servicecontrol"; - newInstance.LogPath = BasePath + "servicecontrollog"; - newInstance.DBPath = BasePath + "servicecontroldb"; + newInstance.InstallPath = Path.Combine(BasePath, "servicecontrol"); + newInstance.LogPath = Path.Combine(BasePath, "servicecontrollog"); + newInstance.DBPath = Path.Combine(BasePath, "servicecontroldb"); var p = new PathsValidator(newInstance); Assert.DoesNotThrow(() => p.CheckNoNestedPaths()); @@ -111,9 +111,9 @@ public void CheckNoNestedPaths_ShouldSucceed() { var newInstance = ServiceControlNewInstance.CreateWithDefaultPersistence(); - newInstance.InstallPath = BasePath + "bin"; - newInstance.LogPath = BasePath + "log"; - newInstance.DBPath = BasePath + "db"; + newInstance.InstallPath = Path.Combine(BasePath, "bin"); + newInstance.LogPath = Path.Combine(BasePath, "log"); + newInstance.DBPath = Path.Combine(BasePath, "db"); var p = new PathsValidator(newInstance);