Skip to content

Commit

Permalink
Attempt to convert the primary seam (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielmarbach committed Mar 26, 2024
1 parent 7864564 commit 25ad59e
Show file tree
Hide file tree
Showing 18 changed files with 98 additions and 71 deletions.
Expand Up @@ -3,7 +3,7 @@ namespace ServiceControl.AcceptanceTests.TestSupport
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using ServiceControl.Persistence;
using Persistence;

class TestServerHostLifetime(IPersistenceLifecycle persistenceLifecycle) : IHostLifetime
{
Expand Down
Expand Up @@ -28,7 +28,6 @@ static void ConfigureLifecycle(IServiceCollection services, DatabaseConfiguratio
var serverConfiguration = databaseConfiguration.ServerConfiguration;
if (serverConfiguration.UseEmbeddedServer)
{
// Installer scenarios do not use the host and do not have a lifetime
services.AddSingleton<RavenEmbeddedPersistenceLifecycle>();
services.AddSingleton<IRavenPersistenceLifecycle>(provider => provider.GetRequiredService<RavenEmbeddedPersistenceLifecycle>());
services.AddSingleton<IRavenDocumentStoreProvider>(provider => provider.GetRequiredService<RavenEmbeddedPersistenceLifecycle>());
Expand Down
7 changes: 7 additions & 0 deletions src/ServiceControl.Audit/HostApplicationBuilderExtensions.cs
Expand Up @@ -85,6 +85,13 @@ static class HostApplicationBuilderExtensions
builder.Services.AddWindowsService();
}

public static void AddServiceControlAuditInstallers(this IHostApplicationBuilder builder, Settings settings)
{
var persistenceConfiguration = PersistenceConfigurationFactory.LoadPersistenceConfiguration(settings.PersistenceType);
var persistenceSettings = persistenceConfiguration.BuildPersistenceSettings(settings);
builder.Services.AddInstaller(persistenceSettings, persistenceConfiguration);
}

static TransportSettings MapSettings(Settings settings)
{
var transportSettings = new TransportSettings
Expand Down
Expand Up @@ -4,8 +4,6 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Persistence;
using Settings;

static class HostApplicationBuilderExtensions
{
Expand All @@ -27,12 +25,5 @@ public static void AddServiceControlAuditApi(this IHostApplicationBuilder builde
controllers.AddApplicationPart(Assembly.GetExecutingAssembly());
controllers.AddJsonOptions(options => options.JsonSerializerOptions.CustomizeDefaults());
}

public static void AddServiceControlAuditInstallers(this IHostApplicationBuilder builder, Settings settings)
{
var persistenceConfiguration = PersistenceConfigurationFactory.LoadPersistenceConfiguration(settings.PersistenceType);
var persistenceSettings = persistenceConfiguration.BuildPersistenceSettings(settings);
builder.Services.AddInstaller(persistenceSettings, persistenceConfiguration);
}
}
}
@@ -0,0 +1,7 @@
namespace ServiceControl.Persistence.RavenDB
{
// Currently this is a marker interface as an intermediate step. Eventually once the custom lifetimes are gone
// it is possible to align things with the audit instance by moving the initialize method from the base interface
// to this one and delete the base interface. One step at a time.
interface IRavenPersistenceLifecycle : IPersistenceLifecycle;
}
Expand Up @@ -10,10 +10,9 @@ namespace ServiceControl.Persistence.RavenDB
using NServiceBus.Logging;
using Raven.Client.Documents;
using Raven.Client.Exceptions.Database;
using ServiceControl.Persistence;

sealed class RavenEmbeddedPersistenceLifecycle(RavenPersisterSettings databaseConfiguration, IHostApplicationLifetime? lifetime)
: IPersistenceLifecycle, IDisposable
: IRavenPersistenceLifecycle, IDisposable
{
public IDocumentStore GetDocumentStore()
{
Expand Down
@@ -1,13 +1,14 @@
namespace ServiceControl.Persistence.RavenDB
#nullable enable

namespace ServiceControl.Persistence.RavenDB
{
using System;
using System.Threading;
using System.Threading.Tasks;
using Raven.Client.Documents;
using Raven.Client.Documents.Conventions;
using ServiceControl.Persistence;

sealed class RavenExternalPersistenceLifecycle(RavenPersisterSettings settings) : IPersistenceLifecycle, IDisposable
sealed class RavenExternalPersistenceLifecycle(RavenPersisterSettings settings) : IRavenPersistenceLifecycle, IDisposable
{
public IDocumentStore GetDocumentStore()
{
Expand Down Expand Up @@ -39,6 +40,6 @@ public async Task Initialize(CancellationToken cancellationToken)

public void Dispose() => documentStore?.Dispose();

IDocumentStore documentStore;
IDocumentStore? documentStore;
}
}
18 changes: 0 additions & 18 deletions src/ServiceControl.Persistence.RavenDB/RavenInstaller.cs

This file was deleted.

43 changes: 25 additions & 18 deletions src/ServiceControl.Persistence.RavenDB/RavenPersistence.cs
Expand Up @@ -18,10 +18,9 @@

class RavenPersistence(RavenPersisterSettings settings) : IPersistence
{
public void Configure(IServiceCollection services)
public void AddPersistence(IServiceCollection services)
{
services.AddSingleton<PersistenceSettings>(settings);
services.AddSingleton(settings);

ConfigureLifecycle(services);

Expand Down Expand Up @@ -75,28 +74,36 @@ public void Configure(IServiceCollection services)
services.AddSingleton<SagaAuditDestinationCustomCheck.State>();
}

void ConfigureLifecycle(IServiceCollection serviceCollection)
public void AddInstaller(IServiceCollection services)
{
if (settings.UseEmbeddedServer)
{
serviceCollection.AddSingleton<RavenEmbeddedPersistenceLifecycle>();
serviceCollection.AddSingleton<IPersistenceLifecycle>(b => b.GetService<RavenEmbeddedPersistenceLifecycle>());
serviceCollection.AddSingleton(b => b.GetService<RavenEmbeddedPersistenceLifecycle>().GetDocumentStore());
return;
}
ConfigureLifecycle(services);

serviceCollection.AddSingleton<RavenExternalPersistenceLifecycle>();
serviceCollection.AddSingleton<IPersistenceLifecycle>(b => b.GetService<RavenExternalPersistenceLifecycle>());
serviceCollection.AddSingleton(b => b.GetService<RavenExternalPersistenceLifecycle>().GetDocumentStore());
// As in intermediate step we are using the hosted service only here to bootstrap the persistence
// The production code still uses the custom lifetime which eventually we need to address by
// introducing a document store provider similar to what the audit instance does or synchronously
// initialize the document store as part of the container wiring.
services.AddHostedService<RavenPersistenceLifecycleHostedService>();
}

public IPersistenceInstaller CreateInstaller()
void ConfigureLifecycle(IServiceCollection services)
{
var serviceCollection = new ServiceCollection();
ConfigureLifecycle(serviceCollection);
services.AddSingleton(settings);

if (settings.UseEmbeddedServer)
{
services.AddSingleton<RavenEmbeddedPersistenceLifecycle>();
// This binding is only necessary as long as we keep around the custom lifetimes
services.AddSingleton<IPersistenceLifecycle>(b => b.GetService<RavenEmbeddedPersistenceLifecycle>());
services.AddSingleton<IRavenPersistenceLifecycle>(b => b.GetService<RavenEmbeddedPersistenceLifecycle>());
services.AddSingleton(b => b.GetService<RavenEmbeddedPersistenceLifecycle>().GetDocumentStore());
return;
}

serviceCollection.AddSingleton(settings);
return new RavenInstaller(serviceCollection);
services.AddSingleton<RavenExternalPersistenceLifecycle>();
// This binding is only necessary as long as we keep around the custom lifetimes
services.AddSingleton<IPersistenceLifecycle>(b => b.GetService<RavenExternalPersistenceLifecycle>());
services.AddSingleton<IRavenPersistenceLifecycle>(b => b.GetService<RavenExternalPersistenceLifecycle>());
services.AddSingleton(b => b.GetService<RavenExternalPersistenceLifecycle>().GetDocumentStore());
}
}
}
@@ -0,0 +1,13 @@
namespace ServiceControl.Persistence.RavenDB
{
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;

class RavenPersistenceLifecycleHostedService(IRavenPersistenceLifecycle persistenceLifecycle) : IHostedService
{
public Task StartAsync(CancellationToken cancellationToken) => persistenceLifecycle.Initialize(cancellationToken);

public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
}
Expand Up @@ -4,7 +4,6 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting.Internal;
using Microsoft.Extensions.Logging.Abstractions;
using NUnit.Framework;
Expand Down
31 changes: 20 additions & 11 deletions src/ServiceControl.Persistence.Tests/PersistenceTestBase.cs
Expand Up @@ -3,6 +3,7 @@
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NUnit.Framework;
using Raven.Client.Documents;
using ServiceControl.Operations.BodyStorage;
Expand All @@ -19,7 +20,7 @@ public abstract class PersistenceTestBase
{
string databaseName;
EmbeddedDatabase embeddedServer;
ServiceProvider serviceProvider;
IHost host;

[SetUp]
public async Task SetUp()
Expand All @@ -29,6 +30,12 @@ public async Task SetUp()

await TestContext.Out.WriteLineAsync($"Test Database Name: {databaseName}");

var hostBuilder = Host.CreateApplicationBuilder(new HostApplicationBuilderSettings
{
// Force the DI container to run the dependency resolution check to verify all dependencies can be resolved
EnvironmentName = Environments.Production
});

embeddedServer = await SharedEmbeddedServer.GetInstance();

PersistenceSettings = new RavenPersisterSettings
Expand All @@ -42,15 +49,16 @@ public async Task SetUp()

var persistence = new RavenPersistenceConfiguration().Create(PersistenceSettings);

var services = new ServiceCollection();
persistence.Configure(services);
persistence.AddPersistence(hostBuilder.Services);
persistence.AddInstaller(hostBuilder.Services);

RegisterServices.Invoke(hostBuilder.Services);

RegisterServices?.Invoke(services);
host = hostBuilder.Build();

serviceProvider = services.BuildServiceProvider();
await GetRequiredService<IPersistenceLifecycle>().Initialize();

var persistenceLifecycle = GetRequiredService<IPersistenceLifecycle>();
await persistenceLifecycle.Initialize();
await host.StartAsync();

CompleteDatabaseOperation();
}
Expand All @@ -61,7 +69,8 @@ public async Task TearDown()
// Needs to go first or database will be disposed
await embeddedServer.DeleteDatabase(databaseName);

await serviceProvider.DisposeAsync();
await host.StopAsync();
host.Dispose();
}

protected PersistenceSettings PersistenceSettings
Expand All @@ -70,10 +79,10 @@ protected PersistenceSettings PersistenceSettings
private set;
}

protected T GetRequiredService<T>() => serviceProvider.GetRequiredService<T>();
protected object GetRequiredService(Type serviceType) => serviceProvider.GetRequiredService(serviceType);
protected T GetRequiredService<T>() => host.Services.GetRequiredService<T>();
protected object GetRequiredService(Type serviceType) => host.Services.GetRequiredService(serviceType);

protected Action<IServiceCollection> RegisterServices { get; set; }
protected Action<IServiceCollection> RegisterServices { get; set; } = _ => { };

protected void CompleteDatabaseOperation() => GetRequiredService<IDocumentStore>().WaitForIndexing();

Expand Down
4 changes: 2 additions & 2 deletions src/ServiceControl.Persistence/IPersistence.cs
Expand Up @@ -4,7 +4,7 @@

public interface IPersistence
{
void Configure(IServiceCollection services);
IPersistenceInstaller CreateInstaller();
void AddPersistence(IServiceCollection services);
void AddInstaller(IServiceCollection services);
}
}
6 changes: 6 additions & 0 deletions src/ServiceControl/HostApplicationBuilderExtensions.cs
Expand Up @@ -117,6 +117,12 @@ public static void AddServiceControl(this IHostApplicationBuilder hostBuilder, S
hostBuilder.AddServiceControlComponents(settings, ServiceControlMainInstance.Components);
}

public static void AddServiceControlInstallers(this IHostApplicationBuilder hostApplicationBuilder, Settings settings)
{
var persistence = PersistenceFactory.Create(settings);
persistence.AddInstaller(hostApplicationBuilder.Services);
}

static TransportSettings MapSettings(Settings settings)
{
var transportSettings = new TransportSettings
Expand Down
9 changes: 8 additions & 1 deletion src/ServiceControl/Hosting/Commands/SetupCommand.cs
Expand Up @@ -3,10 +3,10 @@
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using LicenseManagement;
using Microsoft.Extensions.Hosting;
using NServiceBus.Logging;
using Particular.ServiceControl;
using Particular.ServiceControl.Hosting;
using Persistence;
using ServiceBus.Management.Infrastructure.Installers;
using ServiceBus.Management.Infrastructure.Settings;
using Transports;
Expand Down Expand Up @@ -52,6 +52,13 @@ public override async Task Execute(HostArguments args, Settings settings)
await transportCustomization.ProvisionQueues(transportSettings,
componentSetupContext.Queues);
}

var hostBuilder = Host.CreateApplicationBuilder();
hostBuilder.AddServiceControlInstallers(settings);

using var host = hostBuilder.Build();
await host.StartAsync();
await host.StopAsync();
}

bool ValidateLicense(Settings settings)
Expand Down
Expand Up @@ -6,7 +6,7 @@
using Microsoft.Extensions.Hosting.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ServiceControl.Persistence;
using Persistence;

sealed class PersisterInitializingConsoleLifetime(IOptions<ConsoleLifetimeOptions> options, IHostEnvironment environment, IHostApplicationLifetime applicationLifetime, IOptions<HostOptions> hostOptions, ILoggerFactory loggerFactory, IPersistenceLifecycle persistenceLifecycle)
: ConsoleLifetime(options, environment, applicationLifetime, hostOptions, loggerFactory), IHostLifetime
Expand Down
Expand Up @@ -7,7 +7,7 @@
using Microsoft.Extensions.Hosting.WindowsServices;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ServiceControl.Persistence;
using Persistence;

[SupportedOSPlatform("windows")]
sealed class PersisterInitializingWindowsServiceLifetime(IHostEnvironment environment, IHostApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory, IOptions<HostOptions> optionsAccessor, IOptions<WindowsServiceLifetimeOptions> windowsServiceOptionsAccessor, IPersistenceLifecycle persistenceLifecycle)
Expand Down
Expand Up @@ -9,7 +9,7 @@ static class PersistenceServiceCollectionExtensions
bool maintenanceMode = false)
{
var persistence = PersistenceFactory.Create(settings, maintenanceMode);
persistence.Configure(services);
persistence.AddPersistence(services);
}
}
}

0 comments on commit 25ad59e

Please sign in to comment.