diff --git a/src/modules/Elsa.Scheduling/Activities/Delay.cs b/src/modules/Elsa.Scheduling/Activities/Delay.cs index 0cd8b769a..1f525a7a5 100644 --- a/src/modules/Elsa.Scheduling/Activities/Delay.cs +++ b/src/modules/Elsa.Scheduling/Activities/Delay.cs @@ -1,5 +1,5 @@ -using System; -using Elsa.Common.Services; +using Elsa.Common.Services; +using Elsa.Scheduling.Models; using Elsa.Workflows.Core; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; @@ -18,16 +18,58 @@ public class Delay : Activity public Delay(Variable timeSpan) => TimeSpan = new Input(timeSpan); [Input] public Input TimeSpan { get; set; } = default!; + + [Input] public Input Strategy { get; set; } = default!; + + /// + /// The threshold used by the + /// + [Input] public Input AutoBlockingThreshold { get; set; } = new(System.TimeSpan.FromSeconds(5)); - protected override void Execute(ActivityExecutionContext context) + /// + protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) { var timeSpan = context.ExpressionExecutionContext.Get(TimeSpan); + var blockingMode = context.Get(Strategy); + + switch (blockingMode) + { + case DelayBlockingStrategy.Blocking: + await BlockingStrategy(timeSpan); + break; + case DelayBlockingStrategy.NonBlocking: + await NonBlockingStrategy(timeSpan, context); + break; + case DelayBlockingStrategy.Auto: + await AutoBlockingStrategy(timeSpan, context); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private async ValueTask BlockingStrategy(TimeSpan timeSpan) => await Task.Delay(timeSpan); + + private ValueTask NonBlockingStrategy(TimeSpan timeSpan, ActivityExecutionContext context) + { var clock = context.ExpressionExecutionContext.GetRequiredService(); var resumeAt = clock.UtcNow.Add(timeSpan); var payload = new DelayPayload(resumeAt); context.JournalData.Add("ResumeAt", resumeAt); context.CreateBookmark(payload); + + return ValueTask.CompletedTask; + } + + private async ValueTask AutoBlockingStrategy(TimeSpan timeSpan, ActivityExecutionContext context) + { + var threshold = context.Get(AutoBlockingThreshold); + + if (timeSpan <= threshold) + await BlockingStrategy(timeSpan); + else + await NonBlockingStrategy(timeSpan, context); } public static Delay FromMilliseconds(double value) => new(System.TimeSpan.FromMilliseconds(value)); diff --git a/src/modules/Elsa.Scheduling/Models/DelayBlockingStrategy.cs b/src/modules/Elsa.Scheduling/Models/DelayBlockingStrategy.cs new file mode 100644 index 000000000..58f16d877 --- /dev/null +++ b/src/modules/Elsa.Scheduling/Models/DelayBlockingStrategy.cs @@ -0,0 +1,24 @@ +using Elsa.Scheduling.Activities; + +namespace Elsa.Scheduling.Models; + +/// +/// Represents an execution mode for . +/// +public enum DelayBlockingStrategy +{ + /// + /// Do not suspend the workflow, but instead wait + /// + NonBlocking, + + /// + /// Suspend the workflow and schedule a background timer to resume the workflow. + /// + Blocking, + + /// + /// If the delay is less than a configurable amount of time, behaves as , otherwise as . + /// + Auto +} \ No newline at end of file