diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerGrain.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerGrain.cs index a109042d2..090fec75d 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerGrain.cs @@ -20,13 +20,13 @@ namespace Squidex.Domain.Apps.Entities.Contents { public sealed class ContentSchedulerGrain : Grain, IContentSchedulerGrain, IRemindable { - private readonly IContentRepository contentRepository; - private readonly ICommandBus commandBus; + private readonly Lazy contentRepository; + private readonly Lazy commandBus; private readonly IClock clock; public ContentSchedulerGrain( - IContentRepository contentRepository, - ICommandBus commandBus, + Lazy contentRepository, + Lazy commandBus, IClock clock) { Guard.NotNull(contentRepository, nameof(contentRepository)); @@ -57,11 +57,11 @@ namespace Squidex.Domain.Apps.Entities.Contents { var now = clock.GetCurrentInstant(); - return contentRepository.QueryScheduledWithoutDataAsync(now, content => + return contentRepository.Value.QueryScheduledWithoutDataAsync(now, content => { var command = new ChangeContentStatus { ContentId = content.Id, Status = content.ScheduledTo.Value, Actor = content.ScheduledBy }; - return commandBus.PublishAsync(command); + return commandBus.Value.PublishAsync(command); }); } diff --git a/src/Squidex.Infrastructure/Lazier.cs b/src/Squidex.Infrastructure/Lazier.cs new file mode 100644 index 000000000..aae97240c --- /dev/null +++ b/src/Squidex.Infrastructure/Lazier.cs @@ -0,0 +1,20 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using Microsoft.Extensions.DependencyInjection; + +namespace Squidex.Infrastructure +{ + public sealed class Lazier : Lazy where T : class + { + public Lazier(IServiceProvider provider) + : base(provider.GetRequiredService) + { + } + } +} diff --git a/src/Squidex/Config/Domain/InfrastructureServices.cs b/src/Squidex/Config/Domain/InfrastructureServices.cs index 085e84e61..5734ecf20 100644 --- a/src/Squidex/Config/Domain/InfrastructureServices.cs +++ b/src/Squidex/Config/Domain/InfrastructureServices.cs @@ -5,10 +5,12 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.Extensions.DependencyInjection; using NodaTime; +using Squidex.Infrastructure; using Squidex.Infrastructure.UsageTracking; #pragma warning disable RECS0092 // Convert field to readonly @@ -30,6 +32,8 @@ namespace Squidex.Config.Domain services.AddSingletonAs() .As(); + + services.AddTransient(typeof(Lazy<>), typeof(Lazier<>)); } } } diff --git a/src/Squidex/Config/Orleans/SiloServices.cs b/src/Squidex/Config/Orleans/SiloServices.cs index 737c1ef15..865302883 100644 --- a/src/Squidex/Config/Orleans/SiloServices.cs +++ b/src/Squidex/Config/Orleans/SiloServices.cs @@ -22,18 +22,18 @@ namespace Squidex.Config.Orleans var mongoConfiguration = config.GetRequiredValue("store:mongoDb:configuration"); var mongoDatabaseName = config.GetRequiredValue("store:mongoDb:database"); - services.AddMongoDBMembershipTable(c => + services.AddMongoDBMembershipTable(options => { - c.ConnectionString = mongoConfiguration; - c.CollectionPrefix = "Orleans_"; - c.DatabaseName = mongoDatabaseName; + options.ConnectionString = mongoConfiguration; + options.CollectionPrefix = "Orleans_"; + options.DatabaseName = mongoDatabaseName; }); - services.AddMongoDBReminders(c => + services.AddMongoDBReminders(options => { - c.ConnectionString = mongoConfiguration; - c.CollectionPrefix = "Orleans_"; - c.DatabaseName = mongoDatabaseName; + options.ConnectionString = mongoConfiguration; + options.CollectionPrefix = "Orleans_"; + options.DatabaseName = mongoDatabaseName; }); } }); diff --git a/src/Squidex/Config/Orleans/SiloWrapper.cs b/src/Squidex/Config/Orleans/SiloWrapper.cs index 18f99175e..75991185c 100644 --- a/src/Squidex/Config/Orleans/SiloWrapper.cs +++ b/src/Squidex/Config/Orleans/SiloWrapper.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Diagnostics; using System.Net; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; @@ -20,6 +21,7 @@ using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Rules; using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing.Grains; +using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Log.Adapter; using Squidex.Infrastructure.Orleans; @@ -27,7 +29,8 @@ namespace Squidex.Config.Orleans { public class SiloWrapper : DisposableObjectBase, IInitializable, IDisposable { - private readonly ISiloHost silo; + private readonly Lazy silo; + private readonly ISemanticLog log; internal sealed class Source : IConfigurationSource { @@ -44,61 +47,121 @@ namespace Squidex.Config.Orleans } } - public SiloWrapper(IConfiguration configuration) + public SiloWrapper(IConfiguration config, ISemanticLog log) { - J.Serializer = SerializationServices.DefaultJsonSerializer; - - silo = new SiloHostBuilder() - .UseDashboard(options => options.HostSelf = true) - .AddStartupTask>() - .AddStartupTask>() - .AddStartupTask>() - .ConfigureEndpoints(Dns.GetHostName(), 11111, 40000, listenOnAnyHostAddress: true) - .Configure(options => - { - options.ClusterId = "squidex"; - }) - .ConfigureLogging((hostingContext, builder) => - { - builder.AddConfiguration(hostingContext.Configuration.GetSection("logging")); - builder.AddSemanticLog(); - builder.AddFilter("Orleans.Runtime.SiloControl", LogLevel.Warning); - }) - .ConfigureApplicationParts(builder => - { - builder.AddApplicationPart(SquidexEntities.Assembly); - builder.AddApplicationPart(SquidexInfrastructure.Assembly); - }) - .ConfigureServices((context, services) => + this.log = log; + + silo = new Lazy(() => + { + J.Serializer = SerializationServices.DefaultJsonSerializer; + + var hostBuilder = new SiloHostBuilder() + .UseDashboard(options => options.HostSelf = true) + .AddStartupTask>() + .AddStartupTask>() + .AddStartupTask>() + .Configure(options => + { + options.ClusterId = "squidex"; + }) + .ConfigureLogging((hostingContext, builder) => + { + builder.AddConfiguration(hostingContext.Configuration.GetSection("logging")); + builder.AddSemanticLog(); + builder.AddFilter("Orleans.Runtime.SiloControl", LogLevel.Warning); + }) + .ConfigureApplicationParts(builder => + { + builder.AddApplicationPart(SquidexEntities.Assembly); + builder.AddApplicationPart(SquidexInfrastructure.Assembly); + }) + .ConfigureServices((context, services) => + { + services.AddAppSiloServices(context.Configuration); + services.AddAppServices(context.Configuration); + + services.Configure(options => options.FastKillOnProcessExit = false); + }) + .ConfigureAppConfiguration((hostContext, builder) => + { + if (config is IConfigurationRoot root) + { + foreach (var provider in root.Providers) + { + builder.Add(new Source(provider)); + } + } + }); + + config.ConfigureByOption("orleans:clustering", new Options { - services.AddAppSiloServices(context.Configuration); - services.AddAppServices(context.Configuration); + ["MongoDB"] = () => + { + hostBuilder.ConfigureEndpoints(Dns.GetHostName(), 11111, 40000, listenOnAnyHostAddress: true); + + var mongoConfiguration = config.GetRequiredValue("store:mongoDb:configuration"); + var mongoDatabaseName = config.GetRequiredValue("store:mongoDb:database"); + + hostBuilder.UseMongoDBClustering(options => + { + options.ConnectionString = mongoConfiguration; + options.CollectionPrefix = "Orleans_"; + options.DatabaseName = mongoDatabaseName; + }); + }, + ["Development"] = () => + { + hostBuilder.UseLocalhostClustering(gatewayPort: 40000, clusterId: "squidex"); + hostBuilder.Configure(options => options.ExpectedClusterSize = 1); + } + }); - services.Configure(options => options.FastKillOnProcessExit = false); - }) - .ConfigureAppConfiguration((hostContext, builder) => + config.ConfigureByOption("store:type", new Options { - if (configuration is IConfigurationRoot root) + ["MongoDB"] = () => { - foreach (var provider in root.Providers) + var mongoConfiguration = config.GetRequiredValue("store:mongoDb:configuration"); + var mongoDatabaseName = config.GetRequiredValue("store:mongoDb:database"); + + hostBuilder.UseMongoDBReminders(options => { - builder.Add(new Source(provider)); - } + options.ConnectionString = mongoConfiguration; + options.CollectionPrefix = "Orleans_"; + options.DatabaseName = mongoDatabaseName; + }); } - }) - .Build(); + }); + + return hostBuilder.Build(); + }); } public void Initialize() { - silo.StartAsync().Wait(); + var watch = Stopwatch.StartNew(); + try + { + silo.Value.StartAsync().Wait(); + } + finally + { + watch.Stop(); + + log.LogInformation(w => w + .WriteProperty("message", "Silo started") + .WriteProperty("elapsed", watch.Elapsed) + .WriteProperty("elapsedMs", watch.ElapsedMilliseconds)); + } } protected override void DisposeObject(bool disposing) { if (disposing) { - Task.Run(() => silo.StopAsync()).Wait(); + if (silo.IsValueCreated) + { + Task.Run(() => silo.Value.StopAsync()).Wait(); + } } } } diff --git a/src/Squidex/Squidex.csproj b/src/Squidex/Squidex.csproj index c20c303e0..e898de24f 100644 --- a/src/Squidex/Squidex.csproj +++ b/src/Squidex/Squidex.csproj @@ -70,8 +70,8 @@ - - + + @@ -104,8 +104,4 @@ - - - - \ No newline at end of file diff --git a/src/Squidex/appsettings.json b/src/Squidex/appsettings.json index d2ae2abd2..3dfc0aa47 100644 --- a/src/Squidex/appsettings.json +++ b/src/Squidex/appsettings.json @@ -84,6 +84,15 @@ "exposeSourceUrl": false }, + "orleans": { + /* + * Define the clustering type. + * + * Supported: MongoDB, Development + */ + "clustering": "MongoDb" + }, + "eventStore": { /* * Define the type of the event store.