Skip to content
Closed
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using IntegrationTests;
using JasperFx.Core.Reflection;
using JasperFx.Events.Daemon;
using JasperFx.Events.Projections;
using Marten;
using MartenTests.Distribution.TripDomain;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Shouldly;
using Wolverine;
using Wolverine.Marten;

namespace MartenTests.Bugs;

// GH-3290: with UseWolverineManagedEventSubscriptionDistribution = true, Marten's
// DocumentStore wrote a misleading "The async daemon is disabled ... will not be
// executed" console warning at startup even though Wolverine's managed distribution
// does run the async projections. The Marten integration now reflects the real daemon
// state on StoreOptions.Projections.AsyncMode so the warning is not raised.
public class Bug_3290_misleading_daemon_disabled_warning : PostgresqlContext
{
private static IHostBuilder configureHost(bool useWolverineManagedDistribution,
DaemonMode? explicitDaemonMode = null)
{
return Host.CreateDefaultBuilder()
.UseWolverine(opts =>
{
var marten = opts.Services.AddMarten(m =>
{
m.DisableNpgsqlLogging = true;
m.Connection(Servers.PostgresConnectionString);
m.DatabaseSchemaName = "bug3290";

m.Projections.Add<TripProjection>(ProjectionLifecycle.Async);
})
.IntegrateWithWolverine(m =>
{
m.UseWolverineManagedEventSubscriptionDistribution = useWolverineManagedDistribution;
});

if (explicitDaemonMode.HasValue)
{
marten.AddAsyncDaemon(explicitDaemonMode.Value);
}
});
}

// Marten writes the async-daemon warning with Console.WriteLine inside the
// DocumentStore constructor, so capture stdout around the first resolution of
// the store. The MartenTests assembly runs a single collection (no parallel
// test classes), so swapping Console.Out here is safe.
private static (DocumentStore Store, string ConsoleOutput) buildStoreCapturingConsole(IHost host)
{
var original = Console.Out;
var writer = new StringWriter();
Console.SetOut(writer);
try
{
var store = host.Services.GetRequiredService<IDocumentStore>().As<DocumentStore>();
return (store, writer.ToString());
}
finally
{
Console.SetOut(original);
}
}

[Fact]
public void managed_distribution_reflects_daemon_state_and_does_not_warn()
{
using var host = configureHost(useWolverineManagedDistribution: true).Build();

var (store, console) = buildStoreCapturingConsole(host);

// Wolverine's managed distribution replaces AddAsyncDaemon(DaemonMode.HotCold),
// so Marten should see the equivalent daemon mode...
store.Options.Projections.AsyncMode.ShouldBe(DaemonMode.HotCold);

// ...and must not claim the async projections will not be executed
console.ShouldNotContain("The async daemon is disabled");
}

[Fact]
public void without_managed_distribution_the_warning_still_fires()
{
using var host = configureHost(useWolverineManagedDistribution: false).Build();

var (store, console) = buildStoreCapturingConsole(host);

store.Options.Projections.AsyncMode.ShouldBe(DaemonMode.Disabled);
console.ShouldContain("The async daemon is disabled");
}

[Fact]
public void explicit_add_async_daemon_choice_is_not_overwritten()
{
using var host = configureHost(useWolverineManagedDistribution: true, DaemonMode.Solo).Build();

var (store, _) = buildStoreCapturingConsole(host);

store.Options.Projections.AsyncMode.ShouldBe(DaemonMode.Solo);
}
}
15 changes: 15 additions & 0 deletions src/Persistence/Wolverine.Marten/MartenIntegration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using JasperFx.Core;
using JasperFx.Core.Reflection;
using JasperFx.Events;
using JasperFx.Events.Daemon;
using Marten;
using Marten.Events;
using Marten.Exceptions;
Expand Down Expand Up @@ -168,6 +169,20 @@ public void Configure(IServiceProvider services, StoreOptions options)
{
options.Events.MessageOutbox = new MartenToWolverineOutbox(services, StoreType);

// GH-3290: when Wolverine manages the event subscription distribution, it replaces
// Marten's AddAsyncDaemon(DaemonMode.HotCold) registration outright, but Marten only
// knows the daemon state through Projections.AsyncMode. Left at Disabled, Marten's
// DocumentStore writes a misleading "The async daemon is disabled ... projections
// will not be executed" warning at startup even though Wolverine IS running the
// async projections. Reflect the real daemon state so the warning is not raised.
// Only upgrades from Disabled — an explicit user AddAsyncDaemon() choice is left alone.
var integration = services.GetService<MartenIntegration>();
if (integration is { UseWolverineManagedEventSubscriptionDistribution: true }
&& options.Projections.AsyncMode == DaemonMode.Disabled)
{
options.Projections.AsyncMode = DaemonMode.HotCold;
}

// Envelope is Wolverine's operational outbox document. Keep it
// single-tenant and unpartitioned regardless of blanket document
// policies the user has applied (AllDocumentsAreMultiTenanted or
Expand Down
Loading