From 36b0ec6dc4d8c6ecdeb6ce60896f2aeb41197989 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Fri, 15 Nov 2024 22:21:47 +0700 Subject: [PATCH] Deprecate `ClusterSingleton.Init()` and add missing singleton feature to `ClusterSingletonSettings` (#7387) * Deprecate `ClusterSingleton.Init()` * Update API Approval list * Update akkadotnet-v1.5-upgrade-advisories.md * Update akkadotnet-v1.5-upgrade-advisories.md --------- Co-authored-by: Aaron Stannard --- .../akkadotnet-v1.5-upgrade-advisories.md | 39 +++++++++++++++ .../Singleton/ClusterSingleton.cs | 33 +++++++++++-- .../Singleton/ClusterSingletonManager.cs | 1 + .../Singleton/ClusterSingletonSettings.cs | 47 ++++++++++++++++--- ...ec.ApproveClusterTools.DotNet.verified.txt | 12 +++++ ...ISpec.ApproveClusterTools.Net.verified.txt | 12 +++++ 6 files changed, 133 insertions(+), 11 deletions(-) diff --git a/docs/community/whats-new/akkadotnet-v1.5-upgrade-advisories.md b/docs/community/whats-new/akkadotnet-v1.5-upgrade-advisories.md index 74ae5962952..9ffe9381118 100644 --- a/docs/community/whats-new/akkadotnet-v1.5-upgrade-advisories.md +++ b/docs/community/whats-new/akkadotnet-v1.5-upgrade-advisories.md @@ -11,6 +11,45 @@ This document contains specific upgrade suggestions, warnings, and notices that +## Upgrading to Akka.NET v1.5.32 + +### Future Breaking Change in `Akka.Cluster.Tools` + +The method `ClusterSingleton.Init()` will be removed in future v1.6, if you're using this method, you need to convert it to use `ClusterSingletonManager.Props` and `ClusterSingletonProxy.Props` instead. + +In order to preserve backward compatibility within your cluster, if you're using this convention: + +```csharp +var settings = ClusterSingletonSettings.Create(system); +var singletonActor = SingletonActor.Create(Counter.Props, "GlobalCounter") + .WithStopMessage(MyStopMessage.Instance) + .WithSettings(settings); +var proxy = singleton.Init(singletonActor); +``` + +You will need to convert it to: + +```csharp +var managerSettings = ClusterSingletonManagerSettings.Create(system) + .WithSingletonName("GlobalCounter"); +system.ActorOf( + props: ClusterSingletonManager.Props( + singletonProps: Counter.Props, + terminationMessage: MyStopMessage.Instance, + settings: managerSettings), + name: "singletonManagerGlobalCounter"); + +var proxySettings = ClusterSingletonProxySettings.Create(system) + .WithSingletonName("GlobalCounter"); +var proxy = system.ActorOf( + props: ClusterSingletonProxy.Props( + singletonManagerPath: "/user/singletonManagerGlobalCounter", + settings: proxySettings), + name: "singletonProxyGlobalCounter"); +``` + +Note that to preserve backward compatibility between cluster nodes, the singleton manager actor name **MUST** be in the `$"singletonManager{singletonName}"` format. + ## Upgrading to Akka.NET v1.5.31 Akka.NET v1.5.31 introduces a breaking behavior change to actor `Stash`. In previous behavior, `Stash` will filter out any messages that are identical (see explanation below) when it is prepended with another. It will not do so now, which is the actual intended behavior. diff --git a/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingleton.cs b/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingleton.cs index 92834d9ac63..979247f851b 100644 --- a/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingleton.cs +++ b/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingleton.cs @@ -9,6 +9,7 @@ using System.Collections.Concurrent; using Akka.Actor; using Akka.Annotations; +using Akka.Configuration; using Akka.Util; namespace Akka.Cluster.Tools.Singleton @@ -22,6 +23,17 @@ public class ClusterSingleton : IExtension { private readonly ActorSystem _system; private readonly Lazy _cluster; + + /// + /// Returns default HOCON configuration for the cluster singleton. + /// + public static Config DefaultConfig() + { + return ConfigurationFactory.FromResource( + "Akka.Cluster.Tools.Singleton.reference.conf"); + } + + // Cache for singleton proxies, remove in v1.6 private readonly ConcurrentDictionary _proxies = new(); public static ClusterSingleton Get(ActorSystem system) => @@ -30,6 +42,7 @@ public static ClusterSingleton Get(ActorSystem system) => public ClusterSingleton(ExtendedActorSystem system) { _system = system; + _system.Settings.InjectTopLevelFallback(DefaultConfig()); _cluster = new Lazy(() => Cluster.Get(system)); } @@ -40,6 +53,10 @@ public ClusterSingleton(ExtendedActorSystem system) /// If there already is a proxy running for the given `singletonName` on this node, an to that is returned. /// /// A proxy actor that can be used to communicate with the singleton in the cluster + [Obsolete("This convenience method is deprecated and will be removed in v1.6, " + + "please use ClusterSingletonManager.Props and ClusterSingletonProxy.Props directly instead. " + + "See https://getakka.net/community/whats-new/akkadotnet-v1.5-upgrade-advisories.html#upgrading-to-akkanet-v1532. " + + "Since 1.5.32.")] public IActorRef Init(SingletonActor singleton) { var settings = singleton.Settings.GetOrElse(ClusterSingletonSettings.Create(_system)); @@ -63,21 +80,23 @@ public IActorRef Init(SingletonActor singleton) return GetProxy(singleton.Name, settings); } + [Obsolete("Deprecated, remove in v1.6")] private IActorRef GetProxy(string name, ClusterSingletonSettings settings) { IActorRef ProxyCreator() { var proxyName = $"singletonProxy{name}"; - return _system.ActorOf(ClusterSingletonProxy.Props( - singletonManagerPath: $"/user/{ManagerNameFor(name)}", - settings: settings.ToProxySettings(name)), - proxyName); + return _system.ActorOf( + props: ClusterSingletonProxy.Props( + singletonManagerPath: $"/user/{ManagerNameFor(name)}", + settings: settings.ToProxySettings(name)), + name: proxyName); } return _proxies.GetOrAdd(name, _ => ProxyCreator()); } - + [Obsolete("Deprecated, remove in v1.6")] private string ManagerNameFor(string singletonName) => $"singletonManager{singletonName}"; } @@ -86,6 +105,10 @@ public class ClusterSingletonProvider : ExtensionIdProvider public override ClusterSingleton CreateExtension(ExtendedActorSystem system) => new(system); } + [Obsolete("This setting class is deprecated and will be removed in v1.6, " + + "please use ClusterSingletonManager.Props and ClusterSingletonProxy.Props directly instead. " + + "See https://getakka.net/community/whats-new/akkadotnet-v1.5-upgrade-advisories.html#upgrading-to-akkanet-v1532. " + + "Since 1.5.32.")] public class SingletonActor { public string Name { get; } diff --git a/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingletonManager.cs b/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingletonManager.cs index 7525928b3ae..132dd282de3 100644 --- a/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingletonManager.cs +++ b/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingletonManager.cs @@ -535,6 +535,7 @@ public sealed class ClusterSingletonManager : FSM /// TBD + [Obsolete("Deprecated and will be removed in v1.6, please use ClusterSingleton.DefaultConfig() instead. Since 1.5.32.")] public static Config DefaultConfig() { return ConfigurationFactory.FromResource( diff --git a/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingletonSettings.cs b/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingletonSettings.cs index 478745354d9..f62eded4e1b 100644 --- a/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingletonSettings.cs +++ b/src/contrib/cluster/Akka.Cluster.Tools/Singleton/ClusterSingletonSettings.cs @@ -18,6 +18,10 @@ namespace Akka.Cluster.Tools.Singleton /// The settings used for the /// [Serializable] + [Obsolete("This setting class is deprecated and will be removed in v1.6, " + + "please use ClusterSingletonManager.Props and ClusterSingletonProxy.Props directly instead. " + + "See https://getakka.net/community/whats-new/akkadotnet-v1.5-upgrade-advisories.html#upgrading-to-akkanet-v1532. " + + "Since 1.5.32.")] public class ClusterSingletonSettings : INoSerializationVerificationNeeded { /// @@ -64,6 +68,16 @@ public class ClusterSingletonSettings : INoSerializationVerificationNeeded /// public bool ConsiderAppVersion { get; } + /// + /// Should the singleton proxy publish a warning if no singleton actor were found after a period of time + /// + public bool LogSingletonIdentificationFailure { get; } + + /// + /// The period the proxy will wait until it logs a missing singleton warning, defaults to 1 minute + /// + public TimeSpan SingletonIdentificationFailurePeriod { get; } + /// /// Create settings from the default configuration `akka.cluster`. /// @@ -88,7 +102,9 @@ public static ClusterSingletonSettings Create(Config config) mgrSettings.HandOverRetryInterval, proxySettings.BufferSize, mgrSettings.LeaseSettings, - false); + false, + proxySettings.LogSingletonIdentificationFailure, + proxySettings.SingletonIdentificationFailurePeriod); } private ClusterSingletonSettings( @@ -98,7 +114,9 @@ private ClusterSingletonSettings( TimeSpan handOverRetryInterval, int bufferSize, LeaseUsageSettings leaseSettings, - bool considerAppVersion) + bool considerAppVersion, + bool logSingletonIdentificationFailure, + TimeSpan singletonIdentificationFailurePeriod) { if (singletonIdentificationInterval == TimeSpan.Zero) throw new ArgumentException("singletonIdentificationInterval must be positive", nameof(singletonIdentificationInterval)); @@ -119,16 +137,29 @@ private ClusterSingletonSettings( BufferSize = bufferSize; LeaseSettings = leaseSettings; ConsiderAppVersion = considerAppVersion; + LogSingletonIdentificationFailure = logSingletonIdentificationFailure; + SingletonIdentificationFailurePeriod = singletonIdentificationFailurePeriod; } public ClusterSingletonSettings WithRole(string role) => Copy(role: role); + public ClusterSingletonSettings WithSingletonIdentificationInterval(TimeSpan singletonIdentificationInterval) + => Copy(singletonIdentificationInterval: singletonIdentificationInterval); + public ClusterSingletonSettings WithRemovalMargin(TimeSpan removalMargin) => Copy(removalMargin: removalMargin); public ClusterSingletonSettings WithHandOverRetryInterval(TimeSpan handOverRetryInterval) => Copy(handOverRetryInterval: handOverRetryInterval); + + public ClusterSingletonSettings WithBufferSize(int bufferSize) => Copy(bufferSize: bufferSize); public ClusterSingletonSettings WithLeaseSettings(LeaseUsageSettings leaseSettings) => Copy(leaseSettings: leaseSettings); + public ClusterSingletonSettings WithLogSingletonIdentificationFailure(bool logSingletonIdentificationFailure) + => Copy(logSingletonIdentificationFailure: logSingletonIdentificationFailure); + + public ClusterSingletonSettings WithSingletonIdentificationFailurePeriod(TimeSpan singletonIdentificationFailurePeriod) + => Copy(singletonIdentificationFailurePeriod: singletonIdentificationFailurePeriod); + private ClusterSingletonSettings Copy( Option role = default, TimeSpan? singletonIdentificationInterval = null, @@ -136,7 +167,9 @@ private ClusterSingletonSettings Copy( TimeSpan? handOverRetryInterval = null, int? bufferSize = null, Option leaseSettings = default, - bool? considerAppVersion = null) + bool? considerAppVersion = null, + bool? logSingletonIdentificationFailure = null, + TimeSpan? singletonIdentificationFailurePeriod = null) { return new ClusterSingletonSettings( role: role.HasValue ? role.Value : Role, @@ -145,16 +178,18 @@ private ClusterSingletonSettings Copy( handOverRetryInterval: handOverRetryInterval ?? HandOverRetryInterval, bufferSize: bufferSize ?? BufferSize, leaseSettings: leaseSettings.HasValue ? leaseSettings.Value : LeaseSettings, - considerAppVersion: considerAppVersion ?? ConsiderAppVersion); + considerAppVersion: considerAppVersion ?? ConsiderAppVersion, + logSingletonIdentificationFailure: logSingletonIdentificationFailure ?? LogSingletonIdentificationFailure, + singletonIdentificationFailurePeriod: singletonIdentificationFailurePeriod ?? SingletonIdentificationFailurePeriod); } [InternalApi] internal ClusterSingletonManagerSettings ToManagerSettings(string singletonName) => - new(singletonName, Role, RemovalMargin, HandOverRetryInterval, LeaseSettings, ConsiderAppVersion); + new(singletonName, Role, RemovalMargin, HandOverRetryInterval, LeaseSettings, false); [InternalApi] internal ClusterSingletonProxySettings ToProxySettings(string singletonName) => - new(singletonName, Role, SingletonIdentificationInterval, BufferSize, ConsiderAppVersion, true, TimeSpan.FromSeconds(30)); + new(singletonName, Role, SingletonIdentificationInterval, BufferSize, false, LogSingletonIdentificationFailure, SingletonIdentificationFailurePeriod); [InternalApi] internal bool ShouldRunManager(Cluster cluster) => string.IsNullOrEmpty(Role) || cluster.SelfMember.Roles.Contains(Role); 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 02c86f21446..e4397fdab81 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 @@ -392,7 +392,9 @@ namespace Akka.Cluster.Tools.Singleton public class ClusterSingleton : Akka.Actor.IExtension { public ClusterSingleton(Akka.Actor.ExtendedActorSystem system) { } + public static Akka.Configuration.Config DefaultConfig() { } public static Akka.Cluster.Tools.Singleton.ClusterSingleton Get(Akka.Actor.ActorSystem system) { } + [System.ObsoleteAttribute(@"This convenience method is deprecated and will be removed in v1.6, please use ClusterSingletonManager.Props and ClusterSingletonProxy.Props directly instead. See https://getakka.net/community/whats-new/akkadotnet-v1.5-upgrade-advisories.html#upgrading-to-akkanet-v1532. Since 1.5.32.")] public Akka.Actor.IActorRef Init(Akka.Cluster.Tools.Singleton.SingletonActor singleton) { } } [System.Runtime.CompilerServices.NullableAttribute(new byte[] { @@ -401,6 +403,8 @@ namespace Akka.Cluster.Tools.Singleton public sealed class ClusterSingletonManager : Akka.Actor.FSM { public ClusterSingletonManager(Akka.Actor.Props singletonProps, object terminationMessage, Akka.Cluster.Tools.Singleton.ClusterSingletonManagerSettings settings) { } + [System.ObsoleteAttribute("Deprecated and will be removed in v1.6, please use ClusterSingleton.DefaultConfig" + + "() instead. Since 1.5.32.")] public static Akka.Configuration.Config DefaultConfig() { } protected override void PostStop() { } protected override void PreStart() { } @@ -482,22 +486,29 @@ namespace Akka.Cluster.Tools.Singleton public Akka.Cluster.Tools.Singleton.ClusterSingletonProxySettings WithSingletonIdentificationInterval(System.TimeSpan singletonIdentificationInterval) { } public Akka.Cluster.Tools.Singleton.ClusterSingletonProxySettings WithSingletonName(string singletonName) { } } + [System.ObsoleteAttribute(@"This setting class is deprecated and will be removed in v1.6, please use ClusterSingletonManager.Props and ClusterSingletonProxy.Props directly instead. See https://getakka.net/community/whats-new/akkadotnet-v1.5-upgrade-advisories.html#upgrading-to-akkanet-v1532. Since 1.5.32.")] public class ClusterSingletonSettings : Akka.Actor.INoSerializationVerificationNeeded { public int BufferSize { get; } public bool ConsiderAppVersion { get; } public System.TimeSpan HandOverRetryInterval { get; } public Akka.Coordination.LeaseUsageSettings LeaseSettings { get; } + public bool LogSingletonIdentificationFailure { get; } public System.TimeSpan RemovalMargin { get; } public string Role { get; } + public System.TimeSpan SingletonIdentificationFailurePeriod { get; } public System.TimeSpan SingletonIdentificationInterval { get; } public static Akka.Cluster.Tools.Singleton.ClusterSingletonSettings Create(Akka.Actor.ActorSystem system) { } public static Akka.Cluster.Tools.Singleton.ClusterSingletonSettings Create(Akka.Configuration.Config config) { } public override string ToString() { } + public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithBufferSize(int bufferSize) { } public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithHandOverRetryInterval(System.TimeSpan handOverRetryInterval) { } public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithLeaseSettings(Akka.Coordination.LeaseUsageSettings leaseSettings) { } + public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithLogSingletonIdentificationFailure(bool logSingletonIdentificationFailure) { } public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithRemovalMargin(System.TimeSpan removalMargin) { } public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithRole(string role) { } + public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithSingletonIdentificationFailurePeriod(System.TimeSpan singletonIdentificationFailurePeriod) { } + public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithSingletonIdentificationInterval(System.TimeSpan singletonIdentificationInterval) { } } public enum ClusterSingletonState { @@ -514,6 +525,7 @@ namespace Akka.Cluster.Tools.Singleton } public interface IClusterSingletonData { } public interface IClusterSingletonMessage { } + [System.ObsoleteAttribute(@"This setting class is deprecated and will be removed in v1.6, please use ClusterSingletonManager.Props and ClusterSingletonProxy.Props directly instead. See https://getakka.net/community/whats-new/akkadotnet-v1.5-upgrade-advisories.html#upgrading-to-akkanet-v1532. Since 1.5.32.")] public class SingletonActor { public string Name { get; } 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 cdca3a7373e..ad19deb0f48 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 @@ -392,7 +392,9 @@ namespace Akka.Cluster.Tools.Singleton public class ClusterSingleton : Akka.Actor.IExtension { public ClusterSingleton(Akka.Actor.ExtendedActorSystem system) { } + public static Akka.Configuration.Config DefaultConfig() { } public static Akka.Cluster.Tools.Singleton.ClusterSingleton Get(Akka.Actor.ActorSystem system) { } + [System.ObsoleteAttribute(@"This convenience method is deprecated and will be removed in v1.6, please use ClusterSingletonManager.Props and ClusterSingletonProxy.Props directly instead. See https://getakka.net/community/whats-new/akkadotnet-v1.5-upgrade-advisories.html#upgrading-to-akkanet-v1532. Since 1.5.32.")] public Akka.Actor.IActorRef Init(Akka.Cluster.Tools.Singleton.SingletonActor singleton) { } } [System.Runtime.CompilerServices.NullableAttribute(new byte[] { @@ -401,6 +403,8 @@ namespace Akka.Cluster.Tools.Singleton public sealed class ClusterSingletonManager : Akka.Actor.FSM { public ClusterSingletonManager(Akka.Actor.Props singletonProps, object terminationMessage, Akka.Cluster.Tools.Singleton.ClusterSingletonManagerSettings settings) { } + [System.ObsoleteAttribute("Deprecated and will be removed in v1.6, please use ClusterSingleton.DefaultConfig" + + "() instead. Since 1.5.32.")] public static Akka.Configuration.Config DefaultConfig() { } protected override void PostStop() { } protected override void PreStart() { } @@ -482,22 +486,29 @@ namespace Akka.Cluster.Tools.Singleton public Akka.Cluster.Tools.Singleton.ClusterSingletonProxySettings WithSingletonIdentificationInterval(System.TimeSpan singletonIdentificationInterval) { } public Akka.Cluster.Tools.Singleton.ClusterSingletonProxySettings WithSingletonName(string singletonName) { } } + [System.ObsoleteAttribute(@"This setting class is deprecated and will be removed in v1.6, please use ClusterSingletonManager.Props and ClusterSingletonProxy.Props directly instead. See https://getakka.net/community/whats-new/akkadotnet-v1.5-upgrade-advisories.html#upgrading-to-akkanet-v1532. Since 1.5.32.")] public class ClusterSingletonSettings : Akka.Actor.INoSerializationVerificationNeeded { public int BufferSize { get; } public bool ConsiderAppVersion { get; } public System.TimeSpan HandOverRetryInterval { get; } public Akka.Coordination.LeaseUsageSettings LeaseSettings { get; } + public bool LogSingletonIdentificationFailure { get; } public System.TimeSpan RemovalMargin { get; } public string Role { get; } + public System.TimeSpan SingletonIdentificationFailurePeriod { get; } public System.TimeSpan SingletonIdentificationInterval { get; } public static Akka.Cluster.Tools.Singleton.ClusterSingletonSettings Create(Akka.Actor.ActorSystem system) { } public static Akka.Cluster.Tools.Singleton.ClusterSingletonSettings Create(Akka.Configuration.Config config) { } public override string ToString() { } + public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithBufferSize(int bufferSize) { } public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithHandOverRetryInterval(System.TimeSpan handOverRetryInterval) { } public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithLeaseSettings(Akka.Coordination.LeaseUsageSettings leaseSettings) { } + public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithLogSingletonIdentificationFailure(bool logSingletonIdentificationFailure) { } public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithRemovalMargin(System.TimeSpan removalMargin) { } public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithRole(string role) { } + public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithSingletonIdentificationFailurePeriod(System.TimeSpan singletonIdentificationFailurePeriod) { } + public Akka.Cluster.Tools.Singleton.ClusterSingletonSettings WithSingletonIdentificationInterval(System.TimeSpan singletonIdentificationInterval) { } } public enum ClusterSingletonState { @@ -514,6 +525,7 @@ namespace Akka.Cluster.Tools.Singleton } public interface IClusterSingletonData { } public interface IClusterSingletonMessage { } + [System.ObsoleteAttribute(@"This setting class is deprecated and will be removed in v1.6, please use ClusterSingletonManager.Props and ClusterSingletonProxy.Props directly instead. See https://getakka.net/community/whats-new/akkadotnet-v1.5-upgrade-advisories.html#upgrading-to-akkanet-v1532. Since 1.5.32.")] public class SingletonActor { public string Name { get; }