diff --git a/src/contrib/cluster/Akka.Cluster.Tools/Client/ClusterClientDiscovery.cs b/src/contrib/cluster/Akka.Cluster.Tools/Client/ClusterClientDiscovery.cs index 688f89b43d3..95ddb63343f 100644 --- a/src/contrib/cluster/Akka.Cluster.Tools/Client/ClusterClientDiscovery.cs +++ b/src/contrib/cluster/Akka.Cluster.Tools/Client/ClusterClientDiscovery.cs @@ -13,6 +13,8 @@ using Akka.Actor; using Akka.Discovery; using Akka.Event; +using Akka.Util; +using Akka.Util.Internal; #nullable enable namespace Akka.Cluster.Tools.Client; @@ -49,6 +51,7 @@ private sealed record ResolveResult(Contact Contact, IActorRef? Subject); private readonly string _targetActorSystemName; private readonly string _receptionistName; private readonly string _transportProtocol; + private readonly int _numberOfContacts; private readonly bool _verboseLogging; @@ -109,6 +112,8 @@ public ClusterClientDiscovery(ClusterClientSettings settings) _discoveryRetryInterval = _settings.DiscoverySettings.DiscoveryRetryInterval; _discoveryTimeout = _discoverySettings.DiscoveryTimeout; + _numberOfContacts = _discoverySettings.NumberOfContacts; + _verboseLogging = _settings.VerboseLogging; Become(Discovering); @@ -134,7 +139,7 @@ private ActorPath ResolvedTargetToReceptionistActorPath(ServiceDiscovery.Resolve { var networkAddress = string.IsNullOrWhiteSpace(target.Host) ? target.Address.ToString() : target.Host; var address = new Address(_transportProtocol, _targetActorSystemName, networkAddress, target.Port); - return new RootActorPath(address) / "system" / _discoverySettings.ReceptionistName; + return new RootActorPath(address) / "system" / _receptionistName; } private static async Task ResolveContact(Contact contact, TimeSpan timeout, CancellationToken ct) @@ -207,10 +212,13 @@ async Task VerifyContacts() } else { + var filteredContacts = TrimContacts(contacts, _numberOfContacts); if(_log.IsInfoEnabled) - _log.Info("Cluster client initial contacts are verified at [{0}], starting cluster client actor.", string.Join(", ", contacts.Select(c => c.Path))); + _log.Info( + "Cluster client initial contacts are verified at [{0}], starting cluster client actor.", + string.Join(", ", filteredContacts.Select(c => c.Path))); - Become(Active(contacts)); + Become(Active(filteredContacts)); } return true; @@ -229,6 +237,22 @@ async Task VerifyContacts() } } + /// + /// Trim the number of Contact in the `fullContact` array to `count` length + /// by picking random elements while avoiding repeating elements from being returned + /// + /// Array of Contacts + /// The number of elements to return + /// + private static Contact[] TrimContacts(Contact[] fullContact, int count) + { + if (fullContact.Length <= count) + return fullContact; + + fullContact.Shuffle(); + return fullContact.Take(count).ToArray(); + } + private Receive Active(Contact[] contacts) { if(_verboseLogging && _log.IsDebugEnabled) diff --git a/src/contrib/cluster/Akka.Cluster.Tools/Client/ClusterClientDiscoverySettings.cs b/src/contrib/cluster/Akka.Cluster.Tools/Client/ClusterClientDiscoverySettings.cs index d26ccad9cb4..94101487314 100644 --- a/src/contrib/cluster/Akka.Cluster.Tools/Client/ClusterClientDiscoverySettings.cs +++ b/src/contrib/cluster/Akka.Cluster.Tools/Client/ClusterClientDiscoverySettings.cs @@ -18,9 +18,10 @@ public sealed record ClusterClientDiscoverySettings( string ReceptionistName, string? PortName, TimeSpan DiscoveryRetryInterval, - TimeSpan DiscoveryTimeout) + TimeSpan DiscoveryTimeout, + int NumberOfContacts) { - public static readonly ClusterClientDiscoverySettings Empty = new ("", null, null, "receptionist", null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(60)); + public static readonly ClusterClientDiscoverySettings Empty = new ("", null, null, "receptionist", null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(60), 3); public static ClusterClientDiscoverySettings Create(Config clusterClientConfig) { @@ -35,7 +36,8 @@ public static ClusterClientDiscoverySettings Create(Config clusterClientConfig) config.GetString("receptionist-name", "receptionist"), config.GetString("port-name"), config.GetTimeSpan("discovery-retry-interval", TimeSpan.FromSeconds(1)), - config.GetTimeSpan("discovery-timeout", TimeSpan.FromSeconds(60)) + config.GetTimeSpan("discovery-timeout", TimeSpan.FromSeconds(60)), + config.GetInt("number-of-contacts", 3) ); } } \ No newline at end of file diff --git a/src/contrib/cluster/Akka.Cluster.Tools/Client/reference.conf b/src/contrib/cluster/Akka.Cluster.Tools/Client/reference.conf index 33af33d46db..794946042aa 100644 --- a/src/contrib/cluster/Akka.Cluster.Tools/Client/reference.conf +++ b/src/contrib/cluster/Akka.Cluster.Tools/Client/reference.conf @@ -102,6 +102,8 @@ akka.cluster.client { method = actor-system-name = null receptionist-name = receptionist + # The contact points will be trimmed down to this number of contact points to the client + number-of-contacts = 3 service-name = null port-name = null discovery-retry-interval = 1s diff --git a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveClusterTools.DotNet.verified.txt b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveClusterTools.DotNet.verified.txt index bfbf5aad29c..76bb0308b46 100644 --- a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveClusterTools.DotNet.verified.txt +++ b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveClusterTools.DotNet.verified.txt @@ -55,11 +55,12 @@ namespace Akka.Cluster.Tools.Client { [System.Runtime.CompilerServices.NullableAttribute(1)] public static readonly Akka.Cluster.Tools.Client.ClusterClientDiscoverySettings Empty; - public ClusterClientDiscoverySettings(string DiscoveryMethod, string ActorSystemName, string ServiceName, [System.Runtime.CompilerServices.NullableAttribute(1)] string ReceptionistName, string PortName, System.TimeSpan DiscoveryRetryInterval, System.TimeSpan DiscoveryTimeout) { } + public ClusterClientDiscoverySettings(string DiscoveryMethod, string ActorSystemName, string ServiceName, [System.Runtime.CompilerServices.NullableAttribute(1)] string ReceptionistName, string PortName, System.TimeSpan DiscoveryRetryInterval, System.TimeSpan DiscoveryTimeout, int NumberOfContacts) { } public string ActorSystemName { get; set; } public string DiscoveryMethod { get; set; } public System.TimeSpan DiscoveryRetryInterval { get; set; } public System.TimeSpan DiscoveryTimeout { get; set; } + public int NumberOfContacts { get; set; } public string PortName { get; set; } [System.Runtime.CompilerServices.NullableAttribute(1)] public string ReceptionistName { get; set; } diff --git a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveClusterTools.Net.verified.txt b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveClusterTools.Net.verified.txt index 284c0d053f8..a5c606776fe 100644 --- a/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveClusterTools.Net.verified.txt +++ b/src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveClusterTools.Net.verified.txt @@ -55,11 +55,12 @@ namespace Akka.Cluster.Tools.Client { [System.Runtime.CompilerServices.NullableAttribute(1)] public static readonly Akka.Cluster.Tools.Client.ClusterClientDiscoverySettings Empty; - public ClusterClientDiscoverySettings(string DiscoveryMethod, string ActorSystemName, string ServiceName, [System.Runtime.CompilerServices.NullableAttribute(1)] string ReceptionistName, string PortName, System.TimeSpan DiscoveryRetryInterval, System.TimeSpan DiscoveryTimeout) { } + public ClusterClientDiscoverySettings(string DiscoveryMethod, string ActorSystemName, string ServiceName, [System.Runtime.CompilerServices.NullableAttribute(1)] string ReceptionistName, string PortName, System.TimeSpan DiscoveryRetryInterval, System.TimeSpan DiscoveryTimeout, int NumberOfContacts) { } public string ActorSystemName { get; set; } public string DiscoveryMethod { get; set; } public System.TimeSpan DiscoveryRetryInterval { get; set; } public System.TimeSpan DiscoveryTimeout { get; set; } + public int NumberOfContacts { get; set; } public string PortName { get; set; } [System.Runtime.CompilerServices.NullableAttribute(1)] public string ReceptionistName { get; set; }