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