diff --git a/src/CoreTests/Events/EventGraph_IEventStoreInstrumentation.cs b/src/CoreTests/Events/EventGraph_IEventStoreInstrumentation.cs index ef9236de5a..c619447e19 100644 --- a/src/CoreTests/Events/EventGraph_IEventStoreInstrumentation.cs +++ b/src/CoreTests/Events/EventGraph_IEventStoreInstrumentation.cs @@ -1,103 +1,68 @@ +using System.Threading.Tasks; +using CoreTests.Examples; +using JasperFx.Core.Reflection; using JasperFx.Events; +using Marten; using Marten.Events; +using Marten.Testing.Harness; +using Microsoft.Extensions.DependencyInjection; using Shouldly; using Xunit; namespace CoreTests.Events; -// #4686 — EventGraph implements JasperFx.Events.IEventStoreInstrumentation (shipped in -// JasperFx 2.9.0, jasperfx#424). Both the legacy EnableExtendedProgressionTracking property -// and the storage-agnostic ExtendedProgressionEnabled name on IEventStoreInstrumentation must -// alias the same underlying field, so Wolverine.CritterWatch.Marten can flip the toggle via -// the abstraction without breaking existing code that sets EnableExtendedProgressionTracking -// directly. public class EventGraph_IEventStoreInstrumentation { - private static EventGraph BuildEventGraph() - { - // EventGraph requires a StoreOptions to wire up its registry, but a default - // StoreOptions is sufficient here -- no connection or schema interaction. - return new EventGraph(new Marten.StoreOptions()); - } - - [Fact] - public void implements_event_store_instrumentation() - { - BuildEventGraph().ShouldBeAssignableTo(); - } // #4687: default flipped from false → true (Critter Stack 1.0 timing). The six // monitoring columns are written from existing daemon runtime state so the cost is // negligible, and they're useful for any stuck-shard diagnosis -- not just CritterWatch. // Opt-out remains via either name. [Fact] - public void default_is_enabled() - { - var events = BuildEventGraph(); - - events.EnableExtendedProgressionTracking.ShouldBeTrue(); - events.ExtendedProgressionEnabled.ShouldBeTrue(); - ((IEventStoreInstrumentation)events).ExtendedProgressionEnabled.ShouldBeTrue(); - } - - [Fact] - public void opt_out_via_legacy_name_disables_both_surfaces() - { - var events = BuildEventGraph(); - var instrumentation = (IEventStoreInstrumentation)events; - - events.EnableExtendedProgressionTracking = false; - - events.ExtendedProgressionEnabled.ShouldBeFalse(); - instrumentation.ExtendedProgressionEnabled.ShouldBeFalse(); - } - - [Fact] - public void opt_out_via_interface_disables_both_surfaces() + public void default_is_disabled() { - var events = BuildEventGraph(); - var instrumentation = (IEventStoreInstrumentation)events; - - instrumentation.ExtendedProgressionEnabled = false; - - events.EnableExtendedProgressionTracking.ShouldBeFalse(); - events.ExtendedProgressionEnabled.ShouldBeFalse(); + new StoreOptions().EventGraph.EnableExtendedProgressionTracking.ShouldBeFalse(); } [Fact] - public void enabling_via_legacy_name_flows_to_interface_property() + public void build_store_with_progression_tracking_override() { - var events = BuildEventGraph(); - var instrumentation = (IEventStoreInstrumentation)events; + var collection = new ServiceCollection(); + collection.AddMarten(ConnectionSource.ConnectionString); - events.EnableExtendedProgressionTracking = true; - - events.ExtendedProgressionEnabled.ShouldBeTrue(); - instrumentation.ExtendedProgressionEnabled.ShouldBeTrue(); - } - - [Fact] - public void enabling_via_interface_flows_to_legacy_name() - { - var events = BuildEventGraph(); - var instrumentation = (IEventStoreInstrumentation)events; + using var provider = collection.BuildServiceProvider(); + var instrumentation = provider.GetRequiredService(); + instrumentation.ShouldNotBeNull(); instrumentation.ExtendedProgressionEnabled = true; - events.EnableExtendedProgressionTracking.ShouldBeTrue(); - events.ExtendedProgressionEnabled.ShouldBeTrue(); + var store = provider.GetRequiredService(); + store.Options.Events.EnableExtendedProgressionTracking.ShouldBeTrue(); } [Fact] - public void disabling_via_either_surface_unwinds_the_other() + public void build_store_with_progression_tracking_override_with_ancillary_store() { - var events = BuildEventGraph(); - var instrumentation = (IEventStoreInstrumentation)events; - - events.EnableExtendedProgressionTracking = true; - instrumentation.ExtendedProgressionEnabled = false; - - events.EnableExtendedProgressionTracking.ShouldBeFalse(); - events.ExtendedProgressionEnabled.ShouldBeFalse(); + var collection = new ServiceCollection(); + collection.AddMarten(ConnectionSource.ConnectionString); + collection.AddMartenStore(opts => + { + opts.Connection(ConnectionSource.ConnectionString); + opts.DatabaseSchemaName = "invoices"; + }); + + using var provider = collection.BuildServiceProvider(); + + var instruments = provider.GetServices(); + foreach (var instrument in instruments) + { + instrument.ExtendedProgressionEnabled = true; + } + + var store = provider.GetRequiredService(); + store.Options.Events.EnableExtendedProgressionTracking.ShouldBeTrue(); + + provider.GetRequiredService() + .Options.Events.EnableExtendedProgressionTracking.ShouldBeTrue(); } } diff --git a/src/Marten/Events/EventGraph.cs b/src/Marten/Events/EventGraph.cs index 12fe350569..486b11eb60 100644 --- a/src/Marten/Events/EventGraph.cs +++ b/src/Marten/Events/EventGraph.cs @@ -41,8 +41,7 @@ namespace Marten.Events; Justification = "Class-level: uses Type.MakeGenericType / MethodInfo.MakeGenericMethod / Activator.CreateInstance / FastExpressionCompiler — runtime code generation. AOT consumers pre-generate codegen artifacts (codegen write) and supply source-generator-backed serializer impls per the AOT publishing guide.")] public partial class EventGraph: EventRegistry, IEventStoreOptions, IReadOnlyEventStoreOptions, IDisposable, IAsyncDisposable, - IAggregationSourceFactory, IDescribeMyself, - IEventStoreInstrumentation + IAggregationSourceFactory, IDescribeMyself { private readonly Cache _aggregateNameByType = new(type => type.IsGenericType ? type.ShortNameInCode() : type.Name.ToTableAlias()); @@ -236,34 +235,14 @@ public override IEvent BuildEvent(object eventData) /// When enabled, adds heartbeat, agent_status, pause_reason, running_on_node, and /// warning/critical-behind-threshold columns to the event progression table for /// CritterWatch monitoring. - /// - /// This is the long-standing Marten-side toggle; #4686 added the storage-agnostic - /// as a sibling so - /// store-agnostic monitoring tooling (e.g. Wolverine.CritterWatch.Marten) can flip - /// the switch via the JasperFx.Events abstraction without referencing Marten's concrete - /// option type. Both names refer to the same underlying field; new code is encouraged to - /// prefer ExtendedProgressionEnabled on the interface. - /// /// public bool EnableExtendedProgressionTracking { - get => ExtendedProgressionEnabled; - set => ExtendedProgressionEnabled = value; + get; + set; } - /// - /// -- the primary - /// (storage-agnostic) toggle that controls extended progression tracking. Aliased by the - /// legacy property. - /// - /// **Default: true** (#4687, Critter Stack 1.0 timing). The six monitoring columns are - /// written from existing daemon runtime state, so the cost is negligible (no extra queries, - /// 6 nullable columns on a low-volume progression table) and they're useful for any - /// stuck-shard diagnosis -- not just CritterWatch. Opt out by setting this to false (or - /// the legacy EnableExtendedProgressionTracking) explicitly. - /// - /// - public bool ExtendedProgressionEnabled { get; set; } = true; + public bool UseArchivedStreamPartitioning { get; set; } public bool UseListenNotifyForEventAppends { get; set; } diff --git a/src/Marten/Events/IEventStoreOptions.cs b/src/Marten/Events/IEventStoreOptions.cs index ead5158f92..4908fe2a39 100644 --- a/src/Marten/Events/IEventStoreOptions.cs +++ b/src/Marten/Events/IEventStoreOptions.cs @@ -520,6 +520,21 @@ public IEventStoreOptions Upcast( /// and eliminate the per-query LEFT JOINs across tag tables. /// DcbStorageMode DcbStorageMode { get; set; } + + /// + /// When enabled, adds heartbeat, agent_status, pause_reason, running_on_node, and + /// warning/critical-behind-threshold columns to the event progression table for + /// CritterWatch monitoring. + /// + /// This is the long-standing Marten-side toggle; #4686 added the storage-agnostic + /// as a sibling so + /// store-agnostic monitoring tooling (e.g. Wolverine.CritterWatch.Marten) can flip + /// the switch via the JasperFx.Events abstraction without referencing Marten's concrete + /// option type. Both names refer to the same underlying field; new code is encouraged to + /// prefer ExtendedProgressionEnabled on the interface. + /// + /// + public bool EnableExtendedProgressionTracking { get; set; } } } diff --git a/src/Marten/Events/IReadOnlyEventStoreOptions.cs b/src/Marten/Events/IReadOnlyEventStoreOptions.cs index 8c3acaecb4..8f342e5935 100644 --- a/src/Marten/Events/IReadOnlyEventStoreOptions.cs +++ b/src/Marten/Events/IReadOnlyEventStoreOptions.cs @@ -140,4 +140,19 @@ public interface IReadOnlyEventStoreOptions /// Index names that the schema migration functionality should ignore on the event-store tables. /// IReadOnlyList IgnoredIndexes { get; } + + /// + /// When enabled, adds heartbeat, agent_status, pause_reason, running_on_node, and + /// warning/critical-behind-threshold columns to the event progression table for + /// CritterWatch monitoring. + /// + /// This is the long-standing Marten-side toggle; #4686 added the storage-agnostic + /// as a sibling so + /// store-agnostic monitoring tooling (e.g. Wolverine.CritterWatch.Marten) can flip + /// the switch via the JasperFx.Events abstraction without referencing Marten's concrete + /// option type. Both names refer to the same underlying field; new code is encouraged to + /// prefer ExtendedProgressionEnabled on the interface. + /// + /// + bool EnableExtendedProgressionTracking { get; set; } } diff --git a/src/Marten/MartenServiceCollectionExtensions.cs b/src/Marten/MartenServiceCollectionExtensions.cs index bb22e45f03..afe529b573 100644 --- a/src/Marten/MartenServiceCollectionExtensions.cs +++ b/src/Marten/MartenServiceCollectionExtensions.cs @@ -215,6 +215,11 @@ Func optionSource services.AddSingleton(s => (IEventStore)s.GetRequiredService()); services.AddSingleton(s => (IDocumentStoreUsageSource)s.GetRequiredService()); + + var instrument = new SetEventStoreInstrumentation(); + services.AddSingleton(instrument); + services.AddSingleton(instrument); + services.AddSingleton(s => { var options = optionSource(s); @@ -328,6 +333,10 @@ public static MartenStoreExpression AddMartenStore(this IServiceCollection services.AddSingleton(s => (IEventStore)s.GetRequiredService()); services.AddSingleton(s => (IDocumentStoreUsageSource)s.GetRequiredService()); + var instrument = new SetEventStoreInstrumentation(); + services.AddSingleton>(instrument); + services.AddSingleton(instrument); + var stores = services .Where(x => !x.IsKeyedService) .Select(x => x.ImplementationInstance) @@ -1081,3 +1090,24 @@ public IDocumentStore Resolve(IServiceProvider services) return services.GetRequiredService(); } } + +// This little bit of fun is all about CritterWatch +internal class SetEventStoreInstrumentation: IConfigureMarten, IEventStoreInstrumentation +{ + public void Configure(IServiceProvider services, StoreOptions options) + { + options.EventGraph.EnableExtendedProgressionTracking = ExtendedProgressionEnabled; + } + + public bool ExtendedProgressionEnabled { get; set; } +} + +internal class SetEventStoreInstrumentation: IConfigureMarten, IEventStoreInstrumentation where T : IDocumentStore +{ + public void Configure(IServiceProvider services, StoreOptions options) + { + options.EventGraph.EnableExtendedProgressionTracking = ExtendedProgressionEnabled; + } + + public bool ExtendedProgressionEnabled { get; set; } +}