diff --git a/docs/scenarios/command_handler_workflow.md b/docs/scenarios/command_handler_workflow.md index aa060d9c76..4793ac11b0 100644 --- a/docs/scenarios/command_handler_workflow.md +++ b/docs/scenarios/command_handler_workflow.md @@ -133,38 +133,6 @@ the standard `IDocumentSession.SaveChangesAsync()` method call. At that point, i `Order` stream between our handler calling `FetchForWriting()` and `IDocumentSession.SaveChangesAsync()`, the entire command will fail with a Marten `ConcurrencyException`. -## Inline Aggregates - -::: warning -You may need to opt out of this behavior if you are modifying the aggregate document returned by `FetchForWriting` -before new events are applied to it to not arrive at incorrectly applied projection data. -::: - -`FetchForWriting()` works with all possible projection lifecycles as of Marten 7. However, there's a wrinkle with `Inline` -projections you should be aware of. It's frequently a valuable optimization to use _Lightweight_ sessions that omit -the identity map behavior. Let's say that we have a configuration like this: - -snippet: sample_using_lightweight_sessions_with_inline_single_stream_projection - -As an optimization, Marten is quietly turning on the identity map behavior for just a single aggregate document type -when `FetchForWriting()` is called with an `Inline` projection as shown below: - -snippet: sample_usage_of_identity_map_for_inline_projections - -This was done specifically to prevent unnecessary database fetches for the exact same data within common command handler -workflow operations. There can of course be a downside if you happen to be making any kind of mutations to the aggregate -document somewhere in between calling `FetchForWriting()` and `SaveChangesAsync()`, so to opt out of this behavior if -that causes you any trouble, use this: - - -::: info -Marten's default behavior of using sessions with the _Identity Map_ functionality option turned on was admittedly copied -from RavenDb almost a decade ago, and the Marten team has been too afraid to change the default behavior to the more performant, _Lightweight_ sessions because of the -very real risk of introducing difficult to diagnose regression errors. There you go folks, a 10 year old decision that this -author still regrets, but we're probably stuck with for the foreseeable future. -::: - - ## Explicit Optimistic Concurrency This time let's explicitly opt into optimistic concurrency checks by telling Marten what the expected starting diff --git a/src/EventSourcingTests/Aggregation/fetching_inline_aggregates_for_writing.cs b/src/EventSourcingTests/Aggregation/fetching_inline_aggregates_for_writing.cs index 365d383fd8..1cbbd1a100 100644 --- a/src/EventSourcingTests/Aggregation/fetching_inline_aggregates_for_writing.cs +++ b/src/EventSourcingTests/Aggregation/fetching_inline_aggregates_for_writing.cs @@ -1,13 +1,10 @@ using System; using System.Threading.Tasks; -using Marten; using Marten.Events; using Marten.Events.Projections; using Marten.Exceptions; using Marten.Storage; using Marten.Testing.Harness; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; using Xunit; using Shouldly; @@ -15,89 +12,6 @@ namespace EventSourcingTests.Aggregation; public class fetching_inline_aggregates_for_writing : OneOffConfigurationsContext { - public static async Task example() - { - - - #region sample_using_lightweight_sessions_with_inline_single_stream_projection - - var builder = Host.CreateApplicationBuilder(); - builder.Services.AddMarten(opts => - { - opts.Connection(builder.Configuration.GetConnectionString("postgres")); - - // An inline single stream projection that builds SimpleAggregate - // A "snapshot" in Marten still boils down to a SingleStreamProjection - // for that "T" in Marten internals - opts.Projections.Snapshot(SnapshotLifecycle.Inline); - }) - - // This is a commonly used, frequently helpful performance optimization - .UseLightweightSessions(); - - using var host = builder.Build(); - await host.StartAsync(); - - #endregion - - #region sample_usage_of_identity_map_for_inline_projections - - // Little helper extension method to quickly get at the Marten - // DocumentStore from an IHost - var store = host.DocumentStore(); - - // This session is *not* using identity map by default as - // a performance optimization - using var session = store.LightweightSession(); - - // Some aggregate id that would probably be passed in through a CQRS command message - // or maybe an HTTP request argument - var id = Guid.NewGuid(); - - // In your command handler, you would use this call to fetch both the current state - // of the SimpleAggregate as well as getting Marten ready to do optimistic concurrency - // checks for you as well - var stream = await session.Events.FetchForWriting(id); - - // Just showing you that this is the current version of the projected - // aggregate document that was fetched out of the database by Marten - var aggregate = stream.Aggregate; - - // The command would append new events to the event stream... - stream.AppendMany(new AEvent(), new BEvent()); - - // Persist the new events to the existing event stream, and oh, yeah, - // also update the SimpleAggregate document with the new events - // As of Marten 7.21, Marten is able to start with the version of the aggregate - // document that was initially loaded as part of FetchForWriting() instead - // of having to fetch it all over again from the database - await session.SaveChangesAsync(); - - #endregion - } - - public static async Task disable_optimization() - { - var builder = Host.CreateApplicationBuilder(); - builder.Services.AddMarten(opts => - { - opts.Connection(builder.Configuration.GetConnectionString("postgres")); - - // An inline single stream projection that builds SimpleAggregate - // A "snapshot" in Marten still boils down to a SingleStreamProjection - // for that "T" in Marten internals - opts.Projections.Snapshot(SnapshotLifecycle.Inline); - - opts.Events.UseIdentityMapForInlineAggregates = false; - }) - - // This is a commonly used, frequently helpful performance optimization - .UseLightweightSessions(); - - using var host = builder.Build(); - await host.StartAsync(); - } - [Fact] public async Task fetch_new_stream_for_writing_Guid_identifier() { diff --git a/src/EventSourcingTests/EventGraphTests.cs b/src/EventSourcingTests/EventGraphTests.cs index 1fafe378ba..6c3e5e1576 100644 --- a/src/EventSourcingTests/EventGraphTests.cs +++ b/src/EventSourcingTests/EventGraphTests.cs @@ -106,12 +106,6 @@ public void switch_to_quick_and_back_to_rich() theGraph.EventAppender.ShouldBeOfType(); } - [Fact] - public void use_identity_map_optimization_is_true_by_default() - { - theGraph.UseIdentityMapForInlineAggregates.ShouldBeTrue(); - } - public class HouseRemodeling { public Guid Id { get; set; } diff --git a/src/Marten/Events/EventGraph.cs b/src/Marten/Events/EventGraph.cs index dda06cf3e0..9f4a71c9b7 100644 --- a/src/Marten/Events/EventGraph.cs +++ b/src/Marten/Events/EventGraph.cs @@ -73,14 +73,6 @@ internal EventGraph(StoreOptions options) internal EventMetadataCollection Metadata { get; } = new(); - /// - /// Setting this to true will direct Marten to automatically use the identity map for inline projections - /// in calls to FetchForWriting as an optimization to reduce repeated queries for the same aggregate. - /// The default is true. Disable this call if applying state changes to the loaded aggregate in your own - /// command handlers - /// - public bool UseIdentityMapForInlineAggregates { get; set; } = true; - /// /// TimeProvider used for event timestamping metadata. Replace for controlling the timestamps /// in testing diff --git a/src/Marten/Events/Fetching/FetchInlinedPlan.cs b/src/Marten/Events/Fetching/FetchInlinedPlan.cs index 9a2da680bc..b7fa93c67b 100644 --- a/src/Marten/Events/Fetching/FetchInlinedPlan.cs +++ b/src/Marten/Events/Fetching/FetchInlinedPlan.cs @@ -29,12 +29,9 @@ public async Task> FetchForWriting(DocumentSessionBase sessio await _identityStrategy.EnsureEventStorageExists(session, cancellation).ConfigureAwait(false); await session.Database.EnsureStorageExistsAsync(typeof(TDoc), cancellation).ConfigureAwait(false); - if (session.Options.Events.UseIdentityMapForInlineAggregates) - { - // Opt into the identity map mechanics for this aggregate type just in case - // you're using a lightweight session - session.UseIdentityMapFor(); - } + // Opt into the identity map mechanics for this aggregate type just in case + // you're using a lightweight session + session.UseIdentityMapFor(); if (forUpdate) { diff --git a/src/Marten/Events/IEventStoreOptions.cs b/src/Marten/Events/IEventStoreOptions.cs index 5b466fea5d..47d4aef125 100644 --- a/src/Marten/Events/IEventStoreOptions.cs +++ b/src/Marten/Events/IEventStoreOptions.cs @@ -53,14 +53,6 @@ public interface IEventStoreOptions public EventAppendMode AppendMode { get; set; } - /// - /// Setting this to true will direct Marten to automatically use the identity map for inline projections - /// in calls to FetchForWriting as an optimization to reduce repeated queries for the same aggregate. - /// The default is true. Disable this call if applying state changes to the loaded aggregate in your own - /// command handlers - /// - public bool UseIdentityMapForInlineAggregates { get; set; } - /// /// Register an event type with Marten. This isn't strictly necessary for normal usage, /// but can help Marten with asynchronous projections where Marten hasn't yet encountered