diff --git a/docs/guide/durability/sagas.md b/docs/guide/durability/sagas.md index 523eace0c..5a0382928 100644 --- a/docs/guide/durability/sagas.md +++ b/docs/guide/durability/sagas.md @@ -504,6 +504,13 @@ public void Handle(OrderTimeout timeout, ILogger logger) snippet source | anchor +## Saga Concurrency + +Both the Marten and EF Core backed saga support has built in support for optimistic concurrency checks on persisting +a saga after handling a command. See [Dealing with Concurrency](/tutorials/concurrency) and especially the +[partitioned sequential messaging](/tutorials/concurrency) and its option for "inferred" message grouping to maybe completely +side step concurrency issues with saga message handling. + ## Lightweight Saga Storage The Wolverine integration with either Sql Server or PostgreSQL comes with a lightweight saga storage mechanism @@ -543,3 +550,20 @@ using var host = await Host.CreateDefaultBuilder() Note that this manual registration is not necessary at development time or if you're content to just let Wolverine handle database migrations at runtime. + +## Overriding Logging + +We recently had a question about how to turn down logging levels for `Saga` message processing when the log +output was getting too verbose. `Saga` types are officially message handlers to the Wolverine internals, so you can +still use the `public static void Configure(HandlerChain)` mechanism for one off configurations to every message handler +method on the `Saga` like this: + +snippet: sample_overriding_logging_on_saga + +Or if you wanted to just do it globally, something like this approach: + +snippet: sample_turn_down_logging_for_sagas + +and register that policy something like this: + +snippet: sample_configuring_chain_policy_on_sagas \ No newline at end of file diff --git a/src/Persistence/MartenTests/Saga/RevisionedSaga.cs b/src/Persistence/MartenTests/Saga/RevisionedSaga.cs index 21d3f2e25..513063f48 100644 --- a/src/Persistence/MartenTests/Saga/RevisionedSaga.cs +++ b/src/Persistence/MartenTests/Saga/RevisionedSaga.cs @@ -1,12 +1,17 @@ using IntegrationTests; using JasperFx.CodeGeneration; using JasperFx.Core; +using JasperFx.Core.Reflection; using Marten; using Marten.Metadata; using Microsoft.Extensions.Hosting; using JasperFx.Resources; +using Microsoft.Extensions.Logging; +using Shouldly; using Wolverine; using Wolverine.Marten; +using Wolverine.Runtime; +using Wolverine.Runtime.Handlers; using Wolverine.Tracking; namespace MartenTests.Saga; @@ -31,6 +36,16 @@ public async Task InitializeAsync() }).StartAsync(); } + [Fact] + public void can_override_the_log_level() + { + var runtime = theHost.GetRuntime(); + runtime.As().BuildFor(typeof(StartNewRevisionedSaga)); + var chain = runtime.Handlers.ChainFor(); + chain.SuccessLogLevel.ShouldBe(LogLevel.None); + chain.ProcessingLogLevel.ShouldBe(LogLevel.None); + } + [Fact] public async Task execute_using_update_revision() { @@ -62,8 +77,20 @@ public async Task DisposeAsync() } } +#region sample_overriding_logging_on_saga + public class RevisionedSaga : Wolverine.Saga { + // This works just the same as on any other message handler + // type + public static void Configure(HandlerChain chain) + { + chain.ProcessingLogLevel = LogLevel.None; + chain.SuccessLogLevel = LogLevel.None; + } + + #endregion + public static TaskCompletionSource InSlowMessage = new TaskCompletionSource(); public static RevisionedSaga Start(StartNewRevisionedSaga command) => new RevisionedSaga { Id = command.Id }; diff --git a/src/Persistence/PersistenceTests/Samples/SagaChainPolicies.cs b/src/Persistence/PersistenceTests/Samples/SagaChainPolicies.cs new file mode 100644 index 000000000..1de104f3e --- /dev/null +++ b/src/Persistence/PersistenceTests/Samples/SagaChainPolicies.cs @@ -0,0 +1,41 @@ +using JasperFx; +using JasperFx.CodeGeneration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Wolverine; +using Wolverine.Configuration; +using Wolverine.Persistence.Sagas; + +namespace PersistenceTests.Samples; + +public class SagaChainPolicies +{ + public static async Task configure() + { + #region sample_configuring_chain_policy_on_sagas + + using var host = await Host.CreateDefaultBuilder() + .UseWolverine(opts => + { + opts.Policies.Add(); + }).StartAsync(); + + #endregion + } +} + +#region sample_turn_down_logging_for_sagas + +public class TurnDownLoggingOnSagas : IChainPolicy +{ + public void Apply(IReadOnlyList chains, GenerationRules rules, IServiceContainer container) + { + foreach (var sagaChain in chains.OfType()) + { + sagaChain.ProcessingLogLevel = LogLevel.None; + sagaChain.SuccessLogLevel = LogLevel.None; + } + } +} + +#endregion \ No newline at end of file