diff --git a/Cleipnir.ResilientFunctions b/Cleipnir.ResilientFunctions index e5ec420..5491be3 160000 --- a/Cleipnir.ResilientFunctions +++ b/Cleipnir.ResilientFunctions @@ -1 +1 @@ -Subproject commit e5ec420fc1de4059050cf5c9587feae23237f147 +Subproject commit 5491be3b98f5ea89dfaf473708692433b6327b10 diff --git a/Cleipnir.Tests.AspNet/BulkPublishTests.cs b/Cleipnir.Tests.AspNet/BulkPublishTests.cs index d962329..a914b89 100644 --- a/Cleipnir.Tests.AspNet/BulkPublishTests.cs +++ b/Cleipnir.Tests.AspNet/BulkPublishTests.cs @@ -25,7 +25,7 @@ public Task MultipleMessageCanBePublishedUsingSqlServer() private async Task MultipleMessageCanBePublished(Task storeTask) { - using var container = FlowsContainer.Create(functionStore: await storeTask); + using var container = await FlowsContainer.CreateAndStart(functionStore: await storeTask); var flows = container.RegisterAnonymousFlow(() => new TestFlow(), flowName: $"TestFlow#{Guid.NewGuid()}"); var instances = Enumerable.Range(0, 100) diff --git a/Cleipnir.Tests/Flows/FlowFactoryTests.cs b/Cleipnir.Tests/Flows/FlowFactoryTests.cs index 86e4d5f..b0077db 100644 --- a/Cleipnir.Tests/Flows/FlowFactoryTests.cs +++ b/Cleipnir.Tests/Flows/FlowFactoryTests.cs @@ -9,7 +9,7 @@ public class FlowFactoryTests [TestMethod] public async Task FlowFactoryForTestFlowWithResultIsUsedWhenProvided() { - var flowsContainer = FlowsContainer.Create(); + var flowsContainer = await FlowsContainer.CreateAndStart(); var flows = flowsContainer.RegisterAnonymousFlow(flowFactory: () => new TestFlowWithResult()); var result = await flows.Run("SomeInstance", "hallo world"); result.ShouldBe("HALLO WORLD"); @@ -30,7 +30,7 @@ public override Task Run(string param) [TestMethod] public async Task FlowFactoryForTestFlowWithParamIsUsedWhenProvided() { - var flowsContainer = FlowsContainer.Create(); + var flowsContainer = await FlowsContainer.CreateAndStart(); var flows = flowsContainer.RegisterAnonymousFlow(flowFactory: () => new TestFlowWithParam()); await flows.Run("SomeInstance", "hallo world"); @@ -55,7 +55,7 @@ public override Task Run(string param) [TestMethod] public async Task FlowFactoryForTestFlowWithoutParamIsUsedWhenProvided() { - var flowsContainer = FlowsContainer.Create(); + var flowsContainer = await FlowsContainer.CreateAndStart(); var flows = flowsContainer.RegisterAnonymousFlow(flowFactory: () => new TestFlowWithoutParam()); await flows.Run("SomeInstance"); diff --git a/Cleipnir.Tests/Flows/FlowsWithResultTests.cs b/Cleipnir.Tests/Flows/FlowsWithResultTests.cs index 0b1d08d..430d224 100644 --- a/Cleipnir.Tests/Flows/FlowsWithResultTests.cs +++ b/Cleipnir.Tests/Flows/FlowsWithResultTests.cs @@ -17,7 +17,7 @@ public async Task SimpleFlowCompletesSuccessfully() serviceCollection.AddTransient(); var flowStore = new InMemoryFunctionStore(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( flowStore, serviceCollection.BuildServiceProvider() ); @@ -63,7 +63,7 @@ public async Task CompletionOfScheduledFlowCanBeAwaited() serviceCollection.AddTransient(); var flowStore = new InMemoryFunctionStore(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( flowStore, serviceCollection.BuildServiceProvider(), new Settings(watchdogCheckFrequency: TimeSpan.FromMilliseconds(100)) @@ -100,7 +100,7 @@ public async Task EventDrivenFlowCompletesSuccessfully() serviceCollection.AddTransient(); var flowStore = new InMemoryFunctionStore(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( flowStore, serviceCollection.BuildServiceProvider(), new Settings(watchdogCheckFrequency: TimeSpan.FromMilliseconds(100)) @@ -148,7 +148,7 @@ public async Task FailingFlowCompletesWithError() serviceCollection.AddTransient(); var flowStore = new InMemoryFunctionStore(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( flowStore, serviceCollection.BuildServiceProvider() ); diff --git a/Cleipnir.Tests/Flows/FunctionRegistrationTests.cs b/Cleipnir.Tests/Flows/FunctionRegistrationTests.cs index 04c2336..250ac5e 100644 --- a/Cleipnir.Tests/Flows/FunctionRegistrationTests.cs +++ b/Cleipnir.Tests/Flows/FunctionRegistrationTests.cs @@ -11,7 +11,7 @@ public class FunctionRegistrationTests [TestMethod] public async Task FunctionCanBeRegisteredAndInvoked() { - using var container = FlowsContainer.Create(); + using var container = await FlowsContainer.CreateAndStart(); var registration = container.Functions.RegisterFunc( "TestFlow", inner: Task (string param) => param.ToUpper().ToTask() @@ -24,7 +24,7 @@ public async Task FunctionCanBeRegisteredAndInvoked() [TestMethod] public async Task FlowCanBeCreatedWithInitialState() { - var flowsContainer = FlowsContainer.Create(); + var flowsContainer = await FlowsContainer.CreateAndStart(); var flow = new InitialStateFlow(); var flows = flowsContainer.RegisterAnonymousFlow( flowFactory: () => flow diff --git a/Cleipnir.Tests/Flows/MultipleRegistrationTests.cs b/Cleipnir.Tests/Flows/MultipleRegistrationTests.cs index 5239bdd..b43eda2 100644 --- a/Cleipnir.Tests/Flows/MultipleRegistrationTests.cs +++ b/Cleipnir.Tests/Flows/MultipleRegistrationTests.cs @@ -1,4 +1,5 @@ using Cleipnir.ResilientFunctions.Storage; +using System.Threading.Tasks; using Cleipnir.ResilientFunctions.Domain; using Microsoft.Extensions.DependencyInjection; using Shouldly; @@ -9,21 +10,21 @@ namespace Cleipnir.Flows.Tests.Flows; public class MultipleRegistrationTests { [TestMethod] - public void SameFlowTypeWithSameNameCanBeRegisteredSeveralTimes() + public async Task SameFlowTypeWithSameNameCanBeRegisteredSeveralTimes() { var serviceCollection = new ServiceCollection(); var store = new InMemoryFunctionStore(); - var flowsContainer = new FlowsContainer(store, serviceCollection.BuildServiceProvider()); + var flowsContainer = await FlowsContainer.CreateAndStart(store, serviceCollection.BuildServiceProvider()); _ = new TestFlowType1s(flowsContainer); _ = new TestFlowType1s(flowsContainer); } [TestMethod] - public void DifferentFlowTypesWithSameNameCannotBeRegisteredSeveralTimes() + public async Task DifferentFlowTypesWithSameNameCannotBeRegisteredSeveralTimes() { var serviceCollection = new ServiceCollection(); var store = new InMemoryFunctionStore(); - var flowsContainer = new FlowsContainer(store, serviceCollection.BuildServiceProvider()); + var flowsContainer = await FlowsContainer.CreateAndStart(store, serviceCollection.BuildServiceProvider()); _ = new TestFlowType1s(flowsContainer); Should.Throw(() => new TestFlowType2s(flowsContainer) diff --git a/Cleipnir.Tests/Flows/OptionsTests.cs b/Cleipnir.Tests/Flows/OptionsTests.cs index 94ba01f..901092d 100644 --- a/Cleipnir.Tests/Flows/OptionsTests.cs +++ b/Cleipnir.Tests/Flows/OptionsTests.cs @@ -47,7 +47,7 @@ await Should.ThrowAsync( // Flow may be Executing (waiting for message) or Suspended depending on timing controlPanel.Status.ShouldBeOneOf(Status.Executing, Status.Suspended); - await controlPanel.Messages.Append(new StringWrapper("Hello")); + await flowsWithDefaultProvidedOptions.MessageWriter("Id").AppendMessage(new StringWrapper("Hello")); await controlPanel.WaitForCompletion(allowPostponeAndSuspended: true); } diff --git a/Cleipnir.Tests/Flows/ParamlessFlowsTests.cs b/Cleipnir.Tests/Flows/ParamlessFlowsTests.cs index d80b300..68afedc 100644 --- a/Cleipnir.Tests/Flows/ParamlessFlowsTests.cs +++ b/Cleipnir.Tests/Flows/ParamlessFlowsTests.cs @@ -17,7 +17,7 @@ public async Task SimpleFlowCompletesSuccessfully() serviceCollection.AddTransient(); var flowStore = new InMemoryFunctionStore(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( flowStore, serviceCollection.BuildServiceProvider() ); @@ -56,7 +56,7 @@ public async Task EventDrivenFlowCompletesSuccessfully() serviceCollection.AddTransient(); var flowStore = new InMemoryFunctionStore(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( flowStore, serviceCollection.BuildServiceProvider(), new Settings(watchdogCheckFrequency: TimeSpan.FromMilliseconds(100)) @@ -102,7 +102,7 @@ public async Task FailingFlowCompletesWithError() serviceCollection.AddTransient(); var flowStore = new InMemoryFunctionStore(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( flowStore, serviceCollection.BuildServiceProvider() ); @@ -146,7 +146,7 @@ public override Task Run() [TestMethod] public async Task FlowCanBeCreatedWithInitialState() { - var flowsContainer = FlowsContainer.Create(); + var flowsContainer = await FlowsContainer.CreateAndStart(); var flow = new InitialStateFlow(); var flows = flowsContainer.RegisterAnonymousFlow( flowFactory: () => flow diff --git a/Cleipnir.Tests/Flows/UnitFlowsTests.cs b/Cleipnir.Tests/Flows/UnitFlowsTests.cs index b8144e3..dfb9875 100644 --- a/Cleipnir.Tests/Flows/UnitFlowsTests.cs +++ b/Cleipnir.Tests/Flows/UnitFlowsTests.cs @@ -18,7 +18,7 @@ public async Task SimpleFlowCompletesSuccessfully() serviceCollection.AddTransient(); var flowStore = new InMemoryFunctionStore(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( flowStore, serviceCollection.BuildServiceProvider() ); @@ -60,7 +60,7 @@ public async Task EventDrivenFlowCompletesSuccessfully() serviceCollection.AddTransient(); var flowStore = new InMemoryFunctionStore(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( flowStore, serviceCollection.BuildServiceProvider(), new Settings(watchdogCheckFrequency: TimeSpan.FromMilliseconds(100)) @@ -107,7 +107,7 @@ public async Task FailingActionFlowCompletesWithError() serviceCollection.AddTransient(); var flowStore = new InMemoryFunctionStore(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( flowStore, serviceCollection.BuildServiceProvider() ); @@ -156,7 +156,7 @@ public async Task FailingFuncFlowCompletesWithError() serviceCollection.AddTransient(); var flowStore = new InMemoryFunctionStore(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( flowStore, serviceCollection.BuildServiceProvider() ); @@ -205,7 +205,7 @@ public async Task FailingParamlessFlowCompletesWithError() serviceCollection.AddTransient(); var flowStore = new InMemoryFunctionStore(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( flowStore, serviceCollection.BuildServiceProvider() ); @@ -250,7 +250,7 @@ public override Task Run() [TestMethod] public async Task FlowCanBeCreatedWithInitialState() { - var flowsContainer = FlowsContainer.Create(); + var flowsContainer = await FlowsContainer.CreateAndStart(); var flow = new InitialStateFlow(); var flows = flowsContainer.RegisterAnonymousFlow( flowFactory: () => flow diff --git a/Cleipnir.Tests/Flows/UtcNowTests.cs b/Cleipnir.Tests/Flows/UtcNowTests.cs index b7c5f87..88a6616 100644 --- a/Cleipnir.Tests/Flows/UtcNowTests.cs +++ b/Cleipnir.Tests/Flows/UtcNowTests.cs @@ -15,7 +15,7 @@ public async Task ProvidedUtcNowDelegateIsUsed() var serviceCollection = new ServiceCollection(); serviceCollection.AddSingleton(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( new InMemoryFunctionStore(), serviceCollection.BuildServiceProvider(), new Settings(utcNow: () => now, watchdogCheckFrequency: TimeSpan.FromMilliseconds(100)) diff --git a/Cleipnir/AspNet/FlowsModule.cs b/Cleipnir/AspNet/FlowsModule.cs index d5b6fe0..d8189fb 100644 --- a/Cleipnir/AspNet/FlowsModule.cs +++ b/Cleipnir/AspNet/FlowsModule.cs @@ -20,8 +20,16 @@ public static IServiceCollection AddFlows(this IServiceCollection services, Func services.AddSingleton(new Settings()); else services.AddSingleton(configurator.OptionsFunc); - - services.AddSingleton(); + + services.AddSingleton(sp => + { + var store = sp.GetRequiredService(); + var settings = configurator.OptionsFunc?.Invoke(sp) ?? sp.GetService(); + // Resolved once during host start-up; blocking here mirrors the old constructor, which + // initialized the replica watchdog synchronously. + return FlowsContainer.CreateAndStart(store, sp, settings).GetAwaiter().GetResult(); + } + ); services.AddTransient(_ => CurrentFlow.Workflow ?? throw new InvalidOperationException("Workflow is not present outside Flow")); services.AddTransient(_ => CurrentFlow.Workflow?.Effect ?? throw new InvalidOperationException("Effect is not present outside Flow")); diff --git a/Cleipnir/FlowOptions.cs b/Cleipnir/FlowOptions.cs index 50f05cb..cdb6366 100644 --- a/Cleipnir/FlowOptions.cs +++ b/Cleipnir/FlowOptions.cs @@ -8,19 +8,16 @@ public class FlowOptions public static FlowOptions Default { get; } = new(); internal TimeSpan? RetentionPeriod { get; } - internal bool? EnableWatchdogs { get; } internal int? MaxParallelRetryInvocations { get; } internal TimeSpan? MessagesDefaultMaxWaitForCompletion { get; } public FlowOptions( TimeSpan? retentionPeriod = null, - bool? enableWatchdogs = null, TimeSpan? messagesDefaultMaxWaitForCompletion = null, int? maxParallelRetryInvocations = null ) { RetentionPeriod = retentionPeriod; - EnableWatchdogs = enableWatchdogs; MessagesDefaultMaxWaitForCompletion = messagesDefaultMaxWaitForCompletion; MaxParallelRetryInvocations = maxParallelRetryInvocations; } @@ -29,12 +26,11 @@ public FlowOptions Merge(Settings settings) { return new FlowOptions( RetentionPeriod ?? settings.RetentionPeriod, - EnableWatchdogs ?? settings.EnableWatchdogs, MessagesDefaultMaxWaitForCompletion ?? settings.MessagesDefaultMaxWaitForCompletion, MaxParallelRetryInvocations ?? settings.MaxParallelRetryInvocations ); } internal LocalSettings MapToLocalSettings() - => new(RetentionPeriod, EnableWatchdogs, MessagesDefaultMaxWaitForCompletion, MaxParallelRetryInvocations); + => new(RetentionPeriod, MessagesDefaultMaxWaitForCompletion, MaxParallelRetryInvocations); } diff --git a/Cleipnir/Flows.cs b/Cleipnir/Flows.cs index 8a81a75..041291e 100644 --- a/Cleipnir/Flows.cs +++ b/Cleipnir/Flows.cs @@ -5,7 +5,6 @@ using Cleipnir.ResilientFunctions; using Cleipnir.ResilientFunctions.CoreRuntime.Invocation; using Cleipnir.ResilientFunctions.Domain; -using Cleipnir.ResilientFunctions.Domain.Exceptions.Commands; using Cleipnir.ResilientFunctions.Helpers; using Cleipnir.ResilientFunctions.Messaging; using Microsoft.Extensions.DependencyInjection; @@ -21,8 +20,6 @@ public abstract class BaseFlows : IBaseFlows where TFlow : notnull { public static Type FlowType { get; } = typeof(TFlow); - public abstract Task Interrupt(IEnumerable instances); - protected static Action CreateWorkflowSetter() { ParameterExpression flowParam = Expression.Parameter(typeof(TFlow), "flow"); @@ -56,34 +53,19 @@ public Flows(string flowName, FlowsContainer flowsContainer, FlowOptions? option flowsContainer.EnsureNoExistingRegistration(flowName, typeof(TFlow)); _registration = flowsContainer.FunctionRegistry.RegisterParamless( flowName, + // Registered via the public overload so the core wraps the body in its suspension race and + // converts thrown exceptions to failed results - the flow body itself must not catch. inner: async w => { - try - { - await using var scope = serviceProvider.CreateAsyncScope(); - - var flow = flowFactory == null - ? scope.ServiceProvider.GetRequiredService() - : flowFactory(); - - workflowSetter(flow, w); - - await flow.Run(); - return new Result(Unit.Instance); - } - catch (SuspendInvocationException) - { - return new Result(Suspend.Invocation); - } - catch (FatalWorkflowException exception) - { - exception.FlowId = w.FlowId; - return new Result(exception); - } - catch (Exception exception) - { - return new Result(FatalWorkflowException.CreateNonGeneric(w.FlowId, exception)); - } + await using var scope = serviceProvider.CreateAsyncScope(); + + var flow = flowFactory == null + ? scope.ServiceProvider.GetRequiredService() + : flowFactory(); + + workflowSetter(flow, w); + + await flow.Run(); }, (options ?? FlowOptions.Default).MapToLocalSettings() ); @@ -147,14 +129,6 @@ public Task Schedule(FlowInstance instanceId, InitialState? initialSt /// A task which will complete when the flows have been persisted public Task BulkSchedule(IEnumerable instanceIds) => _registration.BulkSchedule(instanceIds); - /// - /// Emit interrupt signal to flows - /// Execution of suspended flows will be resumed. Already executing flows will be restarted on suspension. - /// - /// Instance ids for flows - /// A task which will complete when the interrupt-signal has been persisted - public override Task Interrupt(IEnumerable instances) => _registration.Interrupt(instances); - /// /// Send the provided message to a flow /// @@ -190,34 +164,19 @@ public Flows(string flowName, FlowsContainer flowsContainer, FlowOptions? option flowsContainer.EnsureNoExistingRegistration(flowName, typeof(TFlow)); _registration = flowsContainer.FunctionRegistry.RegisterAction( flowName, + // Registered via the public overload so the core wraps the body in its suspension race and + // converts thrown exceptions to failed results - the flow body itself must not catch. inner: async (p, w) => { - try - { - await using var scope = serviceProvider.CreateAsyncScope(); - - var flow = flowFactory == null - ? scope.ServiceProvider.GetRequiredService() - : flowFactory(); - - workflowSetter(flow, w); - - await flow.Run(p); - return new Result(Unit.Instance); - } - catch (SuspendInvocationException) - { - return new Result(Suspend.Invocation); - } - catch (FatalWorkflowException exception) - { - exception.FlowId = w.FlowId; - return new Result(exception); - } - catch (Exception exception) - { - return new Result(FatalWorkflowException.CreateNonGeneric(w.FlowId, exception)); - } + await using var scope = serviceProvider.CreateAsyncScope(); + + var flow = flowFactory == null + ? scope.ServiceProvider.GetRequiredService() + : flowFactory(); + + workflowSetter(flow, w); + + await flow.Run(p); }, settings: (options ?? FlowOptions.Default).MapToLocalSettings() ); @@ -286,14 +245,6 @@ public Task ScheduleIn( TimeSpan delay ) => _registration.ScheduleIn(instanceId.Value, param, delay); - /// - /// Emit interrupt signal to flows - /// Execution of suspended flows will be resumed. Already executing flows will be restarted on suspension. - /// - /// Instance ids for flows - /// A task which will complete when the interrupt-signal has been persisted - public override Task Interrupt(IEnumerable instances) => _registration.Interrupt(instances); - /// /// Send the provided message to a flow /// @@ -336,34 +287,19 @@ public Flows(string flowName, FlowsContainer flowsContainer, FlowOptions? option flowsContainer.EnsureNoExistingRegistration(flowName, typeof(TFlow)); _registration = flowsContainer.FunctionRegistry.RegisterFunc( flowName, + // Registered via the public overload so the core wraps the body in its suspension race and + // converts thrown exceptions to failed results - the flow body itself must not catch. inner: async (p, w) => { - try - { - await using var scope = serviceProvider.CreateAsyncScope(); - - var flow = flowFactory == null - ? scope.ServiceProvider.GetRequiredService() - : flowFactory(); - - workflowSetter(flow, w); - - var result = await flow.Run(p); - return new Result(result); - } - catch (SuspendInvocationException) - { - return new Result(Suspend.Invocation); - } - catch (FatalWorkflowException exception) - { - exception.FlowId = w.FlowId; - return new Result(exception); - } - catch (Exception exception) - { - return new Result(FatalWorkflowException.CreateNonGeneric(w.FlowId, exception)); - } + await using var scope = serviceProvider.CreateAsyncScope(); + + var flow = flowFactory == null + ? scope.ServiceProvider.GetRequiredService() + : flowFactory(); + + workflowSetter(flow, w); + + return await flow.Run(p); }, (options ?? FlowOptions.Default).MapToLocalSettings() ); @@ -429,14 +365,6 @@ public Task ScheduleIn( TimeSpan delay ) => _registration.ScheduleIn(instanceId.Value, param, delay); - /// - /// Emit interrupt signal to flows - /// Execution of suspended flows will be resumed. Already executing flows will be restarted on suspension. - /// - /// Instance ids for flows - /// A task which will complete when the interrupt-signal has been persisted - public override Task Interrupt(IEnumerable instances) => _registration.Interrupt(instances); - /// /// Send the provided message to a flow /// diff --git a/Cleipnir/FlowsContainer.cs b/Cleipnir/FlowsContainer.cs index b3dabb6..58d305e 100644 --- a/Cleipnir/FlowsContainer.cs +++ b/Cleipnir/FlowsContainer.cs @@ -19,9 +19,14 @@ public class FlowsContainer : IDisposable public FunctionsRegistry Functions => FunctionRegistry; - public FlowsContainer(IFunctionStore flowStore, IServiceProvider serviceProvider, Settings? settings = null) + private FlowsContainer(IServiceProvider serviceProvider, FunctionsRegistry functionRegistry) { ServiceProvider = serviceProvider; + FunctionRegistry = functionRegistry; + } + + public static async Task CreateAndStart(IFunctionStore flowStore, IServiceProvider serviceProvider, Settings? settings = null) + { settings ??= new Settings(); if (settings.UnhandledExceptionHandler == null && serviceProvider.GetService() != null) @@ -31,11 +36,9 @@ public FlowsContainer(IFunctionStore flowStore, IServiceProvider serviceProvider unhandledExceptionHandler: ex => logger.LogError(ex, "Unhandled exception in Cleipnir"), retentionPeriod: settings.RetentionPeriod, retentionCleanUpFrequency: settings.RetentionCleanUpFrequency, - enableWatchdogs: settings.EnableWatchdogs, watchdogCheckFrequency: settings.WatchdogCheckFrequency, messagesPullFrequency: settings.MessagesPullFrequency, messagesDefaultMaxWaitForCompletion: settings.MessagesDefaultMaxWaitForCompletion, - delayStartup: settings.DelayStartup, maxParallelRetryInvocations: settings.MaxParallelRetryInvocations, serializer: settings.Serializer, utcNow: settings.UtcNow, @@ -43,7 +46,8 @@ public FlowsContainer(IFunctionStore flowStore, IServiceProvider serviceProvider ); } - FunctionRegistry = new FunctionsRegistry(flowStore, settings); + var functionRegistry = await FunctionsRegistry.CreateAndStart(flowStore, settings); + return new FlowsContainer(serviceProvider, functionRegistry); } internal void EnsureNoExistingRegistration(string flowName, Type flowType) @@ -76,11 +80,11 @@ public Flows RegisterAnonymousFlow(flowName, flowsContainer: this, options ?? new FlowOptions(), flowFactory); } - public static FlowsContainer Create( + public static Task CreateAndStart( IServiceProvider? serviceProvider = null, IFunctionStore? functionStore = null, Settings? settings = null) - => new( + => CreateAndStart( functionStore ?? new InMemoryFunctionStore(), serviceProvider ?? new ServiceCollection().BuildServiceProvider(), settings ?? new Settings() diff --git a/Samples/Cleipnir.Sample.Presentation/A_OrderFlowRpc/Example.cs b/Samples/Cleipnir.Sample.Presentation/A_OrderFlowRpc/Example.cs index 9032235..dfa72f5 100644 --- a/Samples/Cleipnir.Sample.Presentation/A_OrderFlowRpc/Example.cs +++ b/Samples/Cleipnir.Sample.Presentation/A_OrderFlowRpc/Example.cs @@ -13,7 +13,7 @@ public static async Task Do() serviceCollection.AddTransient(); serviceCollection.AddTransient(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( new InMemoryFunctionStore(), serviceCollection.BuildServiceProvider() ); diff --git a/Samples/Cleipnir.Sample.Presentation/A_OrderFlowRpc/Solution/Example.cs b/Samples/Cleipnir.Sample.Presentation/A_OrderFlowRpc/Solution/Example.cs index e43bb0a..3e4017c 100644 --- a/Samples/Cleipnir.Sample.Presentation/A_OrderFlowRpc/Solution/Example.cs +++ b/Samples/Cleipnir.Sample.Presentation/A_OrderFlowRpc/Solution/Example.cs @@ -13,7 +13,7 @@ public static async Task Do() serviceCollection.AddTransient(); serviceCollection.AddTransient(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( new InMemoryFunctionStore(), serviceCollection.BuildServiceProvider() ); diff --git a/Samples/Cleipnir.Sample.Presentation/B_OrderFlow_Messaging/Example.cs b/Samples/Cleipnir.Sample.Presentation/B_OrderFlow_Messaging/Example.cs index 3f00bf9..8624bca 100644 --- a/Samples/Cleipnir.Sample.Presentation/B_OrderFlow_Messaging/Example.cs +++ b/Samples/Cleipnir.Sample.Presentation/B_OrderFlow_Messaging/Example.cs @@ -16,7 +16,7 @@ public static async Task Execute() serviceCollection.AddSingleton(messageBroker); serviceCollection.AddTransient(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( new InMemoryFunctionStore(), serviceCollection.BuildServiceProvider() ); diff --git a/Samples/Cleipnir.Sample.Presentation/B_OrderFlow_Messaging/Solution/Example.cs b/Samples/Cleipnir.Sample.Presentation/B_OrderFlow_Messaging/Solution/Example.cs index 644ad70..9da58ec 100644 --- a/Samples/Cleipnir.Sample.Presentation/B_OrderFlow_Messaging/Solution/Example.cs +++ b/Samples/Cleipnir.Sample.Presentation/B_OrderFlow_Messaging/Solution/Example.cs @@ -16,7 +16,7 @@ public static async Task Execute() serviceCollection.AddSingleton(messageBroker); serviceCollection.AddTransient(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( new InMemoryFunctionStore(), serviceCollection.BuildServiceProvider() ); diff --git a/Samples/Cleipnir.Sample.Presentation/C_NewsletterSender/Solution/Example.cs b/Samples/Cleipnir.Sample.Presentation/C_NewsletterSender/Solution/Example.cs index f0dae5a..734bb7d 100644 --- a/Samples/Cleipnir.Sample.Presentation/C_NewsletterSender/Solution/Example.cs +++ b/Samples/Cleipnir.Sample.Presentation/C_NewsletterSender/Solution/Example.cs @@ -14,7 +14,7 @@ public static async Task Perform() var serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( flowStore, serviceCollection.BuildServiceProvider(), new Settings(unhandledExceptionHandler: Console.WriteLine) diff --git a/Samples/Cleipnir.Samples.Console/AtLeastOnce/Example.cs b/Samples/Cleipnir.Samples.Console/AtLeastOnce/Example.cs index 031f03a..27f1e52 100644 --- a/Samples/Cleipnir.Samples.Console/AtLeastOnce/Example.cs +++ b/Samples/Cleipnir.Samples.Console/AtLeastOnce/Example.cs @@ -11,7 +11,7 @@ public static async Task Do() var serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( new InMemoryFunctionStore(), serviceCollection.BuildServiceProvider() ); diff --git a/Samples/Cleipnir.Samples.Console/AtMostOnce/Example.cs b/Samples/Cleipnir.Samples.Console/AtMostOnce/Example.cs index 0541b4a..0353f36 100644 --- a/Samples/Cleipnir.Samples.Console/AtMostOnce/Example.cs +++ b/Samples/Cleipnir.Samples.Console/AtMostOnce/Example.cs @@ -11,7 +11,7 @@ public static async Task Do() var serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( new InMemoryFunctionStore(), serviceCollection.BuildServiceProvider() ); diff --git a/Samples/Cleipnir.Samples.Console/Postpone/Example.cs b/Samples/Cleipnir.Samples.Console/Postpone/Example.cs index d3a6786..375e2ee 100644 --- a/Samples/Cleipnir.Samples.Console/Postpone/Example.cs +++ b/Samples/Cleipnir.Samples.Console/Postpone/Example.cs @@ -11,7 +11,7 @@ public static async Task Do() var serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( new InMemoryFunctionStore(), serviceCollection.BuildServiceProvider() ); diff --git a/Samples/Cleipnir.Samples.Console/RestartFlow/Example.cs b/Samples/Cleipnir.Samples.Console/RestartFlow/Example.cs index afad67c..aee80d9 100644 --- a/Samples/Cleipnir.Samples.Console/RestartFlow/Example.cs +++ b/Samples/Cleipnir.Samples.Console/RestartFlow/Example.cs @@ -12,7 +12,7 @@ public static async Task Do() var serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( new InMemoryFunctionStore(), serviceCollection.BuildServiceProvider() ); diff --git a/Samples/Cleipnir.Samples.Console/Retry/Example.cs b/Samples/Cleipnir.Samples.Console/Retry/Example.cs index a48842f..8205db7 100644 --- a/Samples/Cleipnir.Samples.Console/Retry/Example.cs +++ b/Samples/Cleipnir.Samples.Console/Retry/Example.cs @@ -16,7 +16,7 @@ public static async Task Do() var serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( store, serviceCollection.BuildServiceProvider(), new Settings(unhandledExceptionHandler: Console.WriteLine) diff --git a/Samples/Cleipnir.Samples.Console/WaitForMessages/Example.cs b/Samples/Cleipnir.Samples.Console/WaitForMessages/Example.cs index a6d98e6..a975892 100644 --- a/Samples/Cleipnir.Samples.Console/WaitForMessages/Example.cs +++ b/Samples/Cleipnir.Samples.Console/WaitForMessages/Example.cs @@ -11,7 +11,7 @@ public static async Task Do() var serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(); - var flowsContainer = new FlowsContainer( + var flowsContainer = await FlowsContainer.CreateAndStart( new InMemoryFunctionStore(), serviceCollection.BuildServiceProvider() ); diff --git a/ServiceBuses/Kafka/Cleipnir.Kafka.Tests/MessageTests.cs b/ServiceBuses/Kafka/Cleipnir.Kafka.Tests/MessageTests.cs index f810adc..c2636e1 100644 --- a/ServiceBuses/Kafka/Cleipnir.Kafka.Tests/MessageTests.cs +++ b/ServiceBuses/Kafka/Cleipnir.Kafka.Tests/MessageTests.cs @@ -12,7 +12,7 @@ public sealed class MessageTests public async Task MultipleMessagesCanBeHandled() { var instanceId = "Instance#1".ToFlowInstance(); - var flowsContainer = FlowsContainer.Create(); + var flowsContainer = await FlowsContainer.CreateAndStart(); var flows = flowsContainer.RegisterAnonymousFlow( flowFactory: () => new TestFlow() ); diff --git a/ServiceBuses/MassTransit/Cleipnir.MassTransit.RabbitMq.Console/OrderFlow.cs b/ServiceBuses/MassTransit/Cleipnir.MassTransit.RabbitMq.Console/OrderFlow.cs index 961ed1e..cf8d902 100644 --- a/ServiceBuses/MassTransit/Cleipnir.MassTransit.RabbitMq.Console/OrderFlow.cs +++ b/ServiceBuses/MassTransit/Cleipnir.MassTransit.RabbitMq.Console/OrderFlow.cs @@ -1,5 +1,4 @@ using Cleipnir.Flows.MassTransit.RabbitMq.Console.Other; -using Cleipnir.ResilientFunctions.Domain.Exceptions.Commands; using MassTransit; namespace Cleipnir.Flows.MassTransit.RabbitMq.Console; @@ -31,7 +30,7 @@ public override async Task Run(Order order) await SendOrderConfirmationEmail(order, trackAndTraceNumber); await Message(); } - catch (Exception e) when (e is not SuspendInvocationException) + catch (Exception) { await Compensate(order, transactionId, fundsReserved, productsShipped, fundsCaptured); }