diff --git a/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj b/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj index bf4d53171..8801d54d9 100644 --- a/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj +++ b/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj @@ -14,6 +14,7 @@ + diff --git a/src/apps/Elsa.Server.Web/Program.cs b/src/apps/Elsa.Server.Web/Program.cs index fc36b8e1c..3e5a9b3f9 100644 --- a/src/apps/Elsa.Server.Web/Program.cs +++ b/src/apps/Elsa.Server.Web/Program.cs @@ -27,6 +27,8 @@ using Elsa.MongoDb.Modules.Management; using Elsa.MongoDb.Modules.Runtime; using Elsa.MongoDb.Modules.Tenants; using Elsa.OpenTelemetry.Middleware; +using Elsa.Retention.Extensions; +using Elsa.Retention.Models; using Elsa.Secrets.Extensions; using Elsa.Secrets.Management.Tasks; using Elsa.Secrets.Persistence; @@ -36,6 +38,7 @@ using Elsa.Server.Web.Filters; using Elsa.Server.Web.Messages; using Elsa.Tenants.AspNetCore; using Elsa.Tenants.Extensions; +using Elsa.Workflows; using Elsa.Workflows.Api; using Elsa.Workflows.LogPersistence; using Elsa.Workflows.Management; @@ -513,6 +516,19 @@ services .UseSecretsScripting() ; } + + elsa.UseRetention(r => + { + r.SweepInterval = TimeSpan.FromHours(5); + r.AddDeletePolicy("Delete all finished workflows", sp => + { + var filter = new RetentionWorkflowInstanceFilter + { + WorkflowStatus = WorkflowStatus.Finished + }; + return filter; + }); + }); if (useMultitenancy) { diff --git a/src/modules/Elsa.Common/Features/MultitenancyFeature.cs b/src/modules/Elsa.Common/Features/MultitenancyFeature.cs index 84facff7b..89bbb7d66 100644 --- a/src/modules/Elsa.Common/Features/MultitenancyFeature.cs +++ b/src/modules/Elsa.Common/Features/MultitenancyFeature.cs @@ -2,7 +2,6 @@ using Elsa.Common.Multitenancy; using Elsa.Common.Multitenancy.EventHandlers; using Elsa.Common.Multitenancy.HostedServices; using Elsa.Common.RecurringTasks; -using Elsa.Extensions; using Elsa.Features.Abstractions; using Elsa.Features.Services; using Microsoft.Extensions.DependencyInjection; @@ -52,7 +51,6 @@ public class MultitenancyFeature(IModule module) : FeatureBase(module) .AddSingleton() .AddSingleton() .AddSingleton() - .AddStartupTask() .AddScoped() .AddScoped() .AddScoped() diff --git a/src/modules/Elsa.Common/Multitenancy/EventHandlers/StartRecurringTasks.cs b/src/modules/Elsa.Common/Multitenancy/EventHandlers/StartRecurringTasks.cs index c89c79c70..f83479af9 100644 --- a/src/modules/Elsa.Common/Multitenancy/EventHandlers/StartRecurringTasks.cs +++ b/src/modules/Elsa.Common/Multitenancy/EventHandlers/StartRecurringTasks.cs @@ -7,7 +7,7 @@ namespace Elsa.Common.Multitenancy.EventHandlers; public class StartRecurringTasks(RecurringTaskScheduleManager scheduleManager, ILogger logger) : ITenantActivatedEvent, ITenantDeactivatedEvent { private readonly ICollection _scheduledTimers = new List(); - private CancellationTokenSource _cancellationTokenSource = default!; + private CancellationTokenSource _cancellationTokenSource = null!; public async Task TenantActivatedAsync(TenantActivatedEventArgs args) { @@ -16,7 +16,7 @@ public class StartRecurringTasks(RecurringTaskScheduleManager scheduleManager, I var tenantScope = args.TenantScope; var tasks = tenantScope.ServiceProvider.GetServices().ToList(); var taskExecutor = tenantScope.ServiceProvider.GetRequiredService(); - + foreach (var task in tasks) { var schedule = scheduleManager.GetScheduleFor(task.GetType()); diff --git a/src/modules/Elsa.Common/RecurringTasks/ConfigureRecurringTasksScheduleStartupTask.cs b/src/modules/Elsa.Common/RecurringTasks/ConfigureRecurringTasksScheduleStartupTask.cs deleted file mode 100644 index a376f3b8e..000000000 --- a/src/modules/Elsa.Common/RecurringTasks/ConfigureRecurringTasksScheduleStartupTask.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Cronos; -using JetBrains.Annotations; -using Microsoft.Extensions.Options; - -namespace Elsa.Common.RecurringTasks; - -[UsedImplicitly] -public class ConfigureRecurringTasksScheduleStartupTask(IOptions options, ISystemClock systemClock, RecurringTaskScheduleManager recurringTaskScheduleManager) : IStartupTask -{ - public Task ExecuteAsync(CancellationToken cancellationToken) - { - foreach(var entry in options.Value.Schedule.ScheduledTasks) - { - var taskType = entry.Key; - var intervalExpression = entry.Value; - var schedule = intervalExpression.Type switch - { - IntervalExpressionType.Cron => (ISchedule)new CronSchedule(systemClock, CronExpression.Parse(intervalExpression.Expression)), - IntervalExpressionType.Interval => new IntervalSchedule(TimeSpan.Parse(intervalExpression.Expression)), - _ => throw new NotSupportedException($"Interval expression type '{intervalExpression.Type}' is not supported.") - }; - recurringTaskScheduleManager.ConfigureScheduledTask(taskType, schedule); - } - return Task.CompletedTask; - } -} \ No newline at end of file diff --git a/src/modules/Elsa.Common/RecurringTasks/RecurringTaskScheduleManager.cs b/src/modules/Elsa.Common/RecurringTasks/RecurringTaskScheduleManager.cs index b5418383f..fbb286d56 100644 --- a/src/modules/Elsa.Common/RecurringTasks/RecurringTaskScheduleManager.cs +++ b/src/modules/Elsa.Common/RecurringTasks/RecurringTaskScheduleManager.cs @@ -1,21 +1,31 @@ +using Cronos; +using Microsoft.Extensions.Options; + namespace Elsa.Common.RecurringTasks; -public class RecurringTaskScheduleManager +public class RecurringTaskScheduleManager(IOptions options, ISystemClock systemClock) { public IDictionary ScheduledTasks { get; set; } = new Dictionary(); - public void ConfigureScheduledTask(ISchedule schedule) where T : IRecurringTask - { - ConfigureScheduledTask(typeof(T), schedule); - } - - public void ConfigureScheduledTask(Type recurringTaskType, ISchedule schedule) - { - ScheduledTasks[recurringTaskType] = schedule; - } - public ISchedule GetScheduleFor(Type taskType) { - return ScheduledTasks.TryGetValue(taskType, out var schedule) ? schedule : new IntervalSchedule(TimeSpan.FromMinutes(1)); + if (!ScheduledTasks.TryGetValue(taskType, out var schedule)) + { + var intervalExpression = options.Value.Schedule.ScheduledTasks.TryGetValue(taskType, out var expr) ? expr : null; + schedule = intervalExpression != null ? CreateSchedule(intervalExpression) : new IntervalSchedule(TimeSpan.FromMinutes(1)); + ScheduledTasks[taskType] = schedule; + } + + return schedule; + } + + private ISchedule CreateSchedule(IntervalExpression intervalExpression) + { + return intervalExpression.Type switch + { + IntervalExpressionType.Cron => new CronSchedule(systemClock, CronExpression.Parse(intervalExpression.Expression)), + IntervalExpressionType.Interval => new IntervalSchedule(TimeSpan.Parse(intervalExpression.Expression)), + _ => throw new NotSupportedException($"Interval expression type '{intervalExpression.Type}' is not supported.") + }; } } \ No newline at end of file