From f995a4f35af9694741cd723c6d397b68193f9e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20G=C3=BCran?= Date: Sat, 7 Jan 2023 20:31:42 +0100 Subject: [PATCH] Implemented aliases --- .../Common/ElasticFeatureBase.cs | 13 ++++--- .../Elsa.Elasticsearch/Common/Utils.cs | 19 ++++++++++ ...ingsExtensions.cs => ElasticExtensions.cs} | 20 ++++++++-- .../Modules/Management/Extensions.cs | 4 +- .../WorkflowInstanceConfiguration.cs | 7 +--- .../Runtime/ExecutionLogConfiguration.cs | 6 +-- .../Modules/Runtime/Extensions.cs | 4 +- .../Options/ElasticsearchOptions.cs | 2 +- .../Services/IElasticConfiguration.cs | 12 +++--- .../DependencyInjectionExtensions.cs | 1 + .../Elsa.Scheduling/Elsa.Scheduling.csproj | 1 + .../Features/SchedulingFeature.cs | 1 + .../ElasticCongurationScheduler.cs | 28 ++++++++++++++ .../Jobs/ConfigureElasticIndicesJob.cs | 38 +++++++++++++++++++ .../Services/IElasticCongurationScheduler.cs | 8 ++++ 15 files changed, 135 insertions(+), 29 deletions(-) create mode 100644 src/modules/Elsa.Elasticsearch/Common/Utils.cs rename src/modules/Elsa.Elasticsearch/Extensions/{ConnectionSettingsExtensions.cs => ElasticExtensions.cs} (62%) create mode 100644 src/modules/Elsa.Scheduling/Implementations/ElasticCongurationScheduler.cs create mode 100644 src/modules/Elsa.Scheduling/Jobs/ConfigureElasticIndicesJob.cs create mode 100644 src/modules/Elsa.Scheduling/Services/IElasticCongurationScheduler.cs diff --git a/src/modules/Elsa.Elasticsearch/Common/ElasticFeatureBase.cs b/src/modules/Elsa.Elasticsearch/Common/ElasticFeatureBase.cs index 91f61e4f1..cfde91c9c 100644 --- a/src/modules/Elsa.Elasticsearch/Common/ElasticFeatureBase.cs +++ b/src/modules/Elsa.Elasticsearch/Common/ElasticFeatureBase.cs @@ -16,21 +16,22 @@ public abstract class ElasticFeatureBase : FeatureBase } internal ElasticsearchOptions Options { get; set; } = new(); - internal IDictionary IndexConfig { get; set; } + internal IDictionary AliasConfig { get; set; } = IElasticConfiguration.GetDefaultAliasConfig(); public override void Apply() { - if (Services.All(x => x.ServiceType != typeof(ElasticClient))) - { - Services.AddSingleton(new ElasticClient(GetSettings())); - } + if (Services.Any(x => x.ServiceType == typeof(ElasticClient))) return; + + var elasticClient = new ElasticClient(GetSettings()); + elasticClient.ConfigureIndicesAndAliases(AliasConfig); + Services.AddSingleton(elasticClient); } private ConnectionSettings GetSettings() { return new ConnectionSettings(new Uri(Options.Endpoint)) .ConfigureAuthentication(Options) - .ConfigureMapping(IndexConfig); + .ConfigureMapping(AliasConfig); } protected void AddStore() where TModel : class where TStore : class diff --git a/src/modules/Elsa.Elasticsearch/Common/Utils.cs b/src/modules/Elsa.Elasticsearch/Common/Utils.cs new file mode 100644 index 000000000..70dfc500f --- /dev/null +++ b/src/modules/Elsa.Elasticsearch/Common/Utils.cs @@ -0,0 +1,19 @@ +using Elsa.Elasticsearch.Services; + +namespace Elsa.Elasticsearch.Common; + +public static class Utils +{ + public static string GenerateIndexName(string aliasName) + { + var month = DateTime.Now.ToString("MM"); + var year = DateTime.Now.Year; + + return aliasName + "-" + year + "-" + month; + } + + public static IEnumerable GetElasticDocumentTypes() => + AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(s => s.GetTypes()) + .Where(p => typeof(IElasticConfiguration).IsAssignableFrom(p) && p.IsClass); +} \ No newline at end of file diff --git a/src/modules/Elsa.Elasticsearch/Extensions/ConnectionSettingsExtensions.cs b/src/modules/Elsa.Elasticsearch/Extensions/ElasticExtensions.cs similarity index 62% rename from src/modules/Elsa.Elasticsearch/Extensions/ConnectionSettingsExtensions.cs rename to src/modules/Elsa.Elasticsearch/Extensions/ElasticExtensions.cs index b184035b0..c5dd32aa8 100644 --- a/src/modules/Elsa.Elasticsearch/Extensions/ConnectionSettingsExtensions.cs +++ b/src/modules/Elsa.Elasticsearch/Extensions/ElasticExtensions.cs @@ -1,11 +1,12 @@ using Elasticsearch.Net; +using Elsa.Elasticsearch.Common; using Elsa.Elasticsearch.Options; using Elsa.Elasticsearch.Services; using Nest; namespace Elsa.Elasticsearch.Extensions; -public static class ConnectionSettingsExtensions +public static class ElasticExtensions { public static ConnectionSettings ConfigureAuthentication(this ConnectionSettings settings, ElasticsearchOptions options) { @@ -21,7 +22,7 @@ public static class ConnectionSettingsExtensions return settings; } - public static ConnectionSettings ConfigureMapping(this ConnectionSettings settings, IDictionary indexConfig) + public static ConnectionSettings ConfigureMapping(this ConnectionSettings settings, IDictionary aliasConfig) { var configs = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(s => s.GetTypes()) @@ -30,9 +31,22 @@ public static class ConnectionSettingsExtensions foreach (var config in configs) { var configInstance = (IElasticConfiguration)Activator.CreateInstance(config)!; - configInstance.Apply(settings, indexConfig); + configInstance.Apply(settings, aliasConfig); } return settings; } + + public static void ConfigureIndicesAndAliases(this ElasticClient client, IDictionary aliasConfig) + { + foreach (var type in Utils.GetElasticDocumentTypes()) + { + var aliasName = aliasConfig[type.Name]; + var indexName = Utils.GenerateIndexName(aliasName); + + client.Indices.Create(indexName, s => s + .Aliases(a => a.Alias(aliasName)) + .Map(m => m.AutoMap(type))); + } + } } \ No newline at end of file diff --git a/src/modules/Elsa.Elasticsearch/Modules/Management/Extensions.cs b/src/modules/Elsa.Elasticsearch/Modules/Management/Extensions.cs index c4a2bf2d8..72fff722b 100644 --- a/src/modules/Elsa.Elasticsearch/Modules/Management/Extensions.cs +++ b/src/modules/Elsa.Elasticsearch/Modules/Management/Extensions.cs @@ -8,12 +8,12 @@ public static class Extensions /// /// Configures the to use the . /// - public static WorkflowInstanceFeature UseElasticsearch(this WorkflowInstanceFeature feature, ElasticsearchOptions options, IDictionary? indexConfig = default, Action? configure = default) + public static WorkflowInstanceFeature UseElasticsearch(this WorkflowInstanceFeature feature, ElasticsearchOptions options, IDictionary? aliasConfig = default, Action? configure = default) { configure += f => { f.Options = options; - f.IndexConfig = indexConfig ?? options.Indices ?? new Dictionary(); + f.AliasConfig = aliasConfig ?? options.Aliases ?? f.AliasConfig; }; feature.Module.Configure(configure); diff --git a/src/modules/Elsa.Elasticsearch/Modules/Management/WorkflowInstanceConfiguration.cs b/src/modules/Elsa.Elasticsearch/Modules/Management/WorkflowInstanceConfiguration.cs index cfcb245c0..73d518f63 100644 --- a/src/modules/Elsa.Elasticsearch/Modules/Management/WorkflowInstanceConfiguration.cs +++ b/src/modules/Elsa.Elasticsearch/Modules/Management/WorkflowInstanceConfiguration.cs @@ -1,4 +1,3 @@ -using Elsa.Elasticsearch.Options; using Elsa.Elasticsearch.Services; using Elsa.Workflows.Management.Entities; using Nest; @@ -7,11 +6,9 @@ namespace Elsa.Elasticsearch.Modules.Management; public class WorkflowInstanceConfiguration : IElasticConfiguration { - private const string IndexName = "workflow-instance"; - - public void Apply(ConnectionSettings connectionSettings, IDictionary indexConfig) + public void Apply(ConnectionSettings connectionSettings, IDictionary aliasConfig) { connectionSettings.DefaultMappingFor(m => - m.IndexName(IElasticConfiguration.ResolveIndexName(indexConfig, IndexName))); + m.IndexName(aliasConfig[nameof(WorkflowInstance)])); } } \ No newline at end of file diff --git a/src/modules/Elsa.Elasticsearch/Modules/Runtime/ExecutionLogConfiguration.cs b/src/modules/Elsa.Elasticsearch/Modules/Runtime/ExecutionLogConfiguration.cs index cd5623300..0ee7e7aa5 100644 --- a/src/modules/Elsa.Elasticsearch/Modules/Runtime/ExecutionLogConfiguration.cs +++ b/src/modules/Elsa.Elasticsearch/Modules/Runtime/ExecutionLogConfiguration.cs @@ -7,11 +7,9 @@ namespace Elsa.Elasticsearch.Modules.Runtime; public class ExecutionLogConfiguration : IElasticConfiguration { - private const string IndexName = "workflow-execution-log"; - - public void Apply(ConnectionSettings connectionSettings, IDictionary indexConfig) + public void Apply(ConnectionSettings connectionSettings, IDictionary aliasConfig) { connectionSettings.DefaultMappingFor(m => - m.IndexName(IElasticConfiguration.ResolveIndexName(indexConfig, IndexName))); + m.IndexName(aliasConfig[nameof(WorkflowExecutionLogRecord)])); } } \ No newline at end of file diff --git a/src/modules/Elsa.Elasticsearch/Modules/Runtime/Extensions.cs b/src/modules/Elsa.Elasticsearch/Modules/Runtime/Extensions.cs index bc66235fd..11aa83e11 100644 --- a/src/modules/Elsa.Elasticsearch/Modules/Runtime/Extensions.cs +++ b/src/modules/Elsa.Elasticsearch/Modules/Runtime/Extensions.cs @@ -8,12 +8,12 @@ public static class Extensions /// /// Configures the to use the . /// - public static ExecutionLogRecordFeature UseElasticsearch(this ExecutionLogRecordFeature feature, ElasticsearchOptions options, IDictionary? indexConfig = default, Action? configure = default) + public static ExecutionLogRecordFeature UseElasticsearch(this ExecutionLogRecordFeature feature, ElasticsearchOptions options, IDictionary? aliasConfig = default, Action? configure = default) { configure += f => { f.Options = options; - f.IndexConfig = indexConfig ?? options.Indices ?? new Dictionary(); + f.AliasConfig = aliasConfig ?? options.Aliases ?? f.AliasConfig; }; feature.Module.Configure(configure); diff --git a/src/modules/Elsa.Elasticsearch/Options/ElasticsearchOptions.cs b/src/modules/Elsa.Elasticsearch/Options/ElasticsearchOptions.cs index 741382dfd..9411cd351 100644 --- a/src/modules/Elsa.Elasticsearch/Options/ElasticsearchOptions.cs +++ b/src/modules/Elsa.Elasticsearch/Options/ElasticsearchOptions.cs @@ -4,7 +4,7 @@ public class ElasticsearchOptions { public const string Elasticsearch = "Elasticsearch"; - public Dictionary? Indices { get; set; } + public Dictionary? Aliases { get; set; } public string Endpoint { get; set; } public string Username { get; set; } public string Password { get; set; } diff --git a/src/modules/Elsa.Elasticsearch/Services/IElasticConfiguration.cs b/src/modules/Elsa.Elasticsearch/Services/IElasticConfiguration.cs index d167fb966..677aad4cb 100644 --- a/src/modules/Elsa.Elasticsearch/Services/IElasticConfiguration.cs +++ b/src/modules/Elsa.Elasticsearch/Services/IElasticConfiguration.cs @@ -1,15 +1,15 @@ -using Elsa.Elasticsearch.Options; +using Elsa.Elasticsearch.Common; using Nest; namespace Elsa.Elasticsearch.Services; public interface IElasticConfiguration { - void Apply(ConnectionSettings connectionSettings, IDictionary indexConfig); - - public static string ResolveIndexName(IDictionary indices, string? indexName = default) + void Apply(ConnectionSettings connectionSettings, IDictionary aliasConfig); + + internal static IDictionary GetDefaultAliasConfig() { - var indexNameFromConfig = indices[typeof(T).Name]; - return string.IsNullOrWhiteSpace(indexNameFromConfig) ? indexName : indexNameFromConfig; + var types = Utils.GetElasticDocumentTypes(); + return new Dictionary(types.Select(t => new KeyValuePair(t.Name, t.Name))); } } \ No newline at end of file diff --git a/src/modules/Elsa.Quartz/Extensions/DependencyInjectionExtensions.cs b/src/modules/Elsa.Quartz/Extensions/DependencyInjectionExtensions.cs index eb215ab37..d3a16f4b0 100644 --- a/src/modules/Elsa.Quartz/Extensions/DependencyInjectionExtensions.cs +++ b/src/modules/Elsa.Quartz/Extensions/DependencyInjectionExtensions.cs @@ -12,6 +12,7 @@ public static class DependencyInjectionExtensions { quartz.AddJob(); quartz.AddJob(); + quartz.AddJob(); return quartz; } diff --git a/src/modules/Elsa.Scheduling/Elsa.Scheduling.csproj b/src/modules/Elsa.Scheduling/Elsa.Scheduling.csproj index f114f1c68..bd0deb3a0 100644 --- a/src/modules/Elsa.Scheduling/Elsa.Scheduling.csproj +++ b/src/modules/Elsa.Scheduling/Elsa.Scheduling.csproj @@ -12,6 +12,7 @@ + diff --git a/src/modules/Elsa.Scheduling/Features/SchedulingFeature.cs b/src/modules/Elsa.Scheduling/Features/SchedulingFeature.cs index ec84b8180..79b064e06 100644 --- a/src/modules/Elsa.Scheduling/Features/SchedulingFeature.cs +++ b/src/modules/Elsa.Scheduling/Features/SchedulingFeature.cs @@ -27,6 +27,7 @@ public class SchedulingFeature : FeatureBase Services .AddSingleton() .AddSingleton() + .AddSingleton() .AddNotificationHandlersFrom(); Module.Configure(management => management.AddActivitiesFrom()); diff --git a/src/modules/Elsa.Scheduling/Implementations/ElasticCongurationScheduler.cs b/src/modules/Elsa.Scheduling/Implementations/ElasticCongurationScheduler.cs new file mode 100644 index 000000000..1279872fb --- /dev/null +++ b/src/modules/Elsa.Scheduling/Implementations/ElasticCongurationScheduler.cs @@ -0,0 +1,28 @@ +using Elsa.Jobs.Schedules; +using Elsa.Jobs.Services; +using Elsa.Scheduling.Jobs; +using Elsa.Scheduling.Services; + +namespace Elsa.Scheduling.Implementations; + +public class ElasticCongurationScheduler : IElasticCongurationScheduler +{ + private readonly IJobScheduler _jobScheduler; + + public ElasticCongurationScheduler(IJobScheduler jobScheduler) + { + _jobScheduler = jobScheduler; + } + + public async Task ScheduleAsync(CancellationToken cancellationToken = default) + { + var job = new ConfigureElasticIndicesJob(); + var schedule = new CronSchedule + { + //Last day of every month + CronExpression = "0 18 L * ?" + }; + + await _jobScheduler.ScheduleAsync(job, GetType().Name, schedule, cancellationToken: cancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Scheduling/Jobs/ConfigureElasticIndicesJob.cs b/src/modules/Elsa.Scheduling/Jobs/ConfigureElasticIndicesJob.cs new file mode 100644 index 000000000..3a09518ce --- /dev/null +++ b/src/modules/Elsa.Scheduling/Jobs/ConfigureElasticIndicesJob.cs @@ -0,0 +1,38 @@ +using System.Text.Json.Serialization; +using Elsa.Elasticsearch.Common; +using Elsa.Jobs.Models; +using Nest; +using Job = Elsa.Jobs.Abstractions.Job; + +namespace Elsa.Scheduling.Jobs; + +public class ConfigureElasticIndicesJob : Job +{ + [JsonConstructor] + public ConfigureElasticIndicesJob() + { + } + + protected override async ValueTask ExecuteAsync(JobExecutionContext context) + { + var client = context.GetRequiredService(); + var indexAliasGroups = await client.Indices.GetAliasAsync(); + + foreach (var group in indexAliasGroups.Indices) + { + // Only 1 alias exists per index in Elsa Elasticsearch configuration + var aliasPointingCurrentIndex = group.Value.Aliases.Keys.Single(); + + var currentIndexName = group.Key.Name; + var newIndexName = Utils.GenerateIndexName(aliasPointingCurrentIndex); + + var indexExists = (await client.Indices.ExistsAsync(newIndexName)).Exists; + if (indexExists) continue; + + // Point the alias to the new index + await client.Indices.BulkAliasAsync(aliases => aliases + .Remove(a => a.Alias(aliasPointingCurrentIndex).Index(currentIndexName)) + .Add(a => a.Alias(aliasPointingCurrentIndex).Index(newIndexName))); + } + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Scheduling/Services/IElasticCongurationScheduler.cs b/src/modules/Elsa.Scheduling/Services/IElasticCongurationScheduler.cs new file mode 100644 index 000000000..ad63b296a --- /dev/null +++ b/src/modules/Elsa.Scheduling/Services/IElasticCongurationScheduler.cs @@ -0,0 +1,8 @@ +using Elsa.Workflows.Core.Models; + +namespace Elsa.Scheduling.Services; + +public interface IElasticCongurationScheduler +{ + Task ScheduleAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file