< Summary

Information
Class: Elsa.Workflows.Runtime.ShellFeatures.WorkflowRuntimeFeature
Assembly: Elsa.Workflows.Runtime
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs
Line coverage
16%
Covered lines: 37
Uncovered lines: 185
Coverable lines: 222
Total lines: 394
Line coverage: 16.6%
Branch coverage
66%
Covered branches: 12
Total branches: 18
Branch coverage: 66.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs

#LineLine coverage
 1using System.Diagnostics.CodeAnalysis;
 2using System.Reflection;
 3using CShells.Features;
 4using Elsa.Common;
 5using Elsa.Common.RecurringTasks;
 6using Elsa.Extensions;
 7using Elsa.Mediator.Contracts;
 8using Elsa.Workflows.CommitStates;
 9using Elsa.Workflows.Management;
 10using Elsa.Workflows.Management.Contracts;
 11using Elsa.Workflows.Management.Services;
 12using Elsa.Workflows.Options;
 13using Elsa.Workflows.Runtime.ActivationValidators;
 14using Elsa.Workflows.Runtime.Discovery;
 15using Elsa.Workflows.Runtime.Entities;
 16using Elsa.Workflows.Runtime.Handlers;
 17using Elsa.Workflows.Runtime.Options;
 18using Elsa.Workflows.Runtime.Providers;
 19using Elsa.Workflows.Runtime.Services;
 20using Elsa.Workflows.Runtime.Stores;
 21using Elsa.Workflows.Runtime.Tasks;
 22using Elsa.Workflows.Runtime.UIHints;
 23using Medallion.Threading;
 24using Medallion.Threading.FileSystem;
 25using Microsoft.Extensions.DependencyInjection;
 26using Microsoft.Extensions.DependencyInjection.Extensions;
 27using Microsoft.Extensions.Options;
 28using Elsa.Common.Serialization;
 29
 30namespace Elsa.Workflows.Runtime.ShellFeatures;
 31
 32/// <summary>
 33/// Installs and configures workflow runtime features.
 34/// </summary>
 35[ShellFeature(
 36    DisplayName = "Workflow Runtime",
 37    Description = "Provides workflow execution runtime and scheduling capabilities",
 38    DependsOn = ["Workflows"])]
 39public class WorkflowRuntimeFeature : IShellFeature
 40{
 1841    private IDictionary<string, DispatcherChannel> WorkflowDispatcherChannels { get; set; } = new Dictionary<string, Dis
 42
 43    /// <summary>
 44    /// A list of workflow builders configured during application startup.
 45    /// </summary>
 3046    public IDictionary<string, Func<IServiceProvider, ValueTask<IWorkflow>>> Workflows { get; set; } = new Dictionary<st
 2247    private ISet<Type> WorkflowTypes { get; } = new HashSet<Type>();
 48
 49    /// <summary>
 50    /// A factory that instantiates a concrete <see cref="IWorkflowRuntime"/>.
 51    /// </summary>
 1852    public Func<IServiceProvider, IWorkflowRuntime> WorkflowRuntime { get; set; } = sp => ActivatorUtilities.CreateInsta
 53
 54    /// <summary>
 55    /// A factory that instantiates an <see cref="IWorkflowDispatcher"/>.
 56    /// </summary>
 1857    public Func<IServiceProvider, IWorkflowDispatcher> WorkflowDispatcher { get; set; } = sp =>
 1858    {
 059        var decoratedService = ActivatorUtilities.CreateInstance<BackgroundWorkflowDispatcher>(sp);
 060        var transactionalService = ActivatorUtilities.CreateInstance<TransactionalWorkflowDispatcher>(sp, decoratedServi
 061        return ActivatorUtilities.CreateInstance<ValidatingWorkflowDispatcher>(sp, transactionalService);
 1862    };
 63
 64    /// <summary>
 65    /// A factory that instantiates an <see cref="IStimulusDispatcher"/>.
 66    /// </summary>
 1867    public Func<IServiceProvider, IStimulusDispatcher> StimulusDispatcher { get; set; } = sp => ActivatorUtilities.Creat
 68
 69    /// <summary>
 70    /// A factory that instantiates an <see cref="IWorkflowCancellationDispatcher"/>.
 71    /// </summary>
 1872    public Func<IServiceProvider, IWorkflowCancellationDispatcher> WorkflowCancellationDispatcher { get; set; } = sp => 
 73
 74    /// <summary>
 75    /// A factory that instantiates an <see cref="IWorkflowDispatchOutboxStore"/>.
 76    /// </summary>
 1877    public Func<IServiceProvider, IWorkflowDispatchOutboxStore> WorkflowDispatchOutboxStore { get; set; } = sp => Activa
 78
 79    /// <summary>
 80    /// A factory that instantiates an <see cref="IBookmarkStore"/>.
 81    /// </summary>
 1882    public Func<IServiceProvider, IBookmarkStore> BookmarkStore { get; set; } = sp => sp.GetRequiredService<MemoryBookma
 83
 84    /// <summary>
 85    /// A factory that instantiates an <see cref="IBookmarkQueueStore"/>.
 86    /// </summary>
 1887    public Func<IServiceProvider, IBookmarkQueueStore> BookmarkQueueStore { get; set; } = sp => sp.GetRequiredService<Me
 88
 89    /// <summary>
 90    /// A factory that instantiates an <see cref="IBookmarkQueueDeadLetterStore"/>.
 91    /// </summary>
 1892    public Func<IServiceProvider, IBookmarkQueueDeadLetterStore> BookmarkQueueDeadLetterStore { get; set; } = sp => sp.G
 93
 94    /// <summary>
 95    /// A factory that instantiates an <see cref="ITriggerStore"/>.
 96    /// </summary>
 1897    public Func<IServiceProvider, ITriggerStore> TriggerStore { get; set; } = sp => sp.GetRequiredService<MemoryTriggerS
 98
 99    /// <summary>
 100    /// A factory that instantiates an <see cref="IWorkflowExecutionLogStore"/>.
 101    /// </summary>
 18102    public Func<IServiceProvider, IWorkflowExecutionLogStore> WorkflowExecutionLogStore { get; set; } = sp => sp.GetRequ
 103
 104    /// <summary>
 105    /// A factory that instantiates an <see cref="IActivityExecutionStore"/>.
 106    /// </summary>
 18107    public Func<IServiceProvider, IActivityExecutionStore> ActivityExecutionLogStore { get; set; } = sp => sp.GetRequire
 108
 109    /// <summary>
 110    /// A factory that instantiates an <see cref="IDistributedLockProvider"/>.
 111    /// </summary>
 18112    public Func<IServiceProvider, IDistributedLockProvider> DistributedLockProvider { get; set; } = _ => new FileDistrib
 113
 114    /// <summary>
 115    /// A factory that instantiates an <see cref="ITaskDispatcher"/>.
 116    /// </summary>
 18117    public Func<IServiceProvider, ITaskDispatcher> RunTaskDispatcher { get; set; } = sp => sp.GetRequiredService<Backgro
 118
 119    /// <summary>
 120    /// A factory that instantiates an <see cref="IBackgroundActivityScheduler"/>.
 121    /// </summary>
 18122    public Func<IServiceProvider, IBackgroundActivityScheduler> BackgroundActivityScheduler { get; set; } = sp => Activa
 123
 124    /// <summary>
 125    /// A factory that instantiates a log record sink for an <see cref="ActivityExecutionRecord"/>.
 126    /// </summary>
 18127    public Func<IServiceProvider, ILogRecordSink<ActivityExecutionRecord>> ActivityExecutionLogSink { get; set; } = sp =
 128
 129    /// <summary>
 130    /// A factory that instantiates a log record sink for an <see cref="WorkflowExecutionLogRecord"/>.
 131    /// </summary>
 18132    public Func<IServiceProvider, ILogRecordSink<WorkflowExecutionLogRecord>> WorkflowExecutionLogSink { get; set; } = s
 133
 134    /// <summary>
 135    /// A factory that instantiates an <see cref="ICommandHandler"/>.
 136    /// </summary>
 18137    public Func<IServiceProvider, ICommandHandler> DispatchWorkflowCommandHandler { get; set; } = sp => sp.GetRequiredSe
 138
 139    /// <summary>
 140    /// A factory that instantiates an <see cref="IWorkflowResumer"/>.
 141    /// </summary>
 18142    public Func<IServiceProvider, IWorkflowResumer> WorkflowResumer { get; set; } = sp => sp.GetRequiredService<Workflow
 143
 144    /// <summary>
 145    /// A factory that instantiates an <see cref="IBookmarkQueueWorker"/>.
 146    /// </summary>
 18147    public Func<IServiceProvider, IBookmarkQueueWorker> BookmarkQueueWorker { get; set; } = sp => sp.GetRequiredService<
 148
 149    /// <summary>
 150    /// Callback that tunes the graceful-shutdown machinery (drain deadline, per-source pause timeout, stimulus-queue ba
 151    /// pause-persistence policy). Applied when <see cref="ConfigureServices"/> binds <see cref="GracefulShutdownOptions
 152    /// </summary>
 0153    public GracefulShutdownOptions? GracefulShutdown { get; set; }
 154
 155    /// <summary>
 156    /// Register the specified workflow type.
 157    /// </summary>
 158    public WorkflowRuntimeFeature AddWorkflow<T>() where T : IWorkflow
 159    {
 0160        return AddWorkflow(typeof(T));
 161    }
 162
 163    /// <summary>
 164    /// Register the specified workflow type.
 165    /// </summary>
 166    public WorkflowRuntimeFeature AddWorkflow(Type workflowType)
 167    {
 6168        Workflows.Add(workflowType);
 2169        WorkflowTypes.Add(workflowType);
 2170        return this;
 171    }
 172
 173    /// <summary>
 174    /// Register all workflows in the specified assembly.
 175    /// </summary>
 176    [RequiresUnreferencedCode("The assembly is required to be referenced.")]
 177    public WorkflowRuntimeFeature AddWorkflowsFrom(Assembly assembly)
 178    {
 0179        foreach (var workflowType in WorkflowTypeScanner.GetWorkflowTypes(assembly))
 0180            AddWorkflow(workflowType);
 181
 0182        return this;
 183    }
 184
 185    public void ConfigureServices(IServiceCollection services)
 186    {
 187        // Options.
 0188        services.Configure<SerializationTypeOptions>(RegisterWorkflowTypeAliases);
 0189        services.Configure<RuntimeOptions>(options => { options.Workflows = Workflows; });
 0190        services.Configure<WorkflowDispatcherOptions>(options =>
 0191        {
 0192            options.Channels.AddRange(WorkflowDispatcherChannels.Values);
 0193        });
 0194        services.AddGracefulShutdownOptions(options =>
 0195        {
 0196            if (GracefulShutdown == null)
 0197                return;
 0198
 0199            options.DrainDeadline = GracefulShutdown.DrainDeadline;
 0200            options.IngressPauseTimeout = GracefulShutdown.IngressPauseTimeout;
 0201            options.StimulusQueueMaxDepthWhilePaused = GracefulShutdown.StimulusQueueMaxDepthWhilePaused;
 0202            options.OverflowPolicy = GracefulShutdown.OverflowPolicy;
 0203            options.PausePersistence = GracefulShutdown.PausePersistence;
 0204            options.MaxForceCancelledInstanceIdsReported = GracefulShutdown.MaxForceCancelledInstanceIdsReported;
 0205        });
 206
 0207        services
 0208            // Graceful-shutdown core (US1 â€” quiescence machinery).
 0209            // Per-shell QuiescenceSignal: the persistence key includes the shell id so multi-shell deployments under
 0210            // PausePersistencePolicy.AcrossReactivations don't collide on a single key. Falls back to "default" only
 0211            // when ShellSettings isn't in the DI graph (degenerate single-shell or non-CShells host).
 0212            .AddSingleton<IQuiescenceSignal>(sp => new QuiescenceSignal(
 0213                sp.GetRequiredService<IOptions<GracefulShutdownOptions>>(),
 0214                sp.GetRequiredService<ISystemClock>(),
 0215                sp.GetRequiredService<IExecutionCycleRegistry>(),
 0216                sp.GetRequiredService<IServiceScopeFactory>(),
 0217                shellName: sp.GetService<CShells.ShellSettings>()?.Id))
 0218            .AddSingleton<IIngressSourceRegistry, IngressSourceRegistry>()
 0219            .AddSingleton<IExecutionCycleRegistry, ExecutionCycleRegistry>()
 0220            .AddSingleton(sp => new Lazy<IEnumerable<IIngressSource>>(sp.GetServices<IIngressSource>))
 0221            .AddSingleton<IDrainOrchestrator, DrainOrchestrator>()
 0222            .AddScoped<IWorkflowRuntimeAdminService, WorkflowRuntimeAdminService>()
 0223            .AddTransient<CShells.Lifecycle.IDrainHandler, Lifecycle.ElsaShellDrainHandler>()
 0224            .AddScoped<IInterruptedRecoveryScanner, InterruptedRecoveryScanner>()
 0225            .AddStartupTask<StartupTasks.RecoverInterruptedWorkflowsStartupTask>()
 0226            .AddSingleton<IIngressSource, IngressSources.InternalBookmarkQueueIngressSource>()
 0227            .AddTransient<CShells.Lifecycle.IShellInitializer, Lifecycle.InitializePauseStateShellInitializer>()
 0228
 0229            // Core.
 0230            .AddScoped<ITriggerIndexer, TriggerIndexer>()
 0231            .AddScoped<IWorkflowInstanceFactory, WorkflowInstanceFactory>()
 0232            .AddScoped<IWorkflowHostFactory, WorkflowHostFactory>()
 0233            .AddScoped<IBackgroundActivityInvoker, BackgroundActivityInvoker>()
 0234            .AddScoped(WorkflowRuntime)
 0235            .AddScoped(WorkflowDispatcher)
 0236            .AddScoped(StimulusDispatcher)
 0237            .AddScoped(WorkflowCancellationDispatcher)
 0238            .AddScoped(RunTaskDispatcher)
 0239            .AddScoped(ActivityExecutionLogSink)
 0240            .AddScoped(WorkflowExecutionLogSink)
 0241            .AddSingleton(BackgroundActivityScheduler)
 0242            .AddSingleton<RandomLongIdentityGenerator>()
 0243            .AddSingleton<IBookmarkQueueSignaler, BookmarkQueueSignaler>()
 0244            .AddScoped(BookmarkQueueWorker)
 0245            .AddScoped<IBookmarkManager, DefaultBookmarkManager>()
 0246            .AddScoped<IActivityExecutionManager, DefaultActivityExecutionManager>()
 0247            .AddScoped<IActivityExecutionStatsService, ActivityExecutionStatsService>()
 0248            .AddScoped<IActivityExecutionMapper, DefaultActivityExecutionMapper>()
 0249            .AddScoped<IWorkflowDefinitionStorePopulator, DefaultWorkflowDefinitionStorePopulator>()
 0250            .AddScoped<IRegistriesPopulator, DefaultRegistriesPopulator>()
 0251            .AddScoped<IWorkflowDefinitionsRefresher, WorkflowDefinitionsRefresher>()
 0252            .AddScoped<IWorkflowDefinitionsReloader, WorkflowDefinitionsReloader>()
 0253            .AddScoped<IWorkflowRegistry, DefaultWorkflowRegistry>()
 0254            .AddScoped<IWorkflowMatcher, WorkflowMatcher>()
 0255            .AddScoped<IWorkflowInvoker, WorkflowInvoker>()
 0256            .AddScoped<IStimulusSender, StimulusSender>()
 0257            .AddScoped<ITriggerBoundWorkflowService, TriggerBoundWorkflowService>()
 0258            .AddScoped<IBookmarkBoundWorkflowService, BookmarkBoundWorkflowService>()
 0259            .AddScoped<ITaskReporter, TaskReporter>()
 0260            .AddScoped<SynchronousTaskDispatcher>()
 0261            .AddScoped<BackgroundTaskDispatcher>()
 0262            .AddScoped<StoreActivityExecutionLogSink>()
 0263            .AddScoped<StoreWorkflowExecutionLogSink>()
 0264            .AddScoped<DispatchWorkflowCommandHandler>()
 0265            .AddScoped<IEventPublisher, EventPublisher>()
 0266            .AddScoped<IBookmarkUpdater, BookmarkUpdater>()
 0267            .AddScoped<IBookmarksPersister, BookmarksPersister>()
 0268            .AddScoped<IBookmarkResumer, BookmarkResumer>()
 0269            .AddScoped<IBookmarkQueue, StoreBookmarkQueue>()
 0270            .AddScoped<IBookmarkQueueDeadLetterManager, BookmarkQueueDeadLetterManager>()
 0271            .AddScoped<WorkflowResumer>()
 0272            .AddScoped<BookmarkQueueWorker>()
 0273            .AddScoped(WorkflowResumer)
 0274            .AddScoped<ITriggerInvoker, TriggerInvoker>()
 0275            .AddScoped<IWorkflowCanceler, WorkflowCanceler>()
 0276            .AddScoped<IWorkflowCancellationService, WorkflowCancellationService>()
 0277            .AddScoped<IWorkflowActivationStrategyEvaluator, DefaultWorkflowActivationStrategyEvaluator>()
 0278            .AddScoped<IWorkflowStarter, DefaultWorkflowStarter>()
 0279            .AddScoped<IWorkflowRestarter, DefaultWorkflowRestarter>()
 0280            .AddScoped<IBookmarkQueuePurger, DefaultBookmarkQueuePurger>()
 0281            .AddSingleton<IWorkflowDispatchOutboxAccessor, WorkflowDispatchOutboxAccessor>()
 0282            .AddScoped<ILogRecordExtractor<WorkflowExecutionLogRecord>, WorkflowExecutionLogRecordExtractor>()
 0283            .AddScoped<IActivityPropertyLogPersistenceEvaluator, ActivityPropertyLogPersistenceEvaluator>()
 0284            .AddScoped<IBookmarkQueueProcessor, BookmarkQueueProcessor>()
 0285            .AddScoped<DefaultCommitStateHandler>()
 0286            // Decorator: disposes the execution cycle handle AFTER the workflow runner's terminal commit has persisted 
 0287            // so the drain orchestrator's force-cancel path can sequence its Interrupted write to land last.
 0288            .AddScoped<ICommitStateHandler, ExecutionCycleAwareCommitStateHandler>()
 0289            .AddScoped<WorkflowHeartbeatGeneratorFactory>()
 0290
 0291            // Deprecated services.
 0292            .AddScoped<IWorkflowInbox, StimulusProxyWorkflowInbox>()
 0293
 0294            // Stores.
 0295            .AddScoped(BookmarkStore)
 0296            .AddScoped(BookmarkQueueStore)
 0297            .AddScoped(BookmarkQueueDeadLetterStore)
 0298            .AddScoped(TriggerStore)
 0299            .AddScoped(WorkflowExecutionLogStore)
 0300            .AddScoped(ActivityExecutionLogStore)
 0301
 0302            // Lazy services.
 0303            .AddScoped<Func<IEnumerable<IWorkflowsProvider>>>(sp => sp.GetServices<IWorkflowsProvider>)
 0304            .AddScoped<Func<IEnumerable<IWorkflowMaterializer>>>(sp => sp.GetServices<IWorkflowMaterializer>)
 0305
 0306            // Noop stores.
 0307            .AddScoped<MemoryWorkflowExecutionLogStore>()
 0308            .AddScoped<MemoryActivityExecutionStore>()
 0309
 0310            // Memory stores.
 0311            .AddMemoryStore<StoredBookmark, MemoryBookmarkStore>()
 0312            .AddMemoryStore<StoredTrigger, MemoryTriggerStore>()
 0313            .AddMemoryStore<BookmarkQueueItem, MemoryBookmarkQueueStore>()
 0314            .AddMemoryStore<BookmarkQueueDeadLetterItem, MemoryBookmarkQueueDeadLetterStore>()
 0315            .AddMemoryStore<WorkflowExecutionLogRecord, MemoryWorkflowExecutionLogStore>()
 0316            .AddMemoryStore<ActivityExecutionRecord, MemoryActivityExecutionStore>()
 0317
 0318            // Startup tasks, background tasks, and recurring tasks.
 0319            .AddStartupTask<PopulateRegistriesStartupTask>()
 0320            .AddRecurringTask<TriggerBookmarkQueueRecurringTask>(TimeSpan.FromMinutes(1))
 0321            .AddRecurringTask<PurgeBookmarkQueueRecurringTask>(TimeSpan.FromSeconds(10))
 0322            .AddRecurringTask<RestartInterruptedWorkflowsTask>(TimeSpan.FromMinutes(5)) // Same default as the workflow 
 0323            .AddRecurringTask<ProcessWorkflowDispatchOutboxRecurringTask>(TimeSpan.FromSeconds(10))
 0324
 0325            // Distributed locking.
 0326            .AddSingleton(DistributedLockProvider)
 0327
 0328            // Workflow providers.
 0329            .AddWorkflowsProvider<ClrWorkflowsProvider>()
 0330
 0331            // UI property handlers.
 0332            .AddScoped<IPropertyUIHandler, DispatcherChannelOptionsProvider>()
 0333
 0334            // Domain handlers.
 0335            .AddCommandHandler<DispatchWorkflowCommandHandler>()
 0336            .AddCommandHandler<DispatchStimulusCommandHandler>()
 0337            .AddCommandHandler<CancelWorkflowsCommandHandler>()
 0338            .AddNotificationHandler<ResumeDispatchWorkflowActivity>()
 0339            .AddNotificationHandler<ResumeBulkDispatchWorkflowActivity>()
 0340            .AddNotificationHandler<ProcessWorkflowDispatchOutbox>()
 0341            .AddNotificationHandler<ResumeExecuteWorkflowActivity>()
 0342            .AddNotificationHandler<IndexTriggers>()
 0343            .AddNotificationHandler<CancelBackgroundActivities>()
 0344            .AddNotificationHandler<DeleteBookmarks>()
 0345            .AddNotificationHandler<DeleteTriggers>()
 0346            .AddNotificationHandler<DeleteActivityExecutionLogRecords>()
 0347            .AddNotificationHandler<DeleteWorkflowExecutionLogRecords>()
 0348            .AddNotificationHandler<RefreshActivityRegistry>()
 0349            .AddNotificationHandler<SignalBookmarkQueueWorker>()
 0350            .AddNotificationHandler<EvaluateParentLogPersistenceModes>()
 0351            .AddNotificationHandler<CaptureActivityExecutionState>()
 0352            .AddNotificationHandler<ValidateWorkflowRequestHandler>()
 0353
 0354            // Workflow activation strategies.
 0355            .AddScoped<IWorkflowActivationStrategy, SingletonStrategy>()
 0356            .AddScoped<IWorkflowActivationStrategy, CorrelatedSingletonStrategy>()
 0357            .AddScoped<IWorkflowActivationStrategy, CorrelationStrategy>()
 0358            ;
 359
 0360        services.TryAddScoped<IWorkflowDispatchOutbox>(sp => ActivatorUtilities.CreateInstance<WorkflowDispatchOutbox>(s
 0361        services.TryAddScoped(WorkflowDispatchOutboxStore);
 0362        services.TryAddScoped<IWorkflowDispatchOutboxProcessor, WorkflowDispatchOutboxProcessor>();
 0363    }
 364
 365    private void RegisterWorkflowTypeAliases(SerializationTypeOptions options)
 366    {
 2367        WorkflowRuntimeTypeAliasRegistrar.Register(options, GetRegisteredWorkflowTypes());
 2368    }
 369
 370    private IEnumerable<Type> GetRegisteredWorkflowTypes()
 371    {
 2372        return WorkflowTypes
 7373            .Concat(Workflows.Keys.Select(TryResolveWorkflowType).Where(type => type != null).Select(type => type!))
 2374            .Distinct();
 375    }
 376
 377    private static Type? TryResolveWorkflowType(string typeName)
 378    {
 379        Type? type;
 380
 381        try
 382        {
 5383            type = Type.GetType(typeName, false);
 5384        }
 0385        catch (Exception e) when (e is ArgumentException or FileLoadException or FileNotFoundException or TypeLoadExcept
 386        {
 0387            return null;
 388        }
 389
 5390        return type != null && typeof(IWorkflow).IsAssignableFrom(type) && type is { IsAbstract: false, IsInterface: fal
 5391            ? type
 5392            : null;
 0393    }
 394}