Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 38 additions & 73 deletions src/CoreTests/Events/EventGraph_IEventStoreInstrumentation.cs
Original file line number Diff line number Diff line change
@@ -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<IEventStoreInstrumentation>();
}

// #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<IEventStoreInstrumentation>();

instrumentation.ShouldNotBeNull();
instrumentation.ExtendedProgressionEnabled = true;

events.EnableExtendedProgressionTracking.ShouldBeTrue();
events.ExtendedProgressionEnabled.ShouldBeTrue();
var store = provider.GetRequiredService<IDocumentStore>();
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<IInvoicingStore>(opts =>
{
opts.Connection(ConnectionSource.ConnectionString);
opts.DatabaseSchemaName = "invoices";
});

using var provider = collection.BuildServiceProvider();

var instruments = provider.GetServices<IEventStoreInstrumentation>();
foreach (var instrument in instruments)
{
instrument.ExtendedProgressionEnabled = true;
}

var store = provider.GetRequiredService<IDocumentStore>();
store.Options.Events.EnableExtendedProgressionTracking.ShouldBeTrue();

provider.GetRequiredService<IInvoicingStore>()
.Options.Events.EnableExtendedProgressionTracking.ShouldBeTrue();
}
}
29 changes: 4 additions & 25 deletions src/Marten/Events/EventGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<IQuerySession>, IDescribeMyself,
IEventStoreInstrumentation
IAggregationSourceFactory<IQuerySession>, IDescribeMyself
{
private readonly Cache<Type, string> _aggregateNameByType =
new(type => type.IsGenericType ? type.ShortNameInCode() : type.Name.ToTableAlias());
Expand Down Expand Up @@ -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.
/// <para>
/// This is the long-standing Marten-side toggle; #4686 added the storage-agnostic
/// <see cref="IEventStoreInstrumentation.ExtendedProgressionEnabled"/> as a sibling so
/// store-agnostic monitoring tooling (e.g. <c>Wolverine.CritterWatch.Marten</c>) 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 <c>ExtendedProgressionEnabled</c> on the interface.
/// </para>
/// </summary>
public bool EnableExtendedProgressionTracking
{
get => ExtendedProgressionEnabled;
set => ExtendedProgressionEnabled = value;
get;
set;
}

/// <summary>
/// <see cref="IEventStoreInstrumentation.ExtendedProgressionEnabled"/> -- the primary
/// (storage-agnostic) toggle that controls extended progression tracking. Aliased by the
/// legacy <see cref="EnableExtendedProgressionTracking"/> property.
/// <para>
/// **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 <c>EnableExtendedProgressionTracking</c>) explicitly.
/// </para>
/// </summary>
public bool ExtendedProgressionEnabled { get; set; } = true;

public bool UseArchivedStreamPartitioning { get; set; }
public bool UseListenNotifyForEventAppends { get; set; }

Expand Down
15 changes: 15 additions & 0 deletions src/Marten/Events/IEventStoreOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,21 @@ public IEventStoreOptions Upcast<TOldEvent, TEvent>(
/// and eliminate the per-query LEFT JOINs across tag tables.
/// </summary>
DcbStorageMode DcbStorageMode { get; set; }

/// <summary>
/// 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.
/// <para>
/// This is the long-standing Marten-side toggle; #4686 added the storage-agnostic
/// <see cref="IEventStoreInstrumentation.ExtendedProgressionEnabled"/> as a sibling so
/// store-agnostic monitoring tooling (e.g. <c>Wolverine.CritterWatch.Marten</c>) 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 <c>ExtendedProgressionEnabled</c> on the interface.
/// </para>
/// </summary>
public bool EnableExtendedProgressionTracking { get; set; }
}
}

Expand Down
15 changes: 15 additions & 0 deletions src/Marten/Events/IReadOnlyEventStoreOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,19 @@ public interface IReadOnlyEventStoreOptions
/// Index names that the schema migration functionality should ignore on the event-store tables.
/// </summary>
IReadOnlyList<string> IgnoredIndexes { get; }

/// <summary>
/// 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.
/// <para>
/// This is the long-standing Marten-side toggle; #4686 added the storage-agnostic
/// <see cref="IEventStoreInstrumentation.ExtendedProgressionEnabled"/> as a sibling so
/// store-agnostic monitoring tooling (e.g. <c>Wolverine.CritterWatch.Marten</c>) 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 <c>ExtendedProgressionEnabled</c> on the interface.
/// </para>
/// </summary>
bool EnableExtendedProgressionTracking { get; set; }
}
30 changes: 30 additions & 0 deletions src/Marten/MartenServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ Func<IServiceProvider, StoreOptions> optionSource
services.AddSingleton<IEventStore>(s => (IEventStore)s.GetRequiredService<IDocumentStore>());
services.AddSingleton<IDocumentStoreUsageSource>(s =>
(IDocumentStoreUsageSource)s.GetRequiredService<IDocumentStore>());

var instrument = new SetEventStoreInstrumentation();
services.AddSingleton<IConfigureMarten>(instrument);
services.AddSingleton<IEventStoreInstrumentation>(instrument);

services.AddSingleton(s =>
{
var options = optionSource(s);
Expand Down Expand Up @@ -328,6 +333,10 @@ public static MartenStoreExpression<T> AddMartenStore<T>(this IServiceCollection
services.AddSingleton<IEventStore>(s => (IEventStore)s.GetRequiredService<T>());
services.AddSingleton<IDocumentStoreUsageSource>(s => (IDocumentStoreUsageSource)s.GetRequiredService<T>());

var instrument = new SetEventStoreInstrumentation<T>();
services.AddSingleton<IConfigureMarten<T>>(instrument);
services.AddSingleton<IEventStoreInstrumentation>(instrument);

var stores = services
.Where(x => !x.IsKeyedService)
.Select(x => x.ImplementationInstance)
Expand Down Expand Up @@ -1081,3 +1090,24 @@ public IDocumentStore Resolve(IServiceProvider services)
return services.GetRequiredService<T>();
}
}

// 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<T>: IConfigureMarten<T>, IEventStoreInstrumentation where T : IDocumentStore
{
public void Configure(IServiceProvider services, StoreOptions options)
{
options.EventGraph.EnableExtendedProgressionTracking = ExtendedProgressionEnabled;
}

public bool ExtendedProgressionEnabled { get; set; }
}
Loading