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; }