diff --git a/src/Akka.Cluster.Hosting.Tests/ClusterShardingSpecs.cs b/src/Akka.Cluster.Hosting.Tests/ClusterShardingSpecs.cs index dc211ab..c29ded1 100644 --- a/src/Akka.Cluster.Hosting.Tests/ClusterShardingSpecs.cs +++ b/src/Akka.Cluster.Hosting.Tests/ClusterShardingSpecs.cs @@ -2,9 +2,14 @@ using System.Collections.Generic; using System.Threading.Tasks; using Akka.Actor; +using Akka.Cluster.Hosting.SBR; +using Akka.Cluster.Hosting.Tests.Lease; using Akka.Cluster.Sharding; +using Akka.Cluster.Tools.Singleton; +using Akka.Configuration; using Akka.Hosting; using FluentAssertions; +using FluentAssertions.Extensions; using Microsoft.Extensions.DependencyInjection; using Xunit; using Xunit.Abstractions; @@ -110,4 +115,67 @@ public async Task Should_use_ActorRegistry_with_ShardRegion() id.Should().Be("foo"); sourceRef.Should().Be(actorRegistry.Get()); } + + [Fact(DisplayName = "ShardOptions with different values should generate valid ClusterShardSettings")] + public void ShardOptionsTest() + { + var settings1 = ToSettings(new ShardOptions + { + RememberEntities = true, + StateStoreMode = StateStoreMode.Persistence, + RememberEntitiesStore = RememberEntitiesStore.Eventsourced, + Role = "first", + PassivateIdleEntityAfter = 1.Seconds(), + SnapshotPluginId = "firstSnapshot", + JournalPluginId = "firstJournal", + LeaseImplementation = new TestLeaseOption(), + LeaseRetryInterval = 2.Seconds(), + ShardRegionQueryTimeout = 3.Seconds(), + }); + settings1.RememberEntities.Should().BeTrue(); + settings1.StateStoreMode.Should().Be(StateStoreMode.Persistence); + settings1.RememberEntitiesStore.Should().Be(RememberEntitiesStore.Eventsourced); + settings1.Role.Should().Be("first"); + settings1.PassivateIdleEntityAfter.Should().Be(1.Seconds()); + settings1.SnapshotPluginId.Should().Be("firstSnapshot"); + settings1.JournalPluginId.Should().Be("firstJournal"); + settings1.LeaseSettings.LeaseImplementation.Should().Be("test-lease"); + settings1.LeaseSettings.LeaseRetryInterval.Should().Be(2.Seconds()); + settings1.ShardRegionQueryTimeout.Should().Be(3.Seconds()); + + var settings2 = ToSettings(new ShardOptions + { + RememberEntities = false, + StateStoreMode = StateStoreMode.DData, + RememberEntitiesStore = RememberEntitiesStore.DData, + Role = "second", + PassivateIdleEntityAfter = 4.Seconds(), + SnapshotPluginId = "secondSnapshot", + JournalPluginId = "secondJournal", + ShardRegionQueryTimeout = 5.Seconds(), + }); + settings2.RememberEntities.Should().BeFalse(); + settings2.StateStoreMode.Should().Be(StateStoreMode.DData); + settings2.RememberEntitiesStore.Should().Be(RememberEntitiesStore.DData); + settings2.Role.Should().Be("second"); + settings2.PassivateIdleEntityAfter.Should().Be(4.Seconds()); + settings2.JournalPluginId.Should().Be("secondJournal"); + settings2.SnapshotPluginId.Should().Be("secondSnapshot"); + settings2.LeaseSettings.Should().BeNull(); + settings2.ShardRegionQueryTimeout.Should().Be(5.Seconds()); + } + + private static ClusterShardingSettings ToSettings(ShardOptions shardOptions) + { + var defaultConfig = ClusterSharding.DefaultConfig() + .WithFallback(DistributedData.DistributedData.DefaultConfig()) + .WithFallback(ClusterSingletonManager.DefaultConfig()); + + var shardingConfig = ConfigurationFactory.ParseString(shardOptions.ToString()) + .WithFallback(defaultConfig.GetConfig("akka.cluster.sharding")); + var coordinatorConfig = defaultConfig.GetConfig( + shardingConfig.GetString("coordinator-singleton")); + + return ClusterShardingSettings.Create(shardingConfig, coordinatorConfig); + } } \ No newline at end of file diff --git a/src/Akka.Cluster.Hosting/AkkaClusterHostingExtensions.cs b/src/Akka.Cluster.Hosting/AkkaClusterHostingExtensions.cs index 76f7499..8fa669e 100644 --- a/src/Akka.Cluster.Hosting/AkkaClusterHostingExtensions.cs +++ b/src/Akka.Cluster.Hosting/AkkaClusterHostingExtensions.cs @@ -279,7 +279,7 @@ public sealed class ShardOptions /// /// /// Settings for the Distributed Data replicator. - /// The property is not used. The distributed-data + /// The property is not used. The distributed-data /// role will be the same as . /// Note that there is one Replicator per role and it's not possible /// to have different distributed-data settings for different sharding entity types. @@ -287,6 +287,9 @@ public sealed class ShardOptions /// NOTE This setting is only used when is set to /// /// + [Obsolete("This property is not being applied to the ActorSystem anymore. " + + "Use manual HOCON configuration to set \"akka.cluster.sharding.distributed-data\" values. " + + "Since v1.5.27")] public ShardingDDataOptions DistributedData { get; } = new(); /// @@ -303,12 +306,12 @@ public sealed class ShardOptions /// public TimeSpan? PassivateIdleEntityAfter { get; set; } - internal void Apply(AkkaConfigurationBuilder builder) + public TimeSpan? ShardRegionQueryTimeout { get; set; } + + public override string ToString() { - DistributedData.Apply(builder); - var sb = new StringBuilder(); - + if (Role is not null) sb.AppendLine($"role = {Role.ToHocon()}"); @@ -344,12 +347,10 @@ internal void Apply(AkkaConfigurationBuilder builder) else if(PassivateIdleEntityAfter is not null) sb.AppendLine($"passivate-idle-entity-after = {PassivateIdleEntityAfter.ToHocon()}"); - if (sb.Length > 0) - { - sb.Insert(0, "akka.cluster.sharding {\n"); - sb.AppendLine("}"); - builder.AddHocon(sb.ToString(), HoconAddMode.Prepend); - } + if (ShardRegionQueryTimeout is not null) + sb.AppendLine($"shard-region-query-timeout = {ShardRegionQueryTimeout.ToHocon()}"); + + return sb.ToString(); } } @@ -646,7 +647,7 @@ public static AkkaConfigurationBuilder WithDistributedData( builder.AddHocon(DistributedData.DistributedData.DefaultConfig(), HoconAddMode.Append); return builder; } - + /// /// Starts a actor for the given entity /// and registers the ShardRegion with in the @@ -850,9 +851,10 @@ public static AkkaConfigurationBuilder WithShardRegion( IMessageExtractor messageExtractor, ShardOptions shardOptions) { - shardOptions.Apply(builder); builder.AddHocon( - ClusterSharding.DefaultConfig().WithFallback(DistributedData.DistributedData.DefaultConfig()), + ClusterSharding.DefaultConfig() + .WithFallback(DistributedData.DistributedData.DefaultConfig()) + .WithFallback(ClusterSingletonManager.DefaultConfig()), HoconAddMode.Append); return builder.StartActors(Resolver); @@ -860,7 +862,12 @@ public static AkkaConfigurationBuilder WithShardRegion( async Task Resolver(ActorSystem system, IActorRegistry registry, IDependencyResolver resolver) { var props = entityPropsFactory(system, registry, resolver); - var settings = ClusterShardingSettings.Create(system); + var shardingConfig = ConfigurationFactory.ParseString(shardOptions.ToString()) + .WithFallback(system.Settings.Config.GetConfig("akka.cluster.sharding")); + var coordinatorConfig = system.Settings.Config.GetConfig( + shardingConfig.GetString("coordinator-singleton")); + + var settings = ClusterShardingSettings.Create(shardingConfig, coordinatorConfig); var allocationStrategy = ClusterSharding.Get(system).DefaultShardAllocationStrategy(settings); var shardRegion = await ClusterSharding.Get(system).StartAsync( typeName, props, settings, messageExtractor, allocationStrategy, @@ -911,23 +918,29 @@ public static AkkaConfigurationBuilder WithShardRegion( ExtractShardId extractShardId, ShardOptions shardOptions) { - shardOptions.Apply(builder); builder.AddHocon( - ClusterSharding.DefaultConfig().WithFallback(DistributedData.DistributedData.DefaultConfig()), + ClusterSharding.DefaultConfig() + .WithFallback(DistributedData.DistributedData.DefaultConfig()) + .WithFallback(ClusterSingletonManager.DefaultConfig()), HoconAddMode.Append); - + + return builder.StartActors(Resolver); + async Task Resolver(ActorSystem system, IActorRegistry registry, IDependencyResolver resolver) { var props = entityPropsFactory(system, registry, resolver); - var settings = ClusterShardingSettings.Create(system); + var shardingConfig = ConfigurationFactory.ParseString(shardOptions.ToString()) + .WithFallback(system.Settings.Config.GetConfig("akka.cluster.sharding")); + var coordinatorConfig = system.Settings.Config.GetConfig( + shardingConfig.GetString("coordinator-singleton")); + + var settings = ClusterShardingSettings.Create(shardingConfig, coordinatorConfig); var allocationStrategy = ClusterSharding.Get(system).DefaultShardAllocationStrategy(settings); var shardRegion = await ClusterSharding.Get(system).StartAsync( typeName, props, settings, extractEntityId, extractShardId, allocationStrategy, shardOptions.HandOffStopMessage ?? PoisonPill.Instance).ConfigureAwait(false); registry.Register(shardRegion); } - - return builder.StartActors(Resolver); } /// diff --git a/src/Akka.Hosting.API.Tests/verify/CoreApiSpec.ApproveCluster.verified.txt b/src/Akka.Hosting.API.Tests/verify/CoreApiSpec.ApproveCluster.verified.txt index c2a7ca5..27e2ef4 100644 --- a/src/Akka.Hosting.API.Tests/verify/CoreApiSpec.ApproveCluster.verified.txt +++ b/src/Akka.Hosting.API.Tests/verify/CoreApiSpec.ApproveCluster.verified.txt @@ -89,6 +89,9 @@ namespace Akka.Cluster.Hosting public sealed class ShardOptions { public ShardOptions() { } + [System.Obsolete("This property is not being applied to the ActorSystem anymore. Use manual HOCON c" + + "onfiguration to set \"akka.cluster.sharding.distributed-data\" values. Since v1.5." + + "27")] public Akka.Cluster.Hosting.ShardingDDataOptions DistributedData { get; } public bool? FailOnInvalidEntityStateTransition { get; set; } public object? HandOffStopMessage { get; set; } @@ -100,10 +103,12 @@ namespace Akka.Cluster.Hosting public bool? RememberEntities { get; set; } public Akka.Cluster.Sharding.RememberEntitiesStore? RememberEntitiesStore { get; set; } public string? Role { get; set; } + public System.TimeSpan? ShardRegionQueryTimeout { get; set; } public bool? ShouldPassivateIdleEntities { get; set; } public Akka.Persistence.Hosting.SnapshotOptions? SnapshotOptions { get; set; } public string? SnapshotPluginId { get; set; } public Akka.Cluster.Sharding.StateStoreMode? StateStoreMode { get; set; } + public override string ToString() { } } public sealed class ShardingDDataOptions : Akka.Cluster.Hosting.DDataOptions {