diff --git a/akka-cluster-typed/src/test/java/jdocs/akka/cluster/typed/SingletonCompileOnlyTest.java b/akka-cluster-typed/src/test/java/jdocs/akka/cluster/typed/SingletonCompileOnlyTest.java index 35a1af59e2..9aa0e258f1 100644 --- a/akka-cluster-typed/src/test/java/jdocs/akka/cluster/typed/SingletonCompileOnlyTest.java +++ b/akka-cluster-typed/src/test/java/jdocs/akka/cluster/typed/SingletonCompileOnlyTest.java @@ -4,16 +4,15 @@ package jdocs.akka.cluster.typed; -import akka.actor.typed.ActorRef; -import akka.actor.typed.ActorSystem; -import akka.actor.typed.Behavior; -import akka.actor.typed.Props; +import akka.actor.typed.*; import akka.actor.typed.javadsl.Behaviors; //#import import akka.cluster.typed.ClusterSingleton; import akka.cluster.typed.ClusterSingletonSettings; +import java.time.Duration; + //#import public class SingletonCompileOnlyTest { @@ -49,21 +48,41 @@ public class SingletonCompileOnlyTest { public static void example() { ActorSystem system = ActorSystem.create( - Behaviors.empty(), "SingletonExample" + Behaviors.empty(), "SingletonExample" ); //#singleton ClusterSingleton singleton = ClusterSingleton.get(system); // Start if needed and provide a proxy to a named singleton ActorRef proxy = singleton.spawn( - counter("TheCounter", 0), + counter("TheCounter", 0), + "GlobalCounter", + Props.empty(), + ClusterSingletonSettings.create(system), + new GoodByeCounter() + ); + + proxy.tell(new Increment()); + //#singleton + + } + + public static void backoff() { + + ActorSystem system = ActorSystem.create( + Behaviors.empty(), "SingletonExample" + ); + + //#backoff + ClusterSingleton singleton = ClusterSingleton.get(system); + ActorRef proxy = singleton.spawn( + Behaviors.supervise(counter("TheCounter", 0)) + .onFailure(SupervisorStrategy.restartWithBackoff(Duration.ofSeconds(1), Duration.ofSeconds(10), 0.2)), "GlobalCounter", Props.empty(), ClusterSingletonSettings.create(system), new GoodByeCounter() ); - - proxy.tell(new Increment()); - //#singleton + //#backoff } } diff --git a/akka-cluster-typed/src/test/scala/docs/akka/cluster/typed/SingletonCompileOnlySpec.scala b/akka-cluster-typed/src/test/scala/docs/akka/cluster/typed/SingletonCompileOnlySpec.scala index f70117ae42..dc83b7a53e 100644 --- a/akka-cluster-typed/src/test/scala/docs/akka/cluster/typed/SingletonCompileOnlySpec.scala +++ b/akka-cluster-typed/src/test/scala/docs/akka/cluster/typed/SingletonCompileOnlySpec.scala @@ -4,11 +4,9 @@ package docs.akka.cluster.typed -import akka.actor.typed.ActorRef -import akka.actor.typed.ActorSystem -import akka.actor.typed.Behavior -import akka.actor.typed.Props +import akka.actor.typed.{ ActorRef, ActorSystem, Behavior, Props, SupervisorStrategy } import akka.actor.typed.scaladsl.Behaviors +import scala.concurrent.duration._ object SingletonCompileOnlySpec { @@ -39,7 +37,8 @@ object SingletonCompileOnlySpec { val singletonManager = ClusterSingleton(system) // Start if needed and provide a proxy to a named singleton val proxy: ActorRef[CounterCommand] = singletonManager.spawn( - behavior = counter("TheCounter", 0), + behavior = Behaviors.supervise(counter("TheCounter", 0)) + .onFailure[Exception](SupervisorStrategy.restart), "GlobalCounter", Props.empty, ClusterSingletonSettings(system), @@ -49,4 +48,14 @@ object SingletonCompileOnlySpec { proxy ! Increment //#singleton + //#backoff + val proxyBackOff: ActorRef[CounterCommand] = singletonManager.spawn( + behavior = Behaviors.supervise(counter("TheCounter", 0)) + .onFailure[Exception](SupervisorStrategy.restartWithBackoff(1.second, 10.seconds, 0.2)), + "GlobalCounter", + Props.empty, + ClusterSingletonSettings(system), + terminationMessage = GoodByeCounter + ) + //#backoff } diff --git a/akka-docs/src/main/paradox/cluster-singleton.md b/akka-docs/src/main/paradox/cluster-singleton.md index 08a1a4f1ea..50189f0488 100644 --- a/akka-docs/src/main/paradox/cluster-singleton.md +++ b/akka-docs/src/main/paradox/cluster-singleton.md @@ -160,7 +160,15 @@ with different settings if needed. ## Supervision -Sometimes it is useful to add supervision for the Cluster Singleton itself. To accomplish this you need to add a parent supervisor actor which will be used to create the 'real' singleton instance. Below is an example implementation (credit to [this StackOverflow answer](https://stackoverflow.com/a/36716708/779513)) +There are two actors that could potentially be supervised. For the `consumer` singleton created above these would be: + +* Cluster singleton manager e.g. `/user/consumer` which runs on every node in the cluster +* The user actor e.g. `/user/consumer/singleton` which the manager starts on the oldest node + +The Cluster singleton manager actor should not have its supervision strategy changed as it should always be running. +However it is sometimes useful to add supervision for the user actor. +To accomplish this add a parent supervisor actor which will be used to create the 'real' singleton instance. +Below is an example implementation (credit to [this StackOverflow answer](https://stackoverflow.com/a/36716708/779513)) Scala : @@snip [ClusterSingletonSupervision.scala](/akka-docs/src/test/scala/docs/cluster/singleton/ClusterSingletonSupervision.scala) { #singleton-supervisor-actor } diff --git a/akka-docs/src/main/paradox/typed/cluster-singleton.md b/akka-docs/src/main/paradox/typed/cluster-singleton.md index 6877fc9af8..568114ec07 100644 --- a/akka-docs/src/main/paradox/typed/cluster-singleton.md +++ b/akka-docs/src/main/paradox/typed/cluster-singleton.md @@ -57,6 +57,23 @@ Scala Java : @@snip [SingletonCompileOnlyTest.java](/akka-cluster-typed/src/test/java/jdocs/akka/cluster/typed/SingletonCompileOnlyTest.java) { #import #singleton } +## Supervision + +The default @ref[supervision strategy](./fault-tolerance.md) when an exception is thrown is for an actor to be stopped. +The above example overrides this to `restart` to ensure it is always running. Another option would be to restart with +a backoff: + + +Scala +: @@snip [SingletonCompileOnlySpec.scala](/akka-cluster-typed/src/test/scala/docs/akka/cluster/typed/SingletonCompileOnlySpec.scala) { #backoff} + +Java +: @@snip [SingletonCompileOnlyTest.java](/akka-cluster-typed/src/test/java/jdocs/akka/cluster/typed/SingletonCompileOnlyTest.java) { #backoff} + +Be aware that this means there will be times when the singleton won't be running as restart is delayed. +See @ref[Fault Tolerance](./fault-tolerance.md) for a full list of supervision options. + + ## Accessing singleton of another data centre TODO