From d0a5ced3196dfacec2e2d62f6d25a7471d1a9af0 Mon Sep 17 00:00:00 2001 From: Nicolas Vollmar Date: Wed, 19 Dec 2018 08:08:48 +0100 Subject: [PATCH 01/11] Simplify backoff supervision api #26156 --- .../mima-filters/2.5.22.backwards.excludes | 9 + .../pattern/BackoffOnRestartSupervisor.scala | 37 +-- .../scala/akka/pattern/BackoffOptions.scala | 67 ++--- .../akka/pattern/BackoffSupervisor.scala | 228 +++++++----------- .../scala/akka/pattern/HandleBackoff.scala | 64 +++++ 5 files changed, 223 insertions(+), 182 deletions(-) create mode 100644 akka-actor/src/main/mima-filters/2.5.22.backwards.excludes create mode 100644 akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala diff --git a/akka-actor/src/main/mima-filters/2.5.22.backwards.excludes b/akka-actor/src/main/mima-filters/2.5.22.backwards.excludes new file mode 100644 index 0000000000..6cf3f129cd --- /dev/null +++ b/akka-actor/src/main/mima-filters/2.5.22.backwards.excludes @@ -0,0 +1,9 @@ +# Simplify backoff supervision API #19016 +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestartSupervisor.replyWhileStopped") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestartSupervisor.finalStopMessage") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestartSupervisor.this") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.HandleBackoff.replyWhileStopped") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.HandleBackoff.finalStopMessage") +ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.pattern.HandleBackoff.handleMessageToChild") +ProblemFilters.exclude[IncompatibleResultTypeProblem]("akka.pattern.Backoff.onFailure") +ProblemFilters.exclude[IncompatibleResultTypeProblem]("akka.pattern.Backoff.onStop") \ No newline at end of file diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala index 985a5f6351..09d93cbc65 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala @@ -4,11 +4,10 @@ package akka.pattern -import scala.concurrent.duration._ - -import akka.actor._ -import akka.actor.OneForOneStrategy import akka.actor.SupervisorStrategy._ +import akka.actor.{ OneForOneStrategy, _ } + +import scala.concurrent.duration._ /** * Back-off supervisor that stops and starts a child actor when the child actor restarts. @@ -16,20 +15,19 @@ import akka.actor.SupervisorStrategy._ * with ``akka.pattern.Backoff.onFailure``. */ private class BackoffOnRestartSupervisor( - val childProps: Props, - val childName: String, - minBackoff: FiniteDuration, - maxBackoff: FiniteDuration, - val reset: BackoffReset, - randomFactor: Double, - strategy: OneForOneStrategy, - val replyWhileStopped: Option[Any], - val finalStopMessage: Option[Any ⇒ Boolean]) + val childProps: Props, + val childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + val reset: BackoffReset, + randomFactor: Double, + strategy: OneForOneStrategy, + replyWhileStopped: Option[Any]) extends Actor with HandleBackoff with ActorLogging { - import context._ import BackoffSupervisor._ + import context._ override val supervisorStrategy = OneForOneStrategy(strategy.maxNrOfRetries, strategy.withinTimeRange, strategy.loggingEnabled) { case ex ⇒ @@ -81,6 +79,15 @@ private class BackoffOnRestartSupervisor( stop(self) } - def receive = onTerminated orElse handleBackoff + def receive: Receive = onTerminated orElse handleBackoff + protected def handleMessageToChild(msg: Any): Unit = child match { + case Some(c) ⇒ + c.forward(msg) + case None ⇒ + replyWhileStopped match { + case None ⇒ context.system.deadLetters.forward(msg) + case Some(r) ⇒ sender() ! r + } + } } diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala index de6157a0be..e8192ad4ad 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala @@ -83,7 +83,7 @@ object Backoff { minBackoff: FiniteDuration, maxBackoff: FiniteDuration, randomFactor: Double, - maxNrOfRetries: Int): BackoffOptions = + maxNrOfRetries: Int): BackoffOnFailureOptions = BackoffOptionsImpl(RestartImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor).withMaxNrOfRetries(maxNrOfRetries) /** @@ -137,7 +137,7 @@ object Backoff { childName: String, minBackoff: FiniteDuration, maxBackoff: FiniteDuration, - randomFactor: Double): BackoffOptions = + randomFactor: Double): BackoffOnFailureOptions = BackoffOptionsImpl(RestartImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor) /** @@ -195,7 +195,7 @@ object Backoff { minBackoff: java.time.Duration, maxBackoff: java.time.Duration, randomFactor: Double, - maxNrOfRetries: Int): BackoffOptions = + maxNrOfRetries: Int): BackoffOnFailureOptions = onFailure(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, maxNrOfRetries) /** @@ -250,7 +250,7 @@ object Backoff { childName: String, minBackoff: java.time.Duration, maxBackoff: java.time.Duration, - randomFactor: Double): BackoffOptions = + randomFactor: Double): BackoffOnFailureOptions = onFailure(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, -1) /** @@ -315,7 +315,7 @@ object Backoff { minBackoff: FiniteDuration, maxBackoff: FiniteDuration, randomFactor: Double, - maxNrOfRetries: Int): BackoffOptions = + maxNrOfRetries: Int): BackoffOnStopOptions = BackoffOptionsImpl(StopImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor).withMaxNrOfRetries(maxNrOfRetries) /** @@ -376,7 +376,7 @@ object Backoff { childName: String, minBackoff: FiniteDuration, maxBackoff: FiniteDuration, - randomFactor: Double): BackoffOptions = + randomFactor: Double): BackoffOnStopOptions = BackoffOptionsImpl(StopImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor) /** @@ -441,7 +441,7 @@ object Backoff { minBackoff: java.time.Duration, maxBackoff: java.time.Duration, randomFactor: Double, - maxNrOfRetries: Int): BackoffOptions = + maxNrOfRetries: Int): BackoffOnStopOptions = onStop(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, maxNrOfRetries) /** @@ -503,7 +503,7 @@ object Backoff { childName: String, minBackoff: java.time.Duration, maxBackoff: java.time.Duration, - randomFactor: Double): BackoffOptions = + randomFactor: Double): BackoffOnStopOptions = onStop(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, -1) } @@ -548,23 +548,6 @@ trait BackoffOptions { */ def withDefaultStoppingStrategy: BackoffOptions - /** - * Returns a new BackoffOptions with a constant reply to messages that the supervisor receives while its - * child is stopped. By default, a message received while the child is stopped is forwarded to `deadLetters`. - * With this option, the supervisor will reply to the sender instead. - * @param replyWhileStopped The message that the supervisor will send in response to all messages while - * its child is stopped. - */ - def withReplyWhileStopped(replyWhileStopped: Any): BackoffOptions - - /** - * Predicate evaluated for each message, if it returns true and the supervised actor is - * stopped then the supervisor will stop its self. If it returns true while - * the supervised actor is running then it will be forwarded to the supervised actor and - * when the supervised actor stops its self the supervisor will stop its self. - */ - def withFinalStopMessage(isFinalStopMessage: Any ⇒ Boolean): BackoffOptions - /** * Returns a new BackoffOptions with a maximum number of retries to restart the child actor. * By default, the supervisor will retry infinitely. @@ -574,12 +557,42 @@ trait BackoffOptions { */ def withMaxNrOfRetries(maxNrOfRetries: Int): BackoffOptions + /** + * Returns a new BackoffOptions with a constant reply to messages that the supervisor receives while its + * child is stopped. By default, a message received while the child is stopped is forwarded to `deadLetters`. + * With this option, the supervisor will reply to the sender instead. + * @param replyWhileStopped The message that the supervisor will send in response to all messages while + * its child is stopped. + */ + def withReplyWhileStopped(replyWhileStopped: Any): BackoffOptions + + // for backwards compability with 2.5.19 + @deprecated("Use through BackoffOnStopOptions instead of BackoffOptions", since = "2.5.20") + def withFinalStopMessage(isFinalStopMessage: Any ⇒ Boolean): BackoffOptions + /** * Returns the props to create the back-off supervisor. */ private[akka] def props: Props } +sealed trait BackoffOnStopOptions extends BackoffOptions { + + /** + * Predicate evaluated for each message, if it returns true and the supervised actor is + * stopped then the supervisor will stop its self. If it returns true while + * the supervised actor is running then it will be forwarded to the supervised actor and + * when the supervised actor stops its self the supervisor will stop its self. + */ + def withFinalStopMessage(isFinalStopMessage: Any ⇒ Boolean): BackoffOptions +} + +sealed trait BackoffOnFailureOptions extends BackoffOptions { + // for backwards compability with 2.5.19 + @deprecated("This has no effect for backoff on failure", since = "2.5.20") + def withFinalStopMessage(isFinalStopMessage: Any ⇒ Boolean): BackoffOptions +} + private final case class BackoffOptionsImpl( backoffType: BackoffType = RestartImpliesFailure, childProps: Props, @@ -591,7 +604,7 @@ private final case class BackoffOptionsImpl( supervisorStrategy: OneForOneStrategy = OneForOneStrategy()(SupervisorStrategy.defaultStrategy.decider), replyWhileStopped: Option[Any] = None, finalStopMessage: Option[Any ⇒ Boolean] = None -) extends BackoffOptions { +) extends BackoffOptions with BackoffOnStopOptions with BackoffOnFailureOptions { val backoffReset = reset.getOrElse(AutoReset(minBackoff)) @@ -616,7 +629,7 @@ private final case class BackoffOptionsImpl( backoffType match { //onFailure method in companion object case RestartImpliesFailure ⇒ - Props(new BackoffOnRestartSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped, finalStopMessage)) + Props(new BackoffOnRestartSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped)) //onStop method in companion object case StopImpliesFailure ⇒ Props(new BackoffSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped, finalStopMessage)) diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala index c131b7f39d..37e020f23d 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala @@ -4,11 +4,11 @@ package akka.pattern -import java.util.concurrent.ThreadLocalRandom import java.util.Optional +import java.util.concurrent.ThreadLocalRandom -import akka.actor.{ Actor, ActorLogging, ActorRef, DeadLetterSuppression, OneForOneStrategy, Props, SupervisorStrategy, Terminated } import akka.actor.SupervisorStrategy.{ Directive, Escalate } +import akka.actor.{ Actor, ActorLogging, ActorRef, DeadLetterSuppression, OneForOneStrategy, Props, SupervisorStrategy, Terminated } import akka.util.JavaDurationConverters._ import scala.concurrent.duration.{ Duration, FiniteDuration } @@ -23,15 +23,15 @@ object BackoffSupervisor { * most exceptions will immediately restart the child. You can define another * supervision strategy by using [[#propsWithSupervisorStrategy]]. * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. */ def props( childProps: Props, @@ -49,18 +49,18 @@ object BackoffSupervisor { * most exceptions will immediately restart the child. You can define another * supervision strategy by using [[#propsWithSupervisorStrategy]]. * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration - * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. * @param maxNrOfRetries maximum number of attempts to restart the child actor. - * The supervisor will terminate itself after the maxNoOfRetries is reached. - * In order to restart infinitely pass in `-1`. + * The supervisor will terminate itself after the maxNoOfRetries is reached. + * In order to restart infinitely pass in `-1`. */ def props( childProps: Props, @@ -83,15 +83,15 @@ object BackoffSupervisor { * most exceptions will immediately restart the child. You can define another * supervision strategy by using [[#propsWithSupervisorStrategy]]. * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. */ def props( childProps: Props, @@ -109,18 +109,18 @@ object BackoffSupervisor { * most exceptions will immediately restart the child. You can define another * supervision strategy by using [[#propsWithSupervisorStrategy]]. * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration - * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. * @param maxNrOfRetries maximum number of attempts to restart the child actor. - * The supervisor will terminate itself after the maxNoOfRetries is reached. - * In order to restart infinitely pass in `-1`. + * The supervisor will terminate itself after the maxNoOfRetries is reached. + * In order to restart infinitely pass in `-1`. */ def props( childProps: Props, @@ -140,18 +140,18 @@ object BackoffSupervisor { * `Restart` will perform a normal immediate restart of the child. A `Stop` will * stop the child, but it will be started again after the back-off duration. * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. - * @param strategy the supervision strategy to use for handling exceptions - * in the child. As the BackoffSupervisor creates a separate actor to handle the - * backoff process, only a [[OneForOneStrategy]] makes sense here. + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + * @param strategy the supervision strategy to use for handling exceptions + * in the child. As the BackoffSupervisor creates a separate actor to handle the + * backoff process, only a [[OneForOneStrategy]] makes sense here. */ def propsWithSupervisorStrategy( childProps: Props, @@ -174,18 +174,18 @@ object BackoffSupervisor { * `Restart` will perform a normal immediate restart of the child. A `Stop` will * stop the child, but it will be started again after the back-off duration. * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. - * @param strategy the supervision strategy to use for handling exceptions - * in the child. As the BackoffSupervisor creates a separate actor to handle the - * backoff process, only a [[OneForOneStrategy]] makes sense here. + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + * @param strategy the supervision strategy to use for handling exceptions + * in the child. As the BackoffSupervisor creates a separate actor to handle the + * backoff process, only a [[OneForOneStrategy]] makes sense here. */ def propsWithSupervisorStrategy( childProps: Props, @@ -284,15 +284,17 @@ object BackoffSupervisor { * with `Backoff.onStop`. */ final class BackoffSupervisor( - val childProps: Props, - val childName: String, - minBackoff: FiniteDuration, - maxBackoff: FiniteDuration, - val reset: BackoffReset, - randomFactor: Double, - strategy: SupervisorStrategy, + val childProps: Props, + val childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + val reset: BackoffReset, + randomFactor: Double, + strategy: SupervisorStrategy, + @deprecated("Should be internal", since = "2.5.19") // since removed from HandleBackoff the val can be removed val replyWhileStopped: Option[Any], - val finalStopMessage: Option[Any ⇒ Boolean]) + @deprecated("Should be internal", since = "2.5.19") // since removed from HandleBackoff the val can be removed + val finalStopMessage: Option[Any ⇒ Boolean]) extends Actor with HandleBackoff with ActorLogging { @@ -367,79 +369,25 @@ final class BackoffSupervisor( } - def receive = onTerminated orElse handleBackoff -} + def receive: Receive = onTerminated orElse handleBackoff -private[akka] trait HandleBackoff { this: Actor ⇒ - def childProps: Props - def childName: String - def reset: BackoffReset - def replyWhileStopped: Option[Any] - def finalStopMessage: Option[Any ⇒ Boolean] - - var child: Option[ActorRef] = None - var restartCount = 0 - var finalStopMessageReceived = false - - import BackoffSupervisor._ - import context.dispatcher - - override def preStart(): Unit = startChild() - - def startChild(): Unit = { - if (child.isEmpty) { - child = Some(context.watch(context.actorOf(childProps, childName))) - } - } - - def handleBackoff: Receive = { - case StartChild ⇒ - startChild() - reset match { - case AutoReset(resetBackoff) ⇒ - val _ = context.system.scheduler.scheduleOnce(resetBackoff, self, ResetRestartCount(restartCount)) - case _ ⇒ // ignore + protected def handleMessageToChild(msg: Any): Unit = child match { + case Some(c) ⇒ + c.forward(msg) + if (!finalStopMessageReceived && finalStopMessage.isDefined) { + finalStopMessageReceived = finalStopMessage.get.apply(msg) } - - case Reset ⇒ - reset match { - case ManualReset ⇒ restartCount = 0 - case msg ⇒ unhandled(msg) + case None ⇒ + replyWhileStopped match { + case None ⇒ context.system.deadLetters.forward(msg) + case Some(r) ⇒ sender() ! r } - - case ResetRestartCount(current) ⇒ - if (current == restartCount) { - restartCount = 0 + finalStopMessage match { + case None ⇒ + case Some(fsm) ⇒ + if (fsm(msg)) { + context.stop(self) + } } - - case GetRestartCount ⇒ - sender() ! RestartCount(restartCount) - - case GetCurrentChild ⇒ - sender() ! CurrentChild(child) - - case msg if child.contains(sender()) ⇒ - // use the BackoffSupervisor as sender - context.parent ! msg - - case msg ⇒ child match { - case Some(c) ⇒ - c.forward(msg) - if (!finalStopMessageReceived && finalStopMessage.isDefined) { - finalStopMessageReceived = finalStopMessage.get.apply(msg) - } - case None ⇒ - replyWhileStopped match { - case None ⇒ context.system.deadLetters.forward(msg) - case Some(r) ⇒ sender() ! r - } - finalStopMessage match { - case None ⇒ - case Some(fsm) ⇒ - if (fsm(msg)) { - context.stop(self) - } - } - } } } diff --git a/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala b/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala new file mode 100644 index 0000000000..7920967656 --- /dev/null +++ b/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2018-2019 Lightbend Inc. + */ + +package akka.pattern + +import akka.actor.{ Actor, ActorRef, Props } + +private[akka] trait HandleBackoff { + this: Actor ⇒ + def childProps: Props + def childName: String + def reset: BackoffReset + protected def handleMessageToChild(m: Any): Unit + + var child: Option[ActorRef] = None + var restartCount = 0 + var finalStopMessageReceived = false + + import BackoffSupervisor._ + import context.dispatcher + + override def preStart(): Unit = startChild() + + def startChild(): Unit = { + if (child.isEmpty) { + child = Some(context.watch(context.actorOf(childProps, childName))) + } + } + + def handleBackoff: Receive = { + case StartChild ⇒ + startChild() + reset match { + case AutoReset(resetBackoff) ⇒ + val _ = context.system.scheduler.scheduleOnce(resetBackoff, self, ResetRestartCount(restartCount)) + case _ ⇒ // ignore + } + + case Reset ⇒ + reset match { + case ManualReset ⇒ restartCount = 0 + case msg ⇒ unhandled(msg) + } + + case ResetRestartCount(current) ⇒ + if (current == restartCount) { + restartCount = 0 + } + + case GetRestartCount ⇒ + sender() ! RestartCount(restartCount) + + case GetCurrentChild ⇒ + sender() ! CurrentChild(child) + + case msg if child.contains(sender()) ⇒ + // use the BackoffSupervisor as sender + context.parent ! msg + + case msg ⇒ + handleMessageToChild(msg) + } +} From 551bbdec7766081e4e33a4cdb1d794eaa8e081d0 Mon Sep 17 00:00:00 2001 From: Nicolas Vollmar Date: Thu, 20 Dec 2018 07:26:38 +0100 Subject: [PATCH 02/11] Hide backoff on stop supervisor implementation #26156 --- .../pattern/BackoffOnStopSupervisor.scala | 92 +++++++++++++++++ .../scala/akka/pattern/BackoffOptions.scala | 2 +- .../akka/pattern/BackoffSupervisor.scala | 98 +++---------------- 3 files changed, 107 insertions(+), 85 deletions(-) create mode 100644 akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala new file mode 100644 index 0000000000..a2526fb8d4 --- /dev/null +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2018-2019 Lightbend Inc. + */ + +package akka.pattern + +import akka.actor.SupervisorStrategy.{ Directive, Escalate } +import akka.actor.{ Actor, ActorLogging, OneForOneStrategy, Props, SupervisorStrategy, Terminated } + +import scala.concurrent.duration.FiniteDuration + +/** + * Back-off supervisor that stops and starts a child actor using a back-off algorithm when the child actor stops. + * This back-off supervisor is created by using `akka.pattern.BackoffSupervisor.props` + * with `Backoff.onStop`. + */ +private[akka] class BackoffOnStopSupervisor( + val childProps: Props, + val childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + val reset: BackoffReset, + randomFactor: Double, + strategy: SupervisorStrategy, + replyWhileStopped: Option[Any], + finalStopMessage: Option[Any ⇒ Boolean]) + extends Actor with HandleBackoff + with ActorLogging { + + import BackoffSupervisor._ + import context.dispatcher + + // to keep binary compatibility with 2.4.1 + override val supervisorStrategy = strategy match { + case oneForOne: OneForOneStrategy ⇒ + OneForOneStrategy(oneForOne.maxNrOfRetries, oneForOne.withinTimeRange, oneForOne.loggingEnabled) { + case ex ⇒ + val defaultDirective: Directive = + super.supervisorStrategy.decider.applyOrElse(ex, (_: Any) ⇒ Escalate) + + strategy.decider.applyOrElse(ex, (_: Any) ⇒ defaultDirective) + } + case s ⇒ s + } + + def onTerminated: Receive = { + case Terminated(ref) if child.contains(ref) ⇒ + child = None + if (finalStopMessageReceived) { + context.stop(self) + } else { + val maxNrOfRetries = strategy match { + case oneForOne: OneForOneStrategy ⇒ oneForOne.maxNrOfRetries + case _ ⇒ -1 + } + + val nextRestartCount = restartCount + 1 + + if (maxNrOfRetries == -1 || nextRestartCount <= maxNrOfRetries) { + val restartDelay = calculateDelay(restartCount, minBackoff, maxBackoff, randomFactor) + context.system.scheduler.scheduleOnce(restartDelay, self, StartChild) + restartCount = nextRestartCount + } else { + log.debug(s"Terminating on restart #{} which exceeds max allowed restarts ({})", nextRestartCount, maxNrOfRetries) + context.stop(self) + } + } + + } + + def receive: Receive = onTerminated orElse handleBackoff + + protected def handleMessageToChild(msg: Any): Unit = child match { + case Some(c) ⇒ + c.forward(msg) + if (!finalStopMessageReceived && finalStopMessage.isDefined) { + finalStopMessageReceived = finalStopMessage.get.apply(msg) + } + case None ⇒ + replyWhileStopped match { + case None ⇒ context.system.deadLetters.forward(msg) + case Some(r) ⇒ sender() ! r + } + finalStopMessage match { + case None ⇒ + case Some(fsm) ⇒ + if (fsm(msg)) { + context.stop(self) + } + } + } +} diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala index e8192ad4ad..a629b4eaa7 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala @@ -632,7 +632,7 @@ private final case class BackoffOptionsImpl( Props(new BackoffOnRestartSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped)) //onStop method in companion object case StopImpliesFailure ⇒ - Props(new BackoffSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped, finalStopMessage)) + Props(new BackoffOnStopSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped, finalStopMessage)) } } } diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala index 37e020f23d..b88f012f39 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala @@ -7,8 +7,7 @@ package akka.pattern import java.util.Optional import java.util.concurrent.ThreadLocalRandom -import akka.actor.SupervisorStrategy.{ Directive, Escalate } -import akka.actor.{ Actor, ActorLogging, ActorRef, DeadLetterSuppression, OneForOneStrategy, Props, SupervisorStrategy, Terminated } +import akka.actor.{ ActorRef, DeadLetterSuppression, OneForOneStrategy, Props, SupervisorStrategy } import akka.util.JavaDurationConverters._ import scala.concurrent.duration.{ Duration, FiniteDuration } @@ -163,7 +162,7 @@ object BackoffSupervisor { require(minBackoff > Duration.Zero, "minBackoff must be > 0") require(maxBackoff >= minBackoff, "maxBackoff must be >= minBackoff") require(0.0 <= randomFactor && randomFactor <= 1.0, "randomFactor must be between 0.0 and 1.0") - Props(new BackoffSupervisor(childProps, childName, minBackoff, maxBackoff, randomFactor, strategy)) + Props(new BackoffOnStopSupervisor(childProps, childName, minBackoff, maxBackoff, AutoReset(minBackoff), randomFactor, strategy, None, None)) } /** @@ -278,41 +277,19 @@ object BackoffSupervisor { } } -/** - * Back-off supervisor that stops and starts a child actor using a back-off algorithm when the child actor stops. - * This back-off supervisor is created by using `akka.pattern.BackoffSupervisor.props` - * with `Backoff.onStop`. - */ +// for backwards compability +@deprecated("Use BackoffSupervisor props method instead", since = "2.5.20") final class BackoffSupervisor( - val childProps: Props, - val childName: String, - minBackoff: FiniteDuration, - maxBackoff: FiniteDuration, - val reset: BackoffReset, - randomFactor: Double, - strategy: SupervisorStrategy, - @deprecated("Should be internal", since = "2.5.19") // since removed from HandleBackoff the val can be removed - val replyWhileStopped: Option[Any], - @deprecated("Should be internal", since = "2.5.19") // since removed from HandleBackoff the val can be removed - val finalStopMessage: Option[Any ⇒ Boolean]) - extends Actor with HandleBackoff - with ActorLogging { - - import BackoffSupervisor._ - import context.dispatcher - - // to keep binary compatibility with 2.4.1 - override val supervisorStrategy = strategy match { - case oneForOne: OneForOneStrategy ⇒ - OneForOneStrategy(oneForOne.maxNrOfRetries, oneForOne.withinTimeRange, oneForOne.loggingEnabled) { - case ex ⇒ - val defaultDirective: Directive = - super.supervisorStrategy.decider.applyOrElse(ex, (_: Any) ⇒ Escalate) - - strategy.decider.applyOrElse(ex, (_: Any) ⇒ defaultDirective) - } - case s ⇒ s - } + override val childProps: Props, + override val childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + override val reset: BackoffReset, + randomFactor: Double, + strategy: SupervisorStrategy, + val replyWhileStopped: Option[Any], + val finalStopMessage: Option[Any ⇒ Boolean]) + extends BackoffOnStopSupervisor(childProps, childName, minBackoff, maxBackoff, reset, randomFactor, strategy, replyWhileStopped, finalStopMessage) { // for binary compatibility with 2.5.18 def this( @@ -343,51 +320,4 @@ final class BackoffSupervisor( maxBackoff: FiniteDuration, randomFactor: Double) = this(childProps, childName, minBackoff, maxBackoff, randomFactor, SupervisorStrategy.defaultStrategy) - - def onTerminated: Receive = { - case Terminated(ref) if child.contains(ref) ⇒ - child = None - if (finalStopMessageReceived) { - context.stop(self) - } else { - val maxNrOfRetries = strategy match { - case oneForOne: OneForOneStrategy ⇒ oneForOne.maxNrOfRetries - case _ ⇒ -1 - } - - val nextRestartCount = restartCount + 1 - - if (maxNrOfRetries == -1 || nextRestartCount <= maxNrOfRetries) { - val restartDelay = calculateDelay(restartCount, minBackoff, maxBackoff, randomFactor) - context.system.scheduler.scheduleOnce(restartDelay, self, StartChild) - restartCount = nextRestartCount - } else { - log.debug(s"Terminating on restart #{} which exceeds max allowed restarts ({})", nextRestartCount, maxNrOfRetries) - context.stop(self) - } - } - - } - - def receive: Receive = onTerminated orElse handleBackoff - - protected def handleMessageToChild(msg: Any): Unit = child match { - case Some(c) ⇒ - c.forward(msg) - if (!finalStopMessageReceived && finalStopMessage.isDefined) { - finalStopMessageReceived = finalStopMessage.get.apply(msg) - } - case None ⇒ - replyWhileStopped match { - case None ⇒ context.system.deadLetters.forward(msg) - case Some(r) ⇒ sender() ! r - } - finalStopMessage match { - case None ⇒ - case Some(fsm) ⇒ - if (fsm(msg)) { - context.stop(self) - } - } - } } From a8080a214040c060cff1e62c016ae19b20feb45c Mon Sep 17 00:00:00 2001 From: Nicolas Vollmar Date: Fri, 21 Dec 2018 19:06:30 +0100 Subject: [PATCH 03/11] Improving documentation and code style #26156 --- .../akka/pattern/BackoffOnStopSupervisor.scala | 9 +++------ .../main/scala/akka/pattern/BackoffOptions.scala | 5 +++-- .../main/scala/akka/pattern/BackoffSupervisor.scala | 2 +- .../src/main/scala/akka/pattern/HandleBackoff.scala | 13 +++++++------ 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala index a2526fb8d4..89073e6f6f 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala @@ -78,15 +78,12 @@ private[akka] class BackoffOnStopSupervisor( } case None ⇒ replyWhileStopped match { - case None ⇒ context.system.deadLetters.forward(msg) case Some(r) ⇒ sender() ! r + case None ⇒ context.system.deadLetters.forward(msg) } finalStopMessage match { - case None ⇒ - case Some(fsm) ⇒ - if (fsm(msg)) { - context.stop(self) - } + case Some(fsm) if fsm(msg) ⇒ context.stop(self) + case _ ⇒ } } } diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala index a629b4eaa7..5b90e6ae63 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala @@ -552,8 +552,9 @@ trait BackoffOptions { * Returns a new BackoffOptions with a maximum number of retries to restart the child actor. * By default, the supervisor will retry infinitely. * With this option, the supervisor will terminate itself after the maxNoOfRetries is reached. - * @param maxNrOfRetries the number of times a child actor is allowed to be restarted, negative value means no limit, - * if the limit is exceeded the child actor is stopped + * @param maxNrOfRetries the number of times a child actor is allowed to be restarted. + * If negative, the value is unbounded, otherwise the provided + * limit is used. If the limit is exceeded the child actor will be stopped. */ def withMaxNrOfRetries(maxNrOfRetries: Int): BackoffOptions diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala index b88f012f39..0aa462a91a 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala @@ -278,7 +278,7 @@ object BackoffSupervisor { } // for backwards compability -@deprecated("Use BackoffSupervisor props method instead", since = "2.5.20") +@deprecated("Use `BackoffSupervisor.props` method instead", since = "2.5.20") final class BackoffSupervisor( override val childProps: Props, override val childName: String, diff --git a/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala b/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala index 7920967656..4798ec75a5 100644 --- a/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala +++ b/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala @@ -6,6 +6,9 @@ package akka.pattern import akka.actor.{ Actor, ActorRef, Props } +/** + * Implements basic backoff handling for [[BackoffOnRestartSupervisor]] and [[BackoffOnStopSupervisor]]. + */ private[akka] trait HandleBackoff { this: Actor ⇒ def childProps: Props @@ -22,18 +25,16 @@ private[akka] trait HandleBackoff { override def preStart(): Unit = startChild() - def startChild(): Unit = { - if (child.isEmpty) { - child = Some(context.watch(context.actorOf(childProps, childName))) - } + def startChild(): Unit = if (child.isEmpty) { + child = Some(context.watch(context.actorOf(childProps, childName))) } - def handleBackoff: Receive = { + def handleBackoff: Actor.Receive = { case StartChild ⇒ startChild() reset match { case AutoReset(resetBackoff) ⇒ - val _ = context.system.scheduler.scheduleOnce(resetBackoff, self, ResetRestartCount(restartCount)) + context.system.scheduler.scheduleOnce(resetBackoff, self, ResetRestartCount(restartCount)) case _ ⇒ // ignore } From 0abd8bab96f00cb335c5d232bf96ee423bb59bf9 Mon Sep 17 00:00:00 2001 From: Nicolas Vollmar Date: Tue, 15 Jan 2019 21:34:11 +0100 Subject: [PATCH 04/11] Adding internal api markers and other small corrections #26156 --- .../pattern/BackoffOnRestartSupervisor.scala | 5 +++- .../pattern/BackoffOnStopSupervisor.scala | 11 +++++--- .../scala/akka/pattern/BackoffOptions.scala | 4 +-- .../akka/pattern/BackoffSupervisor.scala | 28 +++++++++---------- .../scala/akka/pattern/HandleBackoff.scala | 5 +++- 5 files changed, 31 insertions(+), 22 deletions(-) diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala index 09d93cbc65..1bf8d8a03d 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala @@ -6,15 +6,18 @@ package akka.pattern import akka.actor.SupervisorStrategy._ import akka.actor.{ OneForOneStrategy, _ } +import akka.annotation.InternalApi import scala.concurrent.duration._ /** + * INTERNAL API + * * Back-off supervisor that stops and starts a child actor when the child actor restarts. * This back-off supervisor is created by using ``akka.pattern.BackoffSupervisor.props`` * with ``akka.pattern.Backoff.onFailure``. */ -private class BackoffOnRestartSupervisor( +@InternalApi private class BackoffOnRestartSupervisor( val childProps: Props, val childName: String, minBackoff: FiniteDuration, diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala index 89073e6f6f..abd4623228 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala @@ -6,15 +6,18 @@ package akka.pattern import akka.actor.SupervisorStrategy.{ Directive, Escalate } import akka.actor.{ Actor, ActorLogging, OneForOneStrategy, Props, SupervisorStrategy, Terminated } +import akka.annotation.InternalApi import scala.concurrent.duration.FiniteDuration /** + * INTERNAL API + * * Back-off supervisor that stops and starts a child actor using a back-off algorithm when the child actor stops. * This back-off supervisor is created by using `akka.pattern.BackoffSupervisor.props` * with `Backoff.onStop`. */ -private[akka] class BackoffOnStopSupervisor( +@InternalApi private[akka] class BackoffOnStopSupervisor( val childProps: Props, val childName: String, minBackoff: FiniteDuration, @@ -30,7 +33,6 @@ private[akka] class BackoffOnStopSupervisor( import BackoffSupervisor._ import context.dispatcher - // to keep binary compatibility with 2.4.1 override val supervisorStrategy = strategy match { case oneForOne: OneForOneStrategy ⇒ OneForOneStrategy(oneForOne.maxNrOfRetries, oneForOne.withinTimeRange, oneForOne.loggingEnabled) { @@ -73,8 +75,9 @@ private[akka] class BackoffOnStopSupervisor( protected def handleMessageToChild(msg: Any): Unit = child match { case Some(c) ⇒ c.forward(msg) - if (!finalStopMessageReceived && finalStopMessage.isDefined) { - finalStopMessageReceived = finalStopMessage.get.apply(msg) + if (!finalStopMessageReceived) finalStopMessage match { + case Some(fsm) ⇒ finalStopMessageReceived = fsm(msg) + case None ⇒ } case None ⇒ replyWhileStopped match { diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala index 5b90e6ae63..c927223ed1 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala @@ -244,7 +244,7 @@ object Backoff { * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. * In order to skip this additional delay pass in `0`. */ - @Deprecated + @deprecated("Use the overloaded one which accepts maxNrOfRetries instead.", "2.5.17") def onFailure( childProps: Props, childName: String, @@ -497,7 +497,7 @@ object Backoff { * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. * In order to skip this additional delay pass in `0`. */ - @Deprecated + @deprecated("Use the overloaded one which accepts maxNrOfRetries instead.", "2.5.17") def onStop( childProps: Props, childName: String, diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala index 0aa462a91a..1e848ba76e 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala @@ -16,7 +16,7 @@ import scala.util.Try object BackoffSupervisor { /** - * Props for creating a [[BackoffSupervisor]] actor. + * Props for creating a `BackoffSupervisor` actor. * * Exceptions in the child are handled with the default supervision strategy, i.e. * most exceptions will immediately restart the child. You can define another @@ -42,7 +42,7 @@ object BackoffSupervisor { } /** - * Props for creating a [[BackoffSupervisor]] actor. + * Props for creating a `BackoffSupervisor` actor. * * Exceptions in the child are handled with the default supervision strategy, i.e. * most exceptions will immediately restart the child. You can define another @@ -76,7 +76,7 @@ object BackoffSupervisor { } /** - * Props for creating a [[BackoffSupervisor]] actor. + * Props for creating a `BackoffSupervisor` actor. * * Exceptions in the child are handled with the default supervision strategy, i.e. * most exceptions will immediately restart the child. You can define another @@ -102,7 +102,7 @@ object BackoffSupervisor { } /** - * Props for creating a [[BackoffSupervisor]] actor. + * Props for creating a `BackoffSupervisor` actor. * * Exceptions in the child are handled with the default supervision strategy, i.e. * most exceptions will immediately restart the child. You can define another @@ -132,7 +132,7 @@ object BackoffSupervisor { } /** - * Props for creating a [[BackoffSupervisor]] actor with a custom + * Props for creating a `BackoffSupervisor` actor with a custom * supervision strategy. * * Exceptions in the child are handled with the given `supervisionStrategy`. A @@ -166,7 +166,7 @@ object BackoffSupervisor { } /** - * Props for creating a [[BackoffSupervisor]] actor with a custom + * Props for creating a `BackoffSupervisor` actor with a custom * supervision strategy. * * Exceptions in the child are handled with the given `supervisionStrategy`. A @@ -197,26 +197,26 @@ object BackoffSupervisor { } /** - * Props for creating a [[BackoffSupervisor]] actor from [[BackoffOptions]]. + * Props for creating a `BackoffSupervisor` actor from [[BackoffOptions]]. * * @param options the [[BackoffOptions]] that specify how to construct a backoff-supervisor. */ def props(options: BackoffOptions): Props = options.props /** - * Send this message to the [[BackoffSupervisor]] and it will reply with + * Send this message to the `BackoffSupervisor` and it will reply with * [[BackoffSupervisor.CurrentChild]] containing the `ActorRef` of the current child, if any. */ final case object GetCurrentChild /** - * Java API: Send this message to the [[BackoffSupervisor]] and it will reply with + * Java API: Send this message to the `BackoffSupervisor` and it will reply with * [[BackoffSupervisor.CurrentChild]] containing the `ActorRef` of the current child, if any. */ def getCurrentChild = GetCurrentChild /** - * Send this message to the [[BackoffSupervisor]] and it will reply with + * Send this message to the `BackoffSupervisor` and it will reply with * [[BackoffSupervisor.CurrentChild]] containing the `ActorRef` of the current child, if any. */ final case class CurrentChild(ref: Option[ActorRef]) { @@ -227,25 +227,25 @@ object BackoffSupervisor { } /** - * Send this message to the [[BackoffSupervisor]] and it will reset the back-off. + * Send this message to the `BackoffSupervisor` and it will reset the back-off. * This should be used in conjunction with `withManualReset` in [[BackoffOptions]]. */ final case object Reset /** - * Java API: Send this message to the [[BackoffSupervisor]] and it will reset the back-off. + * Java API: Send this message to the `BackoffSupervisor` and it will reset the back-off. * This should be used in conjunction with `withManualReset` in [[BackoffOptions]]. */ def reset = Reset /** - * Send this message to the [[BackoffSupervisor]] and it will reply with + * Send this message to the `BackoffSupervisor` and it will reply with * [[BackoffSupervisor.RestartCount]] containing the current restart count. */ final case object GetRestartCount /** - * Java API: Send this message to the [[BackoffSupervisor]] and it will reply with + * Java API: Send this message to the `BackoffSupervisor` and it will reply with * [[BackoffSupervisor.RestartCount]] containing the current restart count. */ def getRestartCount = GetRestartCount diff --git a/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala b/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala index 4798ec75a5..d34b915760 100644 --- a/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala +++ b/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala @@ -5,11 +5,14 @@ package akka.pattern import akka.actor.{ Actor, ActorRef, Props } +import akka.annotation.InternalApi /** + * INTERNAL API + * * Implements basic backoff handling for [[BackoffOnRestartSupervisor]] and [[BackoffOnStopSupervisor]]. */ -private[akka] trait HandleBackoff { +@InternalApi private[akka] trait HandleBackoff { this: Actor ⇒ def childProps: Props def childName: String From 6b775003922b3bb4a6f3460ee1b3d0ff5f35acca Mon Sep 17 00:00:00 2001 From: Nicolas Vollmar Date: Fri, 18 Jan 2019 12:57:19 +0100 Subject: [PATCH 05/11] Adds new extended backoff API and moves old deprecated API to separate file #26156 --- .../mima-filters/2.5.22.backwards.excludes | 4 +- .../src/main/scala/akka/pattern/Backoff.scala | 610 ++++++++++++++++++ .../scala/akka/pattern/BackoffOptions.scala | 461 +++---------- .../akka/pattern/BackoffSupervisor.scala | 21 + 4 files changed, 737 insertions(+), 359 deletions(-) create mode 100644 akka-actor/src/main/scala/akka/pattern/Backoff.scala diff --git a/akka-actor/src/main/mima-filters/2.5.22.backwards.excludes b/akka-actor/src/main/mima-filters/2.5.22.backwards.excludes index 6cf3f129cd..d0b5506993 100644 --- a/akka-actor/src/main/mima-filters/2.5.22.backwards.excludes +++ b/akka-actor/src/main/mima-filters/2.5.22.backwards.excludes @@ -4,6 +4,4 @@ ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestar ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestartSupervisor.this") ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.HandleBackoff.replyWhileStopped") ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.HandleBackoff.finalStopMessage") -ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.pattern.HandleBackoff.handleMessageToChild") -ProblemFilters.exclude[IncompatibleResultTypeProblem]("akka.pattern.Backoff.onFailure") -ProblemFilters.exclude[IncompatibleResultTypeProblem]("akka.pattern.Backoff.onStop") \ No newline at end of file +ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.pattern.HandleBackoff.handleMessageToChild") \ No newline at end of file diff --git a/akka-actor/src/main/scala/akka/pattern/Backoff.scala b/akka-actor/src/main/scala/akka/pattern/Backoff.scala new file mode 100644 index 0000000000..a127760571 --- /dev/null +++ b/akka-actor/src/main/scala/akka/pattern/Backoff.scala @@ -0,0 +1,610 @@ +/* + * Copyright (C) 2015-2019 Lightbend Inc. + */ + +package akka.pattern + +import akka.actor.{ OneForOneStrategy, Props, SupervisorStrategy } +import akka.annotation.DoNotInherit +import akka.util.JavaDurationConverters._ + +import scala.concurrent.duration.{ Duration, FiniteDuration } + +/** + * @deprecated This API is superseded by the [[BackoffOptions]] object. + */ +@Deprecated +@deprecated("Use new API from BackoffOptions object instead", since = "2.5.20") +object Backoff { + /** + * Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. + * + * This explicit supervisor behaves similarly to the normal implicit supervision where + * if an actor throws an exception, the decider on the supervisor will decide when to + * `Stop`, `Restart`, `Escalate`, `Resume` the child actor. + * + * When the `Restart` directive is specified, the supervisor will delay the restart + * using an exponential back off strategy (bounded by minBackoff and maxBackoff). + * + * This supervisor is intended to be transparent to both the child actor and external actors. + * Where external actors can send messages to the supervisor as if it was the child and the + * messages will be forwarded. And when the child is `Terminated`, the supervisor is also + * `Terminated`. + * Transparent to the child means that the child does not have to be aware that it is being + * supervised specifically by this actor. Just like it does + * not need to know when it is being supervised by the usual implicit supervisors. + * The only caveat is that the `ActorRef` of the child is not stable, so any user storing the + * `sender()` `ActorRef` from the child response may eventually not be able to communicate with + * the stored `ActorRef`. In general all messages to the child should be directed through this actor. + * + * An example of where this supervisor might be used is when you may have an actor that is + * responsible for continuously polling on a server for some resource that sometimes may be down. + * Instead of hammering the server continuously when the resource is unavailable, the actor will + * be restarted with an exponentially increasing back off until the resource is available again. + * + * '''*** + * This supervisor should not be used with `Akka Persistence` child actors. + * `Akka Persistence` actors shutdown unconditionally on `persistFailure()`s rather + * than throw an exception on a failure like normal actors. + * [[#onStop]] should be used instead for cases where the child actor + * terminates itself as a failure signal instead of the normal behavior of throwing an exception. + * ***''' + * You can define another + * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + * @param maxNrOfRetries maximum number of attempts to restart the child actor. + * The supervisor will terminate itself after the maxNoOfRetries is reached. + * In order to restart infinitely pass in `-1`. + * + */ + @deprecated("Use BackoffOptions.onFailure instead", "2.5.20") + def onFailure( + childProps: Props, + childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + randomFactor: Double, + maxNrOfRetries: Int): BackoffOptions = + BackoffOptionsImpl(RestartImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor).withMaxNrOfRetries(maxNrOfRetries) + + /** + * Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. + * + * This explicit supervisor behaves similarly to the normal implicit supervision where + * if an actor throws an exception, the decider on the supervisor will decide when to + * `Stop`, `Restart`, `Escalate`, `Resume` the child actor. + * + * When the `Restart` directive is specified, the supervisor will delay the restart + * using an exponential back off strategy (bounded by minBackoff and maxBackoff). + * + * This supervisor is intended to be transparent to both the child actor and external actors. + * Where external actors can send messages to the supervisor as if it was the child and the + * messages will be forwarded. And when the child is `Terminated`, the supervisor is also + * `Terminated`. + * Transparent to the child means that the child does not have to be aware that it is being + * supervised specifically by this actor. Just like it does + * not need to know when it is being supervised by the usual implicit supervisors. + * The only caveat is that the `ActorRef` of the child is not stable, so any user storing the + * `sender()` `ActorRef` from the child response may eventually not be able to communicate with + * the stored `ActorRef`. In general all messages to the child should be directed through this actor. + * + * An example of where this supervisor might be used is when you may have an actor that is + * responsible for continuously polling on a server for some resource that sometimes may be down. + * Instead of hammering the server continuously when the resource is unavailable, the actor will + * be restarted with an exponentially increasing back off until the resource is available again. + * + * '''*** + * This supervisor should not be used with `Akka Persistence` child actors. + * `Akka Persistence` actors shutdown unconditionally on `persistFailure()`s rather + * than throw an exception on a failure like normal actors. + * [[#onStop]] should be used instead for cases where the child actor + * terminates itself as a failure signal instead of the normal behavior of throwing an exception. + * ***''' + * You can define another + * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + */ + @deprecated("Use BackoffOptions.onFailure instead", "2.5.20") + def onFailure( + childProps: Props, + childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + randomFactor: Double): BackoffOptions = + BackoffOptionsImpl(RestartImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor) + + /** + * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. + * + * This explicit supervisor behaves similarly to the normal implicit supervision where + * if an actor throws an exception, the decider on the supervisor will decide when to + * `Stop`, `Restart`, `Escalate`, `Resume` the child actor. + * + * When the `Restart` directive is specified, the supervisor will delay the restart + * using an exponential back off strategy (bounded by minBackoff and maxBackoff). + * + * This supervisor is intended to be transparent to both the child actor and external actors. + * Where external actors can send messages to the supervisor as if it was the child and the + * messages will be forwarded. And when the child is `Terminated`, the supervisor is also + * `Terminated`. + * Transparent to the child means that the child does not have to be aware that it is being + * supervised specifically by this actor. Just like it does + * not need to know when it is being supervised by the usual implicit supervisors. + * The only caveat is that the `ActorRef` of the child is not stable, so any user storing the + * `sender()` `ActorRef` from the child response may eventually not be able to communicate with + * the stored `ActorRef`. In general all messages to the child should be directed through this actor. + * + * An example of where this supervisor might be used is when you may have an actor that is + * responsible for continuously polling on a server for some resource that sometimes may be down. + * Instead of hammering the server continuously when the resource is unavailable, the actor will + * be restarted with an exponentially increasing back off until the resource is available again. + * + * '''*** + * This supervisor should not be used with `Akka Persistence` child actors. + * `Akka Persistence` actors shutdown unconditionally on `persistFailure()`s rather + * than throw an exception on a failure like normal actors. + * [[#onStop]] should be used instead for cases where the child actor + * terminates itself as a failure signal instead of the normal behavior of throwing an exception. + * ***''' + * You can define another + * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + * @param maxNrOfRetries maximum number of attempts to restart the child actor. + * The supervisor will terminate itself after the maxNoOfRetries is reached. + * In order to restart infinitely pass in `-1`. + */ + @Deprecated + @deprecated("Use BackoffOptions.onFailure instead", "2.5.20") + def onFailure( + childProps: Props, + childName: String, + minBackoff: java.time.Duration, + maxBackoff: java.time.Duration, + randomFactor: Double, + maxNrOfRetries: Int): BackoffOptions = + onFailure(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, maxNrOfRetries) + + /** + * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. + * + * This explicit supervisor behaves similarly to the normal implicit supervision where + * if an actor throws an exception, the decider on the supervisor will decide when to + * `Stop`, `Restart`, `Escalate`, `Resume` the child actor. + * + * When the `Restart` directive is specified, the supervisor will delay the restart + * using an exponential back off strategy (bounded by minBackoff and maxBackoff). + * + * This supervisor is intended to be transparent to both the child actor and external actors. + * Where external actors can send messages to the supervisor as if it was the child and the + * messages will be forwarded. And when the child is `Terminated`, the supervisor is also + * `Terminated`. + * Transparent to the child means that the child does not have to be aware that it is being + * supervised specifically by this actor. Just like it does + * not need to know when it is being supervised by the usual implicit supervisors. + * The only caveat is that the `ActorRef` of the child is not stable, so any user storing the + * `sender()` `ActorRef` from the child response may eventually not be able to communicate with + * the stored `ActorRef`. In general all messages to the child should be directed through this actor. + * + * An example of where this supervisor might be used is when you may have an actor that is + * responsible for continuously polling on a server for some resource that sometimes may be down. + * Instead of hammering the server continuously when the resource is unavailable, the actor will + * be restarted with an exponentially increasing back off until the resource is available again. + * + * '''*** + * This supervisor should not be used with `Akka Persistence` child actors. + * `Akka Persistence` actors shutdown unconditionally on `persistFailure()`s rather + * than throw an exception on a failure like normal actors. + * [[#onStop]] should be used instead for cases where the child actor + * terminates itself as a failure signal instead of the normal behavior of throwing an exception. + * ***''' + * You can define another + * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + */ + @Deprecated + @deprecated("Use the overloaded one which accepts maxNrOfRetries instead.", "2.5.17") + def onFailure( + childProps: Props, + childName: String, + minBackoff: java.time.Duration, + maxBackoff: java.time.Duration, + randomFactor: Double): BackoffOptions = + onFailure(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, -1) + + /** + * Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. + * + * This actor can be used to supervise a child actor and start it again + * after a back-off duration if the child actor is stopped. + * + * This is useful in situations where the re-start of the child actor should be + * delayed e.g. in order to give an external resource time to recover before the + * child actor tries contacting it again (after being restarted). + * + * Specifically this pattern is useful for persistent actors, + * which are stopped in case of persistence failures. + * Just restarting them immediately would probably fail again (since the data + * store is probably unavailable). It is better to try again after a delay. + * + * It supports exponential back-off between the given `minBackoff` and + * `maxBackoff` durations. For example, if `minBackoff` is 3 seconds and + * `maxBackoff` 30 seconds the start attempts will be delayed with + * 3, 6, 12, 24, 30, 30 seconds. The exponential back-off counter is reset + * if the actor is not terminated within the `minBackoff` duration. + * + * In addition to the calculated exponential back-off an additional + * random delay based the given `randomFactor` is added, e.g. 0.2 adds up to 20% + * delay. The reason for adding a random delay is to avoid that all failing + * actors hit the backend resource at the same time. + * + * You can retrieve the current child `ActorRef` by sending `BackoffSupervisor.GetCurrentChild` + * message to this actor and it will reply with [[akka.pattern.BackoffSupervisor.CurrentChild]] + * containing the `ActorRef` of the current child, if any. + * + * The `BackoffSupervisor`delegates all messages from the child to the parent of the + * `BackoffSupervisor`, with the supervisor as sender. + * + * The `BackoffSupervisor` forwards all other messages to the child, if it is currently running. + * + * The child can stop itself and send a [[akka.actor.PoisonPill]] to the parent supervisor + * if it wants to do an intentional stop. + * + * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using + * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A + * `Restart` will perform a normal immediate restart of the child. A `Stop` will + * stop the child, but it will be started again after the back-off duration. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + * @param maxNrOfRetries maximum number of attempts to restart the child actor. + * The supervisor will terminate itself after the maxNoOfRetries is reached. + * In order to restart infinitely pass in `-1`. + */ + @deprecated("Use BackoffOptions.onStop instead", "2.5.20") + def onStop( + childProps: Props, + childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + randomFactor: Double, + maxNrOfRetries: Int): BackoffOptions = + BackoffOptionsImpl(StopImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor).withMaxNrOfRetries(maxNrOfRetries) + + /** + * Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. + * + * This actor can be used to supervise a child actor and start it again + * after a back-off duration if the child actor is stopped. + * + * This is useful in situations where the re-start of the child actor should be + * delayed e.g. in order to give an external resource time to recover before the + * child actor tries contacting it again (after being restarted). + * + * Specifically this pattern is useful for persistent actors, + * which are stopped in case of persistence failures. + * Just restarting them immediately would probably fail again (since the data + * store is probably unavailable). It is better to try again after a delay. + * + * It supports exponential back-off between the given `minBackoff` and + * `maxBackoff` durations. For example, if `minBackoff` is 3 seconds and + * `maxBackoff` 30 seconds the start attempts will be delayed with + * 3, 6, 12, 24, 30, 30 seconds. The exponential back-off counter is reset + * if the actor is not terminated within the `minBackoff` duration. + * + * In addition to the calculated exponential back-off an additional + * random delay based the given `randomFactor` is added, e.g. 0.2 adds up to 20% + * delay. The reason for adding a random delay is to avoid that all failing + * actors hit the backend resource at the same time. + * + * You can retrieve the current child `ActorRef` by sending `BackoffSupervisor.GetCurrentChild` + * message to this actor and it will reply with [[akka.pattern.BackoffSupervisor.CurrentChild]] + * containing the `ActorRef` of the current child, if any. + * + * The `BackoffSupervisor`delegates all messages from the child to the parent of the + * `BackoffSupervisor`, with the supervisor as sender. + * + * The `BackoffSupervisor` forwards all other messages to the child, if it is currently running. + * + * The child can stop itself and send a [[akka.actor.PoisonPill]] to the parent supervisor + * if it wants to do an intentional stop. + * + * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using + * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A + * `Restart` will perform a normal immediate restart of the child. A `Stop` will + * stop the child, but it will be started again after the back-off duration. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + */ + @deprecated("Use BackoffOptions.onStop instead", "2.5.20") + def onStop( + childProps: Props, + childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + randomFactor: Double): BackoffOptions = + BackoffOptionsImpl(StopImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor) + + /** + * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. + * + * This actor can be used to supervise a child actor and start it again + * after a back-off duration if the child actor is stopped. + * + * This is useful in situations where the re-start of the child actor should be + * delayed e.g. in order to give an external resource time to recover before the + * child actor tries contacting it again (after being restarted). + * + * Specifically this pattern is useful for persistent actors, + * which are stopped in case of persistence failures. + * Just restarting them immediately would probably fail again (since the data + * store is probably unavailable). It is better to try again after a delay. + * + * It supports exponential back-off between the given `minBackoff` and + * `maxBackoff` durations. For example, if `minBackoff` is 3 seconds and + * `maxBackoff` 30 seconds the start attempts will be delayed with + * 3, 6, 12, 24, 30, 30 seconds. The exponential back-off counter is reset + * if the actor is not terminated within the `minBackoff` duration. + * + * In addition to the calculated exponential back-off an additional + * random delay based the given `randomFactor` is added, e.g. 0.2 adds up to 20% + * delay. The reason for adding a random delay is to avoid that all failing + * actors hit the backend resource at the same time. + * + * You can retrieve the current child `ActorRef` by sending `BackoffSupervisor.GetCurrentChild` + * message to this actor and it will reply with [[akka.pattern.BackoffSupervisor.CurrentChild]] + * containing the `ActorRef` of the current child, if any. + * + * The `BackoffSupervisor`delegates all messages from the child to the parent of the + * `BackoffSupervisor`, with the supervisor as sender. + * + * The `BackoffSupervisor` forwards all other messages to the child, if it is currently running. + * + * The child can stop itself and send a [[akka.actor.PoisonPill]] to the parent supervisor + * if it wants to do an intentional stop. + * + * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using + * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A + * `Restart` will perform a normal immediate restart of the child. A `Stop` will + * stop the child, but it will be started again after the back-off duration. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + * @param maxNrOfRetries maximum number of attempts to restart the child actor. + * The supervisor will terminate itself after the maxNoOfRetries is reached. + * In order to restart infinitely pass in `-1`. + */ + @Deprecated + @deprecated("Use BackoffOptions.onStop instead", "2.5.20") + def onStop( + childProps: Props, + childName: String, + minBackoff: java.time.Duration, + maxBackoff: java.time.Duration, + randomFactor: Double, + maxNrOfRetries: Int): BackoffOptions = + onStop(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, maxNrOfRetries) + + /** + * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. + * + * This actor can be used to supervise a child actor and start it again + * after a back-off duration if the child actor is stopped. + * + * This is useful in situations where the re-start of the child actor should be + * delayed e.g. in order to give an external resource time to recover before the + * child actor tries contacting it again (after being restarted). + * + * Specifically this pattern is useful for persistent actors, + * which are stopped in case of persistence failures. + * Just restarting them immediately would probably fail again (since the data + * store is probably unavailable). It is better to try again after a delay. + * + * It supports exponential back-off between the given `minBackoff` and + * `maxBackoff` durations. For example, if `minBackoff` is 3 seconds and + * `maxBackoff` 30 seconds the start attempts will be delayed with + * 3, 6, 12, 24, 30, 30 seconds. The exponential back-off counter is reset + * if the actor is not terminated within the `minBackoff` duration. + * + * In addition to the calculated exponential back-off an additional + * random delay based the given `randomFactor` is added, e.g. 0.2 adds up to 20% + * delay. The reason for adding a random delay is to avoid that all failing + * actors hit the backend resource at the same time. + * + * You can retrieve the current child `ActorRef` by sending `BackoffSupervisor.GetCurrentChild` + * message to this actor and it will reply with [[akka.pattern.BackoffSupervisor.CurrentChild]] + * containing the `ActorRef` of the current child, if any. + * + * The `BackoffSupervisor`delegates all messages from the child to the parent of the + * `BackoffSupervisor`, with the supervisor as sender. + * + * The `BackoffSupervisor` forwards all other messages to the child, if it is currently running. + * + * The child can stop itself and send a [[akka.actor.PoisonPill]] to the parent supervisor + * if it wants to do an intentional stop. + * + * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using + * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A + * `Restart` will perform a normal immediate restart of the child. A `Stop` will + * stop the child, but it will be started again after the back-off duration. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + */ + @Deprecated + @deprecated("Use the overloaded one which accepts maxNrOfRetries instead.", "2.5.17") + def onStop( + childProps: Props, + childName: String, + minBackoff: java.time.Duration, + maxBackoff: java.time.Duration, + randomFactor: Double): BackoffOptions = + onStop(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, -1) +} + +/** + * Configures a back-off supervisor actor. Start with `Backoff.onStop` or `Backoff.onFailure`. + * BackoffOptions is immutable, so be sure to chain methods like: + * {{{ + * val options = Backoff.onFailure(childProps, childName, minBackoff, maxBackoff, randomFactor) + * .withManualReset + * context.actorOf(BackoffSupervisor.props(options), name) + * }}} + */ +@DoNotInherit +@Deprecated +@deprecated("Use new API from BackoffOptions object instead", since = "2.5.20") +trait BackoffOptions { + + /** + * @see [[ExtendedBackoffOptions.withAutoReset()]] + */ + def withAutoReset(resetBackoff: FiniteDuration): BackoffOptions + + /** + * @see [[ExtendedBackoffOptions.withManualReset()]] + */ + def withManualReset: BackoffOptions + + /** + * @see [[ExtendedBackoffOptions.withSupervisorStrategy()]] + */ + def withSupervisorStrategy(supervisorStrategy: OneForOneStrategy): BackoffOptions + + /** + * @see [[ExtendedBackoffOptions.withDefaultStoppingStrategy()]] + */ + def withDefaultStoppingStrategy: BackoffOptions + + /** + * @see [[ExtendedBackoffOptions.withMaxNrOfRetries()]] + */ + def withMaxNrOfRetries(maxNrOfRetries: Int): BackoffOptions + + /** + * @see [[ExtendedBackoffOptions.withReplyWhileStopped()]] + */ + def withReplyWhileStopped(replyWhileStopped: Any): BackoffOptions + + /** + * @see [[BackoffOnStopOptions.withFinalStopMessage()]] + */ + def withFinalStopMessage(isFinalStopMessage: Any ⇒ Boolean): BackoffOptions + + /** + * Returns the props to create the back-off supervisor. + */ + private[akka] def props: Props +} + +private final case class BackoffOptionsImpl( + backoffType: BackoffType = RestartImpliesFailure, + childProps: Props, + childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + randomFactor: Double, + reset: Option[BackoffReset] = None, + supervisorStrategy: OneForOneStrategy = OneForOneStrategy()(SupervisorStrategy.defaultStrategy.decider), + replyWhileStopped: Option[Any] = None, + finalStopMessage: Option[Any ⇒ Boolean] = None +) extends akka.pattern.BackoffOptions { + + val backoffReset = reset.getOrElse(AutoReset(minBackoff)) + + def withAutoReset(resetBackoff: FiniteDuration) = copy(reset = Some(AutoReset(resetBackoff))) + def withManualReset = copy(reset = Some(ManualReset)) + def withSupervisorStrategy(supervisorStrategy: OneForOneStrategy) = copy(supervisorStrategy = supervisorStrategy) + def withDefaultStoppingStrategy = copy(supervisorStrategy = OneForOneStrategy(supervisorStrategy.maxNrOfRetries)(SupervisorStrategy.stoppingStrategy.decider)) + def withReplyWhileStopped(replyWhileStopped: Any) = copy(replyWhileStopped = Some(replyWhileStopped)) + def withMaxNrOfRetries(maxNrOfRetries: Int) = copy(supervisorStrategy = supervisorStrategy.withMaxNrOfRetries(maxNrOfRetries)) + def withFinalStopMessage(action: Any ⇒ Boolean) = copy(finalStopMessage = Some(action)) + + def props = { + require(minBackoff > Duration.Zero, "minBackoff must be > 0") + require(maxBackoff >= minBackoff, "maxBackoff must be >= minBackoff") + require(0.0 <= randomFactor && randomFactor <= 1.0, "randomFactor must be between 0.0 and 1.0") + backoffReset match { + case AutoReset(resetBackoff) ⇒ + require(minBackoff <= resetBackoff && resetBackoff <= maxBackoff) + case _ ⇒ // ignore + } + + backoffType match { + //onFailure method in companion object + case RestartImpliesFailure ⇒ + Props(new BackoffOnRestartSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped)) + //onStop method in companion object + case StopImpliesFailure ⇒ + Props(new BackoffOnStopSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped, finalStopMessage)) + } + } +} + +private sealed trait BackoffType +private final case object StopImpliesFailure extends BackoffType +private final case object RestartImpliesFailure extends BackoffType diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala index c927223ed1..28dcda8c3c 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala @@ -4,29 +4,16 @@ package akka.pattern -import scala.concurrent.duration.{ Duration, FiniteDuration } -import akka.util.JavaDurationConverters._ import akka.actor.{ OneForOneStrategy, Props, SupervisorStrategy } import akka.annotation.DoNotInherit +import akka.util.JavaDurationConverters._ + +import scala.concurrent.duration.{ Duration, FiniteDuration } /** - * Builds back-off options for creating a back-off supervisor. - * You can pass `BackoffOptions` to `akka.pattern.BackoffSupervisor.props`. - * An example of creating back-off options: - * {{{ - * Backoff.onFailure(childProps, childName, minBackoff, maxBackoff, randomFactor) - * .withManualReset - * .withSupervisorStrategy( - * OneforOneStrategy(){ - * case e: GivingUpException => Stop - * case e: RetryableException => Restart - * } - * ) - * .withReplyWhileStopped(TheSystemIsDown) - * - * }}} + * Backoff options allow to specify a number of properties for backoff supervisors. */ -object Backoff { +object BackoffOpts { /** * Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. * @@ -61,76 +48,17 @@ object Backoff { * terminates itself as a failure signal instead of the normal behavior of throwing an exception. * ***''' * You can define another - * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. + * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOnFailureOptions]]. * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. - * @param maxNrOfRetries maximum number of attempts to restart the child actor. - * The supervisor will terminate itself after the maxNoOfRetries is reached. - * In order to restart infinitely pass in `-1`. - * - */ - def onFailure( - childProps: Props, - childName: String, - minBackoff: FiniteDuration, - maxBackoff: FiniteDuration, - randomFactor: Double, - maxNrOfRetries: Int): BackoffOnFailureOptions = - BackoffOptionsImpl(RestartImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor).withMaxNrOfRetries(maxNrOfRetries) - - /** - * Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. - * - * This explicit supervisor behaves similarly to the normal implicit supervision where - * if an actor throws an exception, the decider on the supervisor will decide when to - * `Stop`, `Restart`, `Escalate`, `Resume` the child actor. - * - * When the `Restart` directive is specified, the supervisor will delay the restart - * using an exponential back off strategy (bounded by minBackoff and maxBackoff). - * - * This supervisor is intended to be transparent to both the child actor and external actors. - * Where external actors can send messages to the supervisor as if it was the child and the - * messages will be forwarded. And when the child is `Terminated`, the supervisor is also - * `Terminated`. - * Transparent to the child means that the child does not have to be aware that it is being - * supervised specifically by this actor. Just like it does - * not need to know when it is being supervised by the usual implicit supervisors. - * The only caveat is that the `ActorRef` of the child is not stable, so any user storing the - * `sender()` `ActorRef` from the child response may eventually not be able to communicate with - * the stored `ActorRef`. In general all messages to the child should be directed through this actor. - * - * An example of where this supervisor might be used is when you may have an actor that is - * responsible for continuously polling on a server for some resource that sometimes may be down. - * Instead of hammering the server continuously when the resource is unavailable, the actor will - * be restarted with an exponentially increasing back off until the resource is available again. - * - * '''*** - * This supervisor should not be used with `Akka Persistence` child actors. - * `Akka Persistence` actors shutdown unconditionally on `persistFailure()`s rather - * than throw an exception on a failure like normal actors. - * [[#onStop]] should be used instead for cases where the child actor - * terminates itself as a failure signal instead of the normal behavior of throwing an exception. - * ***''' - * You can define another - * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. - * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration - * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. */ def onFailure( childProps: Props, @@ -138,7 +66,7 @@ object Backoff { minBackoff: FiniteDuration, maxBackoff: FiniteDuration, randomFactor: Double): BackoffOnFailureOptions = - BackoffOptionsImpl(RestartImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor) + BackoffOnFailureOptionsImpl(childProps, childName, minBackoff, maxBackoff, randomFactor) /** * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. @@ -174,84 +102,25 @@ object Backoff { * terminates itself as a failure signal instead of the normal behavior of throwing an exception. * ***''' * You can define another - * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. + * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOnFailureOptions]]. * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration - * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. - * @param maxNrOfRetries maximum number of attempts to restart the child actor. - * The supervisor will terminate itself after the maxNoOfRetries is reached. - * In order to restart infinitely pass in `-1`. + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. */ - def onFailure( - childProps: Props, - childName: String, - minBackoff: java.time.Duration, - maxBackoff: java.time.Duration, - randomFactor: Double, - maxNrOfRetries: Int): BackoffOnFailureOptions = - onFailure(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, maxNrOfRetries) - - /** - * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. - * - * This explicit supervisor behaves similarly to the normal implicit supervision where - * if an actor throws an exception, the decider on the supervisor will decide when to - * `Stop`, `Restart`, `Escalate`, `Resume` the child actor. - * - * When the `Restart` directive is specified, the supervisor will delay the restart - * using an exponential back off strategy (bounded by minBackoff and maxBackoff). - * - * This supervisor is intended to be transparent to both the child actor and external actors. - * Where external actors can send messages to the supervisor as if it was the child and the - * messages will be forwarded. And when the child is `Terminated`, the supervisor is also - * `Terminated`. - * Transparent to the child means that the child does not have to be aware that it is being - * supervised specifically by this actor. Just like it does - * not need to know when it is being supervised by the usual implicit supervisors. - * The only caveat is that the `ActorRef` of the child is not stable, so any user storing the - * `sender()` `ActorRef` from the child response may eventually not be able to communicate with - * the stored `ActorRef`. In general all messages to the child should be directed through this actor. - * - * An example of where this supervisor might be used is when you may have an actor that is - * responsible for continuously polling on a server for some resource that sometimes may be down. - * Instead of hammering the server continuously when the resource is unavailable, the actor will - * be restarted with an exponentially increasing back off until the resource is available again. - * - * '''*** - * This supervisor should not be used with `Akka Persistence` child actors. - * `Akka Persistence` actors shutdown unconditionally on `persistFailure()`s rather - * than throw an exception on a failure like normal actors. - * [[#onStop]] should be used instead for cases where the child actor - * terminates itself as a failure signal instead of the normal behavior of throwing an exception. - * ***''' - * You can define another - * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. - * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration - * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. - */ - @deprecated("Use the overloaded one which accepts maxNrOfRetries instead.", "2.5.17") def onFailure( childProps: Props, childName: String, minBackoff: java.time.Duration, maxBackoff: java.time.Duration, randomFactor: Double): BackoffOnFailureOptions = - onFailure(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, -1) + onFailure(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor) /** * Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. @@ -292,84 +161,19 @@ object Backoff { * if it wants to do an intentional stop. * * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using - * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A + * [[BackoffOnStopOptions#withSupervisorStrategy]] or [[BackoffOnStopOptions#withDefaultStoppingStrategy]]. A * `Restart` will perform a normal immediate restart of the child. A `Stop` will * stop the child, but it will be started again after the back-off duration. * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. - * @param maxNrOfRetries maximum number of attempts to restart the child actor. - * The supervisor will terminate itself after the maxNoOfRetries is reached. - * In order to restart infinitely pass in `-1`. - */ - def onStop( - childProps: Props, - childName: String, - minBackoff: FiniteDuration, - maxBackoff: FiniteDuration, - randomFactor: Double, - maxNrOfRetries: Int): BackoffOnStopOptions = - BackoffOptionsImpl(StopImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor).withMaxNrOfRetries(maxNrOfRetries) - - /** - * Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. - * - * This actor can be used to supervise a child actor and start it again - * after a back-off duration if the child actor is stopped. - * - * This is useful in situations where the re-start of the child actor should be - * delayed e.g. in order to give an external resource time to recover before the - * child actor tries contacting it again (after being restarted). - * - * Specifically this pattern is useful for persistent actors, - * which are stopped in case of persistence failures. - * Just restarting them immediately would probably fail again (since the data - * store is probably unavailable). It is better to try again after a delay. - * - * It supports exponential back-off between the given `minBackoff` and - * `maxBackoff` durations. For example, if `minBackoff` is 3 seconds and - * `maxBackoff` 30 seconds the start attempts will be delayed with - * 3, 6, 12, 24, 30, 30 seconds. The exponential back-off counter is reset - * if the actor is not terminated within the `minBackoff` duration. - * - * In addition to the calculated exponential back-off an additional - * random delay based the given `randomFactor` is added, e.g. 0.2 adds up to 20% - * delay. The reason for adding a random delay is to avoid that all failing - * actors hit the backend resource at the same time. - * - * You can retrieve the current child `ActorRef` by sending `BackoffSupervisor.GetCurrentChild` - * message to this actor and it will reply with [[akka.pattern.BackoffSupervisor.CurrentChild]] - * containing the `ActorRef` of the current child, if any. - * - * The `BackoffSupervisor`delegates all messages from the child to the parent of the - * `BackoffSupervisor`, with the supervisor as sender. - * - * The `BackoffSupervisor` forwards all other messages to the child, if it is currently running. - * - * The child can stop itself and send a [[akka.actor.PoisonPill]] to the parent supervisor - * if it wants to do an intentional stop. - * - * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using - * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A - * `Restart` will perform a normal immediate restart of the child. A `Stop` will - * stop the child, but it will be started again after the back-off duration. - * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration - * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. */ def onStop( childProps: Props, @@ -377,7 +181,7 @@ object Backoff { minBackoff: FiniteDuration, maxBackoff: FiniteDuration, randomFactor: Double): BackoffOnStopOptions = - BackoffOptionsImpl(StopImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor) + BackoffOnStopOptionsImpl(childProps, childName, minBackoff, maxBackoff, randomFactor) /** * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. @@ -418,158 +222,83 @@ object Backoff { * if it wants to do an intentional stop. * * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using - * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A + * [[BackoffOnStopOptions#withSupervisorStrategy]] or [[BackoffOnStopOptions#withDefaultStoppingStrategy]]. A * `Restart` will perform a normal immediate restart of the child. A `Stop` will * stop the child, but it will be started again after the back-off duration. * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration - * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. - * @param maxNrOfRetries maximum number of attempts to restart the child actor. - * The supervisor will terminate itself after the maxNoOfRetries is reached. - * In order to restart infinitely pass in `-1`. + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. */ - def onStop( - childProps: Props, - childName: String, - minBackoff: java.time.Duration, - maxBackoff: java.time.Duration, - randomFactor: Double, - maxNrOfRetries: Int): BackoffOnStopOptions = - onStop(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, maxNrOfRetries) - - /** - * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. - * - * This actor can be used to supervise a child actor and start it again - * after a back-off duration if the child actor is stopped. - * - * This is useful in situations where the re-start of the child actor should be - * delayed e.g. in order to give an external resource time to recover before the - * child actor tries contacting it again (after being restarted). - * - * Specifically this pattern is useful for persistent actors, - * which are stopped in case of persistence failures. - * Just restarting them immediately would probably fail again (since the data - * store is probably unavailable). It is better to try again after a delay. - * - * It supports exponential back-off between the given `minBackoff` and - * `maxBackoff` durations. For example, if `minBackoff` is 3 seconds and - * `maxBackoff` 30 seconds the start attempts will be delayed with - * 3, 6, 12, 24, 30, 30 seconds. The exponential back-off counter is reset - * if the actor is not terminated within the `minBackoff` duration. - * - * In addition to the calculated exponential back-off an additional - * random delay based the given `randomFactor` is added, e.g. 0.2 adds up to 20% - * delay. The reason for adding a random delay is to avoid that all failing - * actors hit the backend resource at the same time. - * - * You can retrieve the current child `ActorRef` by sending `BackoffSupervisor.GetCurrentChild` - * message to this actor and it will reply with [[akka.pattern.BackoffSupervisor.CurrentChild]] - * containing the `ActorRef` of the current child, if any. - * - * The `BackoffSupervisor`delegates all messages from the child to the parent of the - * `BackoffSupervisor`, with the supervisor as sender. - * - * The `BackoffSupervisor` forwards all other messages to the child, if it is currently running. - * - * The child can stop itself and send a [[akka.actor.PoisonPill]] to the parent supervisor - * if it wants to do an intentional stop. - * - * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using - * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A - * `Restart` will perform a normal immediate restart of the child. A `Stop` will - * stop the child, but it will be started again after the back-off duration. - * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration - * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. - */ - @deprecated("Use the overloaded one which accepts maxNrOfRetries instead.", "2.5.17") def onStop( childProps: Props, childName: String, minBackoff: java.time.Duration, maxBackoff: java.time.Duration, randomFactor: Double): BackoffOnStopOptions = - onStop(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, -1) - + onStop(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor) } -/** - * Configures a back-off supervisor actor. Start with `Backoff.onStop` or `Backoff.onFailure`. - * BackoffOptions is immutable, so be sure to chain methods like: - * {{{ - * val options = Backoff.onFailure(childProps, childName, minBackoff, maxBackoff, randomFactor) - * .withManualReset - * context.actorOf(BackoffSupervisor.props(options), name) - * }}} - */ @DoNotInherit -trait BackoffOptions { +private[akka] sealed trait ExtendedBackoffOptions[T <: ExtendedBackoffOptions[T]] { + /** * Returns a new BackoffOptions with automatic back-off reset. * The back-off algorithm is reset if the child does not crash within the specified `resetBackoff`. + * * @param resetBackoff The back-off is reset if the child does not crash within this duration. */ - def withAutoReset(resetBackoff: FiniteDuration): BackoffOptions + def withAutoReset(resetBackoff: FiniteDuration): T /** * Returns a new BackoffOptions with manual back-off reset. The back-off is only reset * if the child sends a `BackoffSupervisor.Reset` to its parent (the backoff-supervisor actor). */ - def withManualReset: BackoffOptions + def withManualReset: T /** * Returns a new BackoffOptions with the supervisorStrategy. + * * @param supervisorStrategy the supervisorStrategy that the back-off supervisor will use. - * The default supervisor strategy is used as fallback if the specified supervisorStrategy (its decider) - * does not explicitly handle an exception. As the BackoffSupervisor creates a separate actor to handle the - * backoff process, only a [[OneForOneStrategy]] makes sense here. - * Note that changing the strategy will replace the previously defined maxNrOfRetries. + * The default supervisor strategy is used as fallback if the specified supervisorStrategy (its decider) + * does not explicitly handle an exception. As the BackoffSupervisor creates a separate actor to handle the + * backoff process, only a [[OneForOneStrategy]] makes sense here. + * Note that changing the strategy will replace the previously defined maxNrOfRetries. */ - def withSupervisorStrategy(supervisorStrategy: OneForOneStrategy): BackoffOptions + def withSupervisorStrategy(supervisorStrategy: OneForOneStrategy): T /** * Returns a new BackoffOptions with a default `SupervisorStrategy.stoppingStrategy`. * The default supervisor strategy is used as fallback for throwables not handled by `SupervisorStrategy.stoppingStrategy`. */ - def withDefaultStoppingStrategy: BackoffOptions + def withDefaultStoppingStrategy: T /** * Returns a new BackoffOptions with a maximum number of retries to restart the child actor. * By default, the supervisor will retry infinitely. * With this option, the supervisor will terminate itself after the maxNoOfRetries is reached. + * * @param maxNrOfRetries the number of times a child actor is allowed to be restarted. * If negative, the value is unbounded, otherwise the provided * limit is used. If the limit is exceeded the child actor will be stopped. */ - def withMaxNrOfRetries(maxNrOfRetries: Int): BackoffOptions + def withMaxNrOfRetries(maxNrOfRetries: Int): T /** * Returns a new BackoffOptions with a constant reply to messages that the supervisor receives while its * child is stopped. By default, a message received while the child is stopped is forwarded to `deadLetters`. * With this option, the supervisor will reply to the sender instead. + * * @param replyWhileStopped The message that the supervisor will send in response to all messages while - * its child is stopped. + * its child is stopped. */ - def withReplyWhileStopped(replyWhileStopped: Any): BackoffOptions - - // for backwards compability with 2.5.19 - @deprecated("Use through BackoffOnStopOptions instead of BackoffOptions", since = "2.5.20") - def withFinalStopMessage(isFinalStopMessage: Any ⇒ Boolean): BackoffOptions + def withReplyWhileStopped(replyWhileStopped: Any): T /** * Returns the props to create the back-off supervisor. @@ -577,7 +306,8 @@ trait BackoffOptions { private[akka] def props: Props } -sealed trait BackoffOnStopOptions extends BackoffOptions { +@DoNotInherit +sealed trait BackoffOnStopOptions extends ExtendedBackoffOptions[BackoffOnStopOptions] { /** * Predicate evaluated for each message, if it returns true and the supervised actor is @@ -585,17 +315,13 @@ sealed trait BackoffOnStopOptions extends BackoffOptions { * the supervised actor is running then it will be forwarded to the supervised actor and * when the supervised actor stops its self the supervisor will stop its self. */ - def withFinalStopMessage(isFinalStopMessage: Any ⇒ Boolean): BackoffOptions + def withFinalStopMessage(isFinalStopMessage: Any ⇒ Boolean): BackoffOnStopOptions } -sealed trait BackoffOnFailureOptions extends BackoffOptions { - // for backwards compability with 2.5.19 - @deprecated("This has no effect for backoff on failure", since = "2.5.20") - def withFinalStopMessage(isFinalStopMessage: Any ⇒ Boolean): BackoffOptions -} +@DoNotInherit +sealed trait BackoffOnFailureOptions extends ExtendedBackoffOptions[BackoffOnFailureOptions] -private final case class BackoffOptionsImpl( - backoffType: BackoffType = RestartImpliesFailure, +private final case class BackoffOnStopOptionsImpl[T]( childProps: Props, childName: String, minBackoff: FiniteDuration, @@ -605,7 +331,7 @@ private final case class BackoffOptionsImpl( supervisorStrategy: OneForOneStrategy = OneForOneStrategy()(SupervisorStrategy.defaultStrategy.decider), replyWhileStopped: Option[Any] = None, finalStopMessage: Option[Any ⇒ Boolean] = None -) extends BackoffOptions with BackoffOnStopOptions with BackoffOnFailureOptions { +) extends BackoffOnStopOptions { val backoffReset = reset.getOrElse(AutoReset(minBackoff)) @@ -627,20 +353,43 @@ private final case class BackoffOptionsImpl( case _ ⇒ // ignore } - backoffType match { - //onFailure method in companion object - case RestartImpliesFailure ⇒ - Props(new BackoffOnRestartSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped)) - //onStop method in companion object - case StopImpliesFailure ⇒ - Props(new BackoffOnStopSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped, finalStopMessage)) - } + Props(new BackoffOnStopSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped, finalStopMessage)) } } -private sealed trait BackoffType -private final case object StopImpliesFailure extends BackoffType -private final case object RestartImpliesFailure extends BackoffType +private final case class BackoffOnFailureOptionsImpl[T]( + childProps: Props, + childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + randomFactor: Double, + reset: Option[BackoffReset] = None, + supervisorStrategy: OneForOneStrategy = OneForOneStrategy()(SupervisorStrategy.defaultStrategy.decider), + replyWhileStopped: Option[Any] = None +) extends BackoffOnFailureOptions { + + val backoffReset = reset.getOrElse(AutoReset(minBackoff)) + + def withAutoReset(resetBackoff: FiniteDuration) = copy(reset = Some(AutoReset(resetBackoff))) + def withManualReset = copy(reset = Some(ManualReset)) + def withSupervisorStrategy(supervisorStrategy: OneForOneStrategy) = copy(supervisorStrategy = supervisorStrategy) + def withDefaultStoppingStrategy = copy(supervisorStrategy = OneForOneStrategy(supervisorStrategy.maxNrOfRetries)(SupervisorStrategy.stoppingStrategy.decider)) + def withReplyWhileStopped(replyWhileStopped: Any) = copy(replyWhileStopped = Some(replyWhileStopped)) + def withMaxNrOfRetries(maxNrOfRetries: Int) = copy(supervisorStrategy = supervisorStrategy.withMaxNrOfRetries(maxNrOfRetries)) + + def props = { + require(minBackoff > Duration.Zero, "minBackoff must be > 0") + require(maxBackoff >= minBackoff, "maxBackoff must be >= minBackoff") + require(0.0 <= randomFactor && randomFactor <= 1.0, "randomFactor must be between 0.0 and 1.0") + backoffReset match { + case AutoReset(resetBackoff) ⇒ + require(minBackoff <= resetBackoff && resetBackoff <= maxBackoff) + case _ ⇒ // ignore + } + + Props(new BackoffOnRestartSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped)) + } +} private[akka] sealed trait BackoffReset private[akka] final case object ManualReset extends BackoffReset diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala index 1e848ba76e..8afb50f4f6 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala @@ -32,6 +32,7 @@ object BackoffSupervisor { * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. * In order to skip this additional delay pass in `0`. */ + @deprecated("Use props with BackoffOptions instead", since = "2.5.20") def props( childProps: Props, childName: String, @@ -61,6 +62,7 @@ object BackoffSupervisor { * The supervisor will terminate itself after the maxNoOfRetries is reached. * In order to restart infinitely pass in `-1`. */ + @deprecated("Use props with BackoffOptions instead", since = "2.5.20") def props( childProps: Props, childName: String, @@ -92,6 +94,7 @@ object BackoffSupervisor { * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. * In order to skip this additional delay pass in `0`. */ + @deprecated("Use props with BackoffOptions instead", since = "2.5.20") def props( childProps: Props, childName: String, @@ -121,6 +124,7 @@ object BackoffSupervisor { * The supervisor will terminate itself after the maxNoOfRetries is reached. * In order to restart infinitely pass in `-1`. */ + @deprecated("Use props with BackoffOptions instead", since = "2.5.20") def props( childProps: Props, childName: String, @@ -152,6 +156,7 @@ object BackoffSupervisor { * in the child. As the BackoffSupervisor creates a separate actor to handle the * backoff process, only a [[OneForOneStrategy]] makes sense here. */ + @deprecated("Use props with BackoffOptions instead", since = "2.5.20") def propsWithSupervisorStrategy( childProps: Props, childName: String, @@ -186,6 +191,7 @@ object BackoffSupervisor { * in the child. As the BackoffSupervisor creates a separate actor to handle the * backoff process, only a [[OneForOneStrategy]] makes sense here. */ + @deprecated("Use props with BackoffOptions instead", since = "2.5.20") def propsWithSupervisorStrategy( childProps: Props, childName: String, @@ -201,8 +207,23 @@ object BackoffSupervisor { * * @param options the [[BackoffOptions]] that specify how to construct a backoff-supervisor. */ + @deprecated("Use new API from BackoffOptions object instead", since = "2.5.20") def props(options: BackoffOptions): Props = options.props + /** + * Props for creating a `BackoffSupervisor` actor from [[BackoffOnStopOptions]]. + * + * @param options the [[BackoffOnStopOptions]] that specify how to construct a backoff-supervisor. + */ + def props(options: BackoffOnStopOptions): Props = options.props + + /** + * Props for creating a `BackoffSupervisor` actor from [[BackoffOnFailureOptions]]. + * + * @param options the [[BackoffOnFailureOptions]] that specify how to construct a backoff-supervisor. + */ + def props(options: BackoffOnFailureOptions): Props = options.props + /** * Send this message to the `BackoffSupervisor` and it will reply with * [[BackoffSupervisor.CurrentChild]] containing the `ActorRef` of the current child, if any. From 9f3b62a367cc75b3ad3243badc5a4ac50e97b60a Mon Sep 17 00:00:00 2001 From: Nicolas Vollmar Date: Fri, 18 Jan 2019 13:09:14 +0100 Subject: [PATCH 06/11] Updates backoff supervision documentation #26156 --- .../pattern/BackoffSupervisorDocSpec.scala | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala b/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala index 5e9d2efa98..0814829cf9 100644 --- a/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala +++ b/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala @@ -4,8 +4,8 @@ package docs.pattern -import akka.actor.{ ActorSystem, Props, OneForOneStrategy, SupervisorStrategy } -import akka.pattern.{ Backoff, BackoffSupervisor } +import akka.actor.{ ActorSystem, OneForOneStrategy, Props, SupervisorStrategy } +import akka.pattern.{ BackoffOpts, BackoffSupervisor } import akka.testkit.TestActors.EchoActor class BackoffSupervisorDocSpec { @@ -18,13 +18,12 @@ class BackoffSupervisorDocSpec { val childProps = Props(classOf[EchoActor]) val supervisor = BackoffSupervisor.props( - Backoff.onStop( + BackoffOpts.onStop( childProps, childName = "myEcho", minBackoff = 3.seconds, maxBackoff = 30.seconds, - randomFactor = 0.2, // adds 20% "noise" to vary the intervals slightly - maxNrOfRetries = -1 + randomFactor = 0.2 // adds 20% "noise" to vary the intervals slightly )) system.actorOf(supervisor, name = "echoSupervisor") @@ -39,13 +38,12 @@ class BackoffSupervisorDocSpec { val childProps = Props(classOf[EchoActor]) val supervisor = BackoffSupervisor.props( - Backoff.onFailure( + BackoffOpts.onFailure( childProps, childName = "myEcho", minBackoff = 3.seconds, maxBackoff = 30.seconds, - randomFactor = 0.2, // adds 20% "noise" to vary the intervals slightly - maxNrOfRetries = -1 + randomFactor = 0.2 // adds 20% "noise" to vary the intervals slightly )) system.actorOf(supervisor, name = "echoSupervisor") @@ -60,13 +58,12 @@ class BackoffSupervisorDocSpec { //#backoff-custom-stop val supervisor = BackoffSupervisor.props( - Backoff.onStop( + BackoffOpts.onStop( childProps, childName = "myEcho", minBackoff = 3.seconds, maxBackoff = 30.seconds, - randomFactor = 0.2, // adds 20% "noise" to vary the intervals slightly - maxNrOfRetries = -1 + randomFactor = 0.2 // adds 20% "noise" to vary the intervals slightly ).withManualReset // the child must send BackoffSupervisor.Reset to its parent .withDefaultStoppingStrategy // Stop at any Exception thrown ) @@ -83,13 +80,12 @@ class BackoffSupervisorDocSpec { //#backoff-custom-fail val supervisor = BackoffSupervisor.props( - Backoff.onFailure( + BackoffOpts.onFailure( childProps, childName = "myEcho", minBackoff = 3.seconds, maxBackoff = 30.seconds, - randomFactor = 0.2, // adds 20% "noise" to vary the intervals slightly - maxNrOfRetries = -1 + randomFactor = 0.2 // adds 20% "noise" to vary the intervals slightly ).withAutoReset(10.seconds) // reset if the child does not throw any errors within 10 seconds .withSupervisorStrategy( OneForOneStrategy() { From 9233ff5a408bbe8c85456e738b3c2189b31d0ef7 Mon Sep 17 00:00:00 2001 From: Nicolas Vollmar Date: Fri, 18 Jan 2019 20:44:47 +0100 Subject: [PATCH 07/11] Adapts and improves documentation #26156 --- .../pattern/BackoffOnRestartSupervisor.scala | 2 +- .../pattern/BackoffOnStopSupervisor.scala | 2 +- .../scala/akka/pattern/BackoffOptions.scala | 8 ++-- .../cluster/sharding/SupervisionSpec.scala | 38 ++++++++++++++++++- .../src/main/paradox/cluster-sharding.md | 2 + .../src/main/paradox/general/supervision.md | 35 ++++++++++++++++- .../pattern/BackoffSupervisorDocTest.java | 5 ++- .../pattern/BackoffSupervisorDocSpec.scala | 26 ++++++++++++- 8 files changed, 106 insertions(+), 12 deletions(-) diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala index 1bf8d8a03d..87ede87c63 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala @@ -15,7 +15,7 @@ import scala.concurrent.duration._ * * Back-off supervisor that stops and starts a child actor when the child actor restarts. * This back-off supervisor is created by using ``akka.pattern.BackoffSupervisor.props`` - * with ``akka.pattern.Backoff.onFailure``. + * with ``akka.pattern.BackoffOpts.onFailure``. */ @InternalApi private class BackoffOnRestartSupervisor( val childProps: Props, diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala index abd4623228..666d2a8293 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala @@ -15,7 +15,7 @@ import scala.concurrent.duration.FiniteDuration * * Back-off supervisor that stops and starts a child actor using a back-off algorithm when the child actor stops. * This back-off supervisor is created by using `akka.pattern.BackoffSupervisor.props` - * with `Backoff.onStop`. + * with `BackoffOpts.onStop`. */ @InternalApi private[akka] class BackoffOnStopSupervisor( val childProps: Props, diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala index 28dcda8c3c..6dbd839e63 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala @@ -333,7 +333,7 @@ private final case class BackoffOnStopOptionsImpl[T]( finalStopMessage: Option[Any ⇒ Boolean] = None ) extends BackoffOnStopOptions { - val backoffReset = reset.getOrElse(AutoReset(minBackoff)) + private val backoffReset = reset.getOrElse(AutoReset(minBackoff)) def withAutoReset(resetBackoff: FiniteDuration) = copy(reset = Some(AutoReset(resetBackoff))) def withManualReset = copy(reset = Some(ManualReset)) @@ -343,7 +343,7 @@ private final case class BackoffOnStopOptionsImpl[T]( def withMaxNrOfRetries(maxNrOfRetries: Int) = copy(supervisorStrategy = supervisorStrategy.withMaxNrOfRetries(maxNrOfRetries)) def withFinalStopMessage(action: Any ⇒ Boolean) = copy(finalStopMessage = Some(action)) - def props = { + def props: Props = { require(minBackoff > Duration.Zero, "minBackoff must be > 0") require(maxBackoff >= minBackoff, "maxBackoff must be >= minBackoff") require(0.0 <= randomFactor && randomFactor <= 1.0, "randomFactor must be between 0.0 and 1.0") @@ -368,7 +368,7 @@ private final case class BackoffOnFailureOptionsImpl[T]( replyWhileStopped: Option[Any] = None ) extends BackoffOnFailureOptions { - val backoffReset = reset.getOrElse(AutoReset(minBackoff)) + private val backoffReset = reset.getOrElse(AutoReset(minBackoff)) def withAutoReset(resetBackoff: FiniteDuration) = copy(reset = Some(AutoReset(resetBackoff))) def withManualReset = copy(reset = Some(ManualReset)) @@ -377,7 +377,7 @@ private final case class BackoffOnFailureOptionsImpl[T]( def withReplyWhileStopped(replyWhileStopped: Any) = copy(replyWhileStopped = Some(replyWhileStopped)) def withMaxNrOfRetries(maxNrOfRetries: Int) = copy(supervisorStrategy = supervisorStrategy.withMaxNrOfRetries(maxNrOfRetries)) - def props = { + def props: Props = { require(minBackoff > Duration.Zero, "minBackoff must be > 0") require(maxBackoff >= minBackoff, "maxBackoff must be >= minBackoff") require(0.0 <= randomFactor && randomFactor <= 1.0, "randomFactor must be between 0.0 and 1.0") diff --git a/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/SupervisionSpec.scala b/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/SupervisionSpec.scala index 1159f65cfb..9f37e60281 100644 --- a/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/SupervisionSpec.scala +++ b/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/SupervisionSpec.scala @@ -7,7 +7,7 @@ package akka.cluster.sharding import akka.actor.{ Actor, ActorLogging, ActorRef, PoisonPill, Props } import akka.cluster.Cluster import akka.cluster.sharding.ShardRegion.Passivate -import akka.pattern.{ Backoff, BackoffSupervisor } +import akka.pattern.{ Backoff, BackoffOpts, BackoffSupervisor } import akka.testkit.{ AkkaSpec, ImplicitSender } import com.typesafe.config.ConfigFactory @@ -64,7 +64,7 @@ class SupervisionSpec extends AkkaSpec(SupervisionSpec.config) with ImplicitSend import SupervisionSpec._ - "Supervision for a sharded actor" must { + "Supervision for a sharded actor (deprecated)" must { "allow passivation" in { @@ -99,4 +99,38 @@ class SupervisionSpec extends AkkaSpec(SupervisionSpec.config) with ImplicitSend } } + "Supervision for a sharded actor" must { + + "allow passivation" in { + + val supervisedProps = BackoffSupervisor.props(BackoffOpts.onStop( + Props(new PassivatingActor()), + childName = "child", + minBackoff = 1.seconds, + maxBackoff = 30.seconds, + randomFactor = 0.2 + ).withFinalStopMessage(_ == StopMessage)) + + Cluster(system).join(Cluster(system).selfAddress) + val region = ClusterSharding(system).start( + "passy", + supervisedProps, + ClusterShardingSettings(system), + idExtractor, + shardResolver + ) + + region ! Msg(10, "hello") + val response = expectMsgType[Response](5.seconds) + watch(response.self) + + region ! Msg(10, "passivate") + expectTerminated(response.self) + + // This would fail before as sharded actor would be stuck passivating + region ! Msg(10, "hello") + expectMsgType[Response](20.seconds) + } + } + } diff --git a/akka-docs/src/main/paradox/cluster-sharding.md b/akka-docs/src/main/paradox/cluster-sharding.md index 4d7ef2b490..239e7e4fd8 100644 --- a/akka-docs/src/main/paradox/cluster-sharding.md +++ b/akka-docs/src/main/paradox/cluster-sharding.md @@ -413,6 +413,8 @@ Java Note that stopped entities will be started again when a new message is targeted to the entity. +If 'on stop' backoff supervision strategy is used, a final termination message must be set and used for passivation, see #ref:[Supervision](general/supervision.md#Sharding) + ## Graceful Shutdown You can send the @scala[`ShardRegion.GracefulShutdown`] @java[`ShardRegion.gracefulShutdownInstance`] message diff --git a/akka-docs/src/main/paradox/general/supervision.md b/akka-docs/src/main/paradox/general/supervision.md index 7f6c84daf9..4d5373101b 100644 --- a/akka-docs/src/main/paradox/general/supervision.md +++ b/akka-docs/src/main/paradox/general/supervision.md @@ -207,6 +207,25 @@ to recover before the persistent actor is started. > [1] A failure can be indicated in two different ways; by an actor stopping or crashing. +#### Supervision strategies + +There are two basic supervision strategies available for backoff: +* 'On failure': The supervisor will restart the supervised actor once it crashes, but terminate if the actor stops normaly (e.g. through `context.stop`) +* 'On stop': The supervisor will restart the supervised actor if it terminates in any way (consider this for `PersistentActor` since they stop on persistence failures instead of crashing) + +#### Sharding +If the 'on stop' strategy is used for sharded actors a final termination message should be configured and used to terminate the actor on passivation. Otherwise the supervisor will just restart the actor again. + +The termination message is configured with: + +@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-sharded } + +And must be used for passivation: + +@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-sharded-passivation } + +#### Simple backoff + The following Scala snippet shows how to create a backoff supervisor which will start the given echo actor after it has stopped because of a failure, in increasing intervals of 3, 6, 12, 24 and finally 30 seconds: @@ -239,7 +258,21 @@ The above is equivalent to this Java code: @@snip [BackoffSupervisorDocTest.java](/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java) { #backoff-fail } -The `akka.pattern.BackoffOptions` can be used to customize the behavior of the back-off supervisor actor, below are some examples: +#### Customization + +The `akka.pattern.BackoffOnFailureOptions` and `akka.pattern.BackoffOnRestartOptions` can be used to customize the behavior of the back-off supervisor actor. +Options are: +* `withAutoReset`: The backoff is reset if no failure/stop occurs within the duration. This is the default behaviour with `minBackoff` as default value +* `withManualReset`: The child must send `BackoffSupervisor.Reset` to its backoff supervisor (parent) +* `withSupervisionStrategy`: Sets a custom `OneForOneStrategy` (as each backoff supervisor only has one child). The default strategy uses the `akka.actor.SupervisorStrategy.defaultDecider` which restarts on exceptions. +* `withDefaultStoppingStrategy`: Sets a `OneForOneStrategy` with the stopping decider that stops the child on all exceptions. +* `withMaxNrOfRetries`: Sets the maximum number of retries until the supervisor will give up (`-1` is default which means no limit of retries). Note: This is set on the supervision strategy, so setting a different strategy resets the `maxNrOfRetries`. +* `withReplyWhileStopped`: By default all messages received while the child is stopped are forwarded to dead letters. With this set, the supervisor will reply to the sender instead. + +Only available on `BackoffOnStopOptions`: +* `withFinalStopMessage`: Allows to define a predicate to decide on finally stopping the child (and supervisor). Used for passivate sharded actors - see above. + +Some examples: @@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-custom-stop } diff --git a/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java b/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java index e1b68b421b..5f7755837f 100644 --- a/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java +++ b/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java @@ -6,6 +6,7 @@ package jdocs.pattern; import akka.actor.*; import akka.pattern.Backoff; +import akka.pattern.BackoffOpts; import akka.pattern.BackoffSupervisor; import akka.testkit.TestActors.EchoActor; // #backoff-imports @@ -20,7 +21,7 @@ public class BackoffSupervisorDocTest { final Props supervisorProps = BackoffSupervisor.props( - Backoff.onStop( + BackoffOpts.onStop( childProps, "myEcho", Duration.ofSeconds(3), @@ -37,7 +38,7 @@ public class BackoffSupervisorDocTest { final Props supervisorProps = BackoffSupervisor.props( - Backoff.onFailure( + BackoffOpts.onFailure( childProps, "myEcho", Duration.ofSeconds(3), diff --git a/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala b/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala index 0814829cf9..c2f1893bb3 100644 --- a/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala +++ b/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala @@ -4,7 +4,8 @@ package docs.pattern -import akka.actor.{ ActorSystem, OneForOneStrategy, Props, SupervisorStrategy } +import akka.actor.{ ActorContext, ActorSystem, OneForOneStrategy, Props, SupervisorStrategy } +import akka.cluster.sharding.ShardRegion.Passivate import akka.pattern.{ BackoffOpts, BackoffSupervisor } import akka.testkit.TestActors.EchoActor @@ -99,4 +100,27 @@ class BackoffSupervisorDocSpec { case class MyException(msg: String) extends Exception(msg) + case object StopMessage + + class BackoffSupervisorDocSpecExampleSharding { + val system: ActorSystem = ??? + val context: ActorContext = ??? + import scala.concurrent.duration._ + + val childProps = Props(classOf[EchoActor]) + + //#backoff-sharded + val supervisor = BackoffSupervisor.props(BackoffOpts.onStop( + childProps, + childName = "myEcho", + minBackoff = 3.seconds, + maxBackoff = 30.seconds, + randomFactor = 0.2 + ).withFinalStopMessage(_ == StopMessage)) + //#backoff-sharded + + //#backoff-sharded-passivation + context.parent ! Passivate(StopMessage) + //#backoff-sharded-passivation + } } From 54232d785a9479df746ff3cbbf6139e194250fe3 Mon Sep 17 00:00:00 2001 From: Nicolas Vollmar Date: Sat, 19 Jan 2019 12:21:59 +0100 Subject: [PATCH 08/11] Removes stopping strategy from failure options #26156 --- .../scala/akka/pattern/BackoffOptions.scala | 19 +++++++++++-------- .../src/main/paradox/general/supervision.md | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala index 6dbd839e63..d63d1826f6 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala @@ -273,12 +273,6 @@ private[akka] sealed trait ExtendedBackoffOptions[T <: ExtendedBackoffOptions[T] */ def withSupervisorStrategy(supervisorStrategy: OneForOneStrategy): T - /** - * Returns a new BackoffOptions with a default `SupervisorStrategy.stoppingStrategy`. - * The default supervisor strategy is used as fallback for throwables not handled by `SupervisorStrategy.stoppingStrategy`. - */ - def withDefaultStoppingStrategy: T - /** * Returns a new BackoffOptions with a maximum number of retries to restart the child actor. * By default, the supervisor will retry infinitely. @@ -309,6 +303,12 @@ private[akka] sealed trait ExtendedBackoffOptions[T <: ExtendedBackoffOptions[T] @DoNotInherit sealed trait BackoffOnStopOptions extends ExtendedBackoffOptions[BackoffOnStopOptions] { + /** + * Returns a new BackoffOptions with a default `SupervisorStrategy.stoppingStrategy`. + * The default supervisor strategy is used as fallback for throwables not handled by `SupervisorStrategy.stoppingStrategy`. + */ + def withDefaultStoppingStrategy: BackoffOnStopOptions + /** * Predicate evaluated for each message, if it returns true and the supervised actor is * stopped then the supervisor will stop its self. If it returns true while @@ -335,12 +335,15 @@ private final case class BackoffOnStopOptionsImpl[T]( private val backoffReset = reset.getOrElse(AutoReset(minBackoff)) + // default def withAutoReset(resetBackoff: FiniteDuration) = copy(reset = Some(AutoReset(resetBackoff))) def withManualReset = copy(reset = Some(ManualReset)) def withSupervisorStrategy(supervisorStrategy: OneForOneStrategy) = copy(supervisorStrategy = supervisorStrategy) - def withDefaultStoppingStrategy = copy(supervisorStrategy = OneForOneStrategy(supervisorStrategy.maxNrOfRetries)(SupervisorStrategy.stoppingStrategy.decider)) def withReplyWhileStopped(replyWhileStopped: Any) = copy(replyWhileStopped = Some(replyWhileStopped)) def withMaxNrOfRetries(maxNrOfRetries: Int) = copy(supervisorStrategy = supervisorStrategy.withMaxNrOfRetries(maxNrOfRetries)) + + // additional + def withDefaultStoppingStrategy = copy(supervisorStrategy = OneForOneStrategy(supervisorStrategy.maxNrOfRetries)(SupervisorStrategy.stoppingStrategy.decider)) def withFinalStopMessage(action: Any ⇒ Boolean) = copy(finalStopMessage = Some(action)) def props: Props = { @@ -370,10 +373,10 @@ private final case class BackoffOnFailureOptionsImpl[T]( private val backoffReset = reset.getOrElse(AutoReset(minBackoff)) + // default def withAutoReset(resetBackoff: FiniteDuration) = copy(reset = Some(AutoReset(resetBackoff))) def withManualReset = copy(reset = Some(ManualReset)) def withSupervisorStrategy(supervisorStrategy: OneForOneStrategy) = copy(supervisorStrategy = supervisorStrategy) - def withDefaultStoppingStrategy = copy(supervisorStrategy = OneForOneStrategy(supervisorStrategy.maxNrOfRetries)(SupervisorStrategy.stoppingStrategy.decider)) def withReplyWhileStopped(replyWhileStopped: Any) = copy(replyWhileStopped = Some(replyWhileStopped)) def withMaxNrOfRetries(maxNrOfRetries: Int) = copy(supervisorStrategy = supervisorStrategy.withMaxNrOfRetries(maxNrOfRetries)) diff --git a/akka-docs/src/main/paradox/general/supervision.md b/akka-docs/src/main/paradox/general/supervision.md index 4d5373101b..89cecb9325 100644 --- a/akka-docs/src/main/paradox/general/supervision.md +++ b/akka-docs/src/main/paradox/general/supervision.md @@ -265,11 +265,11 @@ Options are: * `withAutoReset`: The backoff is reset if no failure/stop occurs within the duration. This is the default behaviour with `minBackoff` as default value * `withManualReset`: The child must send `BackoffSupervisor.Reset` to its backoff supervisor (parent) * `withSupervisionStrategy`: Sets a custom `OneForOneStrategy` (as each backoff supervisor only has one child). The default strategy uses the `akka.actor.SupervisorStrategy.defaultDecider` which restarts on exceptions. -* `withDefaultStoppingStrategy`: Sets a `OneForOneStrategy` with the stopping decider that stops the child on all exceptions. * `withMaxNrOfRetries`: Sets the maximum number of retries until the supervisor will give up (`-1` is default which means no limit of retries). Note: This is set on the supervision strategy, so setting a different strategy resets the `maxNrOfRetries`. * `withReplyWhileStopped`: By default all messages received while the child is stopped are forwarded to dead letters. With this set, the supervisor will reply to the sender instead. Only available on `BackoffOnStopOptions`: +* `withDefaultStoppingStrategy`: Sets a `OneForOneStrategy` with the stopping decider that stops the child on all exceptions. * `withFinalStopMessage`: Allows to define a predicate to decide on finally stopping the child (and supervisor). Used for passivate sharded actors - see above. Some examples: From f4b6e308103517c0b5ec019b66b83faef4762ee7 Mon Sep 17 00:00:00 2001 From: Nicolas Vollmar Date: Wed, 6 Mar 2019 14:14:44 +0100 Subject: [PATCH 09/11] Small fixes/improvements #26156 --- .../src/main/mima-filters/2.5.21.backwards.excludes | 8 ++++++++ .../src/main/mima-filters/2.5.22.backwards.excludes | 7 ------- akka-actor/src/main/scala/akka/pattern/Backoff.scala | 2 +- akka-docs/src/main/paradox/cluster-sharding.md | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) delete mode 100644 akka-actor/src/main/mima-filters/2.5.22.backwards.excludes diff --git a/akka-actor/src/main/mima-filters/2.5.21.backwards.excludes b/akka-actor/src/main/mima-filters/2.5.21.backwards.excludes index ed4a04254e..71fbb8f8da 100644 --- a/akka-actor/src/main/mima-filters/2.5.21.backwards.excludes +++ b/akka-actor/src/main/mima-filters/2.5.21.backwards.excludes @@ -4,3 +4,11 @@ ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.AskableActorRef ProblemFilters.exclude[DirectMissingMethodProblem]("akka.util.ccompat.package.fromCanBuildFrom") # Add optional field to ApiMayChange annotation #26409 ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.annotation.ApiMayChange.issue") + +# Simplify backoff supervision API #19016 +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestartSupervisor.replyWhileStopped") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestartSupervisor.finalStopMessage") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestartSupervisor.this") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.HandleBackoff.replyWhileStopped") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.HandleBackoff.finalStopMessage") +ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.pattern.HandleBackoff.handleMessageToChild") \ No newline at end of file diff --git a/akka-actor/src/main/mima-filters/2.5.22.backwards.excludes b/akka-actor/src/main/mima-filters/2.5.22.backwards.excludes deleted file mode 100644 index d0b5506993..0000000000 --- a/akka-actor/src/main/mima-filters/2.5.22.backwards.excludes +++ /dev/null @@ -1,7 +0,0 @@ -# Simplify backoff supervision API #19016 -ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestartSupervisor.replyWhileStopped") -ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestartSupervisor.finalStopMessage") -ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestartSupervisor.this") -ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.HandleBackoff.replyWhileStopped") -ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.HandleBackoff.finalStopMessage") -ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.pattern.HandleBackoff.handleMessageToChild") \ No newline at end of file diff --git a/akka-actor/src/main/scala/akka/pattern/Backoff.scala b/akka-actor/src/main/scala/akka/pattern/Backoff.scala index a127760571..bfcbe61a61 100644 --- a/akka-actor/src/main/scala/akka/pattern/Backoff.scala +++ b/akka-actor/src/main/scala/akka/pattern/Backoff.scala @@ -517,7 +517,7 @@ object Backoff { */ @DoNotInherit @Deprecated -@deprecated("Use new API from BackoffOptions object instead", since = "2.5.20") +@deprecated("Use new API from BackoffOptions object instead", since = "2.5.22") trait BackoffOptions { /** diff --git a/akka-docs/src/main/paradox/cluster-sharding.md b/akka-docs/src/main/paradox/cluster-sharding.md index 239e7e4fd8..bc34ceaff5 100644 --- a/akka-docs/src/main/paradox/cluster-sharding.md +++ b/akka-docs/src/main/paradox/cluster-sharding.md @@ -413,7 +413,7 @@ Java Note that stopped entities will be started again when a new message is targeted to the entity. -If 'on stop' backoff supervision strategy is used, a final termination message must be set and used for passivation, see #ref:[Supervision](general/supervision.md#Sharding) +If 'on stop' backoff supervision strategy is used, a final termination message must be set and used for passivation, see @ref:[Supervision](general/supervision.md#Sharding) ## Graceful Shutdown From 4bf1ba0bf0c79feee9ee8688d96ca85a7482819f Mon Sep 17 00:00:00 2001 From: Nicolas Vollmar Date: Thu, 7 Mar 2019 09:58:31 +0100 Subject: [PATCH 10/11] Updates deprecations and documentation #26156 --- .../src/main/scala/akka/pattern/Backoff.scala | 18 +++++++++--------- .../scala/akka/pattern/BackoffOptions.scala | 3 +++ .../scala/akka/pattern/BackoffSupervisor.scala | 16 ++++++++-------- .../src/main/paradox/general/supervision.md | 1 + akka-docs/src/main/paradox/persistence.md | 2 ++ .../persistence/LambdaPersistenceDocTest.java | 7 +++++-- .../docs/persistence/PersistenceDocSpec.scala | 11 +++++------ 7 files changed, 33 insertions(+), 25 deletions(-) diff --git a/akka-actor/src/main/scala/akka/pattern/Backoff.scala b/akka-actor/src/main/scala/akka/pattern/Backoff.scala index bfcbe61a61..938995518c 100644 --- a/akka-actor/src/main/scala/akka/pattern/Backoff.scala +++ b/akka-actor/src/main/scala/akka/pattern/Backoff.scala @@ -11,10 +11,10 @@ import akka.util.JavaDurationConverters._ import scala.concurrent.duration.{ Duration, FiniteDuration } /** - * @deprecated This API is superseded by the [[BackoffOptions]] object. + * @deprecated This API is superseded by the [[BackoffOpts]] object. */ @Deprecated -@deprecated("Use new API from BackoffOptions object instead", since = "2.5.20") +@deprecated("Use new API from BackoffOpts object instead", since = "2.5.22") object Backoff { /** * Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. @@ -66,7 +66,7 @@ object Backoff { * In order to restart infinitely pass in `-1`. * */ - @deprecated("Use BackoffOptions.onFailure instead", "2.5.20") + @deprecated("Use BackoffOpts.onFailure instead", "2.5.22") def onFailure( childProps: Props, childName: String, @@ -122,7 +122,7 @@ object Backoff { * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. * In order to skip this additional delay pass in `0`. */ - @deprecated("Use BackoffOptions.onFailure instead", "2.5.20") + @deprecated("Use BackoffOpts.onFailure instead", "2.5.22") def onFailure( childProps: Props, childName: String, @@ -181,7 +181,7 @@ object Backoff { * In order to restart infinitely pass in `-1`. */ @Deprecated - @deprecated("Use BackoffOptions.onFailure instead", "2.5.20") + @deprecated("Use BackoffOpts.onFailure instead", "2.5.22") def onFailure( childProps: Props, childName: String, @@ -303,7 +303,7 @@ object Backoff { * The supervisor will terminate itself after the maxNoOfRetries is reached. * In order to restart infinitely pass in `-1`. */ - @deprecated("Use BackoffOptions.onStop instead", "2.5.20") + @deprecated("Use BackoffOpts.onStop instead", "2.5.22") def onStop( childProps: Props, childName: String, @@ -366,7 +366,7 @@ object Backoff { * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. * In order to skip this additional delay pass in `0`. */ - @deprecated("Use BackoffOptions.onStop instead", "2.5.20") + @deprecated("Use BackoffOpts.onStop instead", "2.5.22") def onStop( childProps: Props, childName: String, @@ -432,7 +432,7 @@ object Backoff { * In order to restart infinitely pass in `-1`. */ @Deprecated - @deprecated("Use BackoffOptions.onStop instead", "2.5.20") + @deprecated("Use BackoffOpts.onStop instead", "2.5.22") def onStop( childProps: Props, childName: String, @@ -517,7 +517,7 @@ object Backoff { */ @DoNotInherit @Deprecated -@deprecated("Use new API from BackoffOptions object instead", since = "2.5.22") +@deprecated("Use new API from BackoffOpts object instead", since = "2.5.22") trait BackoffOptions { /** diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala index d63d1826f6..03f79517eb 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala @@ -245,6 +245,9 @@ object BackoffOpts { onStop(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor) } +/** + * Not for user extension + */ @DoNotInherit private[akka] sealed trait ExtendedBackoffOptions[T <: ExtendedBackoffOptions[T]] { diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala index 8afb50f4f6..08ccaafc00 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala @@ -32,7 +32,7 @@ object BackoffSupervisor { * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. * In order to skip this additional delay pass in `0`. */ - @deprecated("Use props with BackoffOptions instead", since = "2.5.20") + @deprecated("Use props with BackoffOpts instead", since = "2.5.22") def props( childProps: Props, childName: String, @@ -62,7 +62,7 @@ object BackoffSupervisor { * The supervisor will terminate itself after the maxNoOfRetries is reached. * In order to restart infinitely pass in `-1`. */ - @deprecated("Use props with BackoffOptions instead", since = "2.5.20") + @deprecated("Use props with BackoffOpts instead", since = "2.5.22") def props( childProps: Props, childName: String, @@ -94,7 +94,7 @@ object BackoffSupervisor { * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. * In order to skip this additional delay pass in `0`. */ - @deprecated("Use props with BackoffOptions instead", since = "2.5.20") + @deprecated("Use props with BackoffOpts instead", since = "2.5.22") def props( childProps: Props, childName: String, @@ -124,7 +124,7 @@ object BackoffSupervisor { * The supervisor will terminate itself after the maxNoOfRetries is reached. * In order to restart infinitely pass in `-1`. */ - @deprecated("Use props with BackoffOptions instead", since = "2.5.20") + @deprecated("Use props with BackoffOpts instead", since = "2.5.22") def props( childProps: Props, childName: String, @@ -156,7 +156,7 @@ object BackoffSupervisor { * in the child. As the BackoffSupervisor creates a separate actor to handle the * backoff process, only a [[OneForOneStrategy]] makes sense here. */ - @deprecated("Use props with BackoffOptions instead", since = "2.5.20") + @deprecated("Use props with BackoffOpts instead", since = "2.5.22") def propsWithSupervisorStrategy( childProps: Props, childName: String, @@ -191,7 +191,7 @@ object BackoffSupervisor { * in the child. As the BackoffSupervisor creates a separate actor to handle the * backoff process, only a [[OneForOneStrategy]] makes sense here. */ - @deprecated("Use props with BackoffOptions instead", since = "2.5.20") + @deprecated("Use props with BackoffOpts instead", since = "2.5.22") def propsWithSupervisorStrategy( childProps: Props, childName: String, @@ -207,7 +207,7 @@ object BackoffSupervisor { * * @param options the [[BackoffOptions]] that specify how to construct a backoff-supervisor. */ - @deprecated("Use new API from BackoffOptions object instead", since = "2.5.20") + @deprecated("Use new API from BackoffOpts object instead", since = "2.5.22") def props(options: BackoffOptions): Props = options.props /** @@ -299,7 +299,7 @@ object BackoffSupervisor { } // for backwards compability -@deprecated("Use `BackoffSupervisor.props` method instead", since = "2.5.20") +@deprecated("Use `BackoffSupervisor.props` method instead", since = "2.5.22") final class BackoffSupervisor( override val childProps: Props, override val childName: String, diff --git a/akka-docs/src/main/paradox/general/supervision.md b/akka-docs/src/main/paradox/general/supervision.md index 89cecb9325..58d102e632 100644 --- a/akka-docs/src/main/paradox/general/supervision.md +++ b/akka-docs/src/main/paradox/general/supervision.md @@ -207,6 +207,7 @@ to recover before the persistent actor is started. > [1] A failure can be indicated in two different ways; by an actor stopping or crashing. + #### Supervision strategies There are two basic supervision strategies available for backoff: diff --git a/akka-docs/src/main/paradox/persistence.md b/akka-docs/src/main/paradox/persistence.md index 2aafd25a10..6f15c4505b 100644 --- a/akka-docs/src/main/paradox/persistence.md +++ b/akka-docs/src/main/paradox/persistence.md @@ -466,6 +466,8 @@ Scala Java : @@snip [LambdaPersistenceDocTest.java](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java) { #backoff } +See @ref:[Supervision strategies](general/supervision.md#supervision-strategies) for more details about actor supervision. + If persistence of an event is rejected before it is stored, e.g. due to serialization error, `onPersistRejected` will be invoked (logging a warning by default), and the actor continues with next message. diff --git a/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java b/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java index a2f01bf42e..7307148caf 100644 --- a/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java +++ b/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java @@ -6,11 +6,12 @@ package jdocs.persistence; import akka.actor.*; import akka.japi.Procedure; +import akka.pattern.BackoffOpts; import akka.pattern.BackoffSupervisor; import akka.persistence.*; -import java.time.Duration; import java.io.Serializable; +import java.time.Duration; import java.util.Optional; public class LambdaPersistenceDocTest { @@ -20,6 +21,7 @@ public class LambdaPersistenceDocTest { public interface PersistentActorMethods { // #persistence-id public String persistenceId(); + // #persistence-id // #recovery-status public boolean recoveryRunning(); @@ -136,7 +138,8 @@ public class LambdaPersistenceDocTest { final Props childProps = Props.create(MyPersistentActor1.class); final Props props = BackoffSupervisor.props( - childProps, "myActor", Duration.ofSeconds(3), Duration.ofSeconds(30), 0.2); + BackoffOpts.onStop( + childProps, "myActor", Duration.ofSeconds(3), Duration.ofSeconds(30), 0.2)); getContext().actorOf(props, "mySupervisor"); super.preStart(); } diff --git a/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala b/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala index d2341d89e9..5fe99ebdd9 100644 --- a/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala +++ b/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala @@ -5,10 +5,10 @@ package docs.persistence import akka.actor._ -import akka.pattern.{ Backoff, BackoffSupervisor } +import akka.pattern.{ Backoff, BackoffOpts, BackoffSupervisor } import akka.persistence._ import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{ Source, Sink, Flow } +import akka.stream.scaladsl.{ Flow, Sink, Source } import scala.concurrent.duration._ import scala.language.postfixOps @@ -96,15 +96,14 @@ object PersistenceDocSpec { abstract class MyActor extends Actor { import PersistAsync.MyPersistentActor //#backoff - val childProps = Props[MyPersistentActor] + val childProps = Props[MyPersistentActor]() val props = BackoffSupervisor.props( - Backoff.onStop( + BackoffOpts.onStop( childProps, childName = "myActor", minBackoff = 3.seconds, maxBackoff = 30.seconds, - randomFactor = 0.2, - maxNrOfRetries = -1)) + randomFactor = 0.2)) context.actorOf(props, name = "mySupervisor") //#backoff } From 35207d55aa95f49603b78c5730ec7b930e9db6ff Mon Sep 17 00:00:00 2001 From: Nicolas Vollmar Date: Thu, 7 Mar 2019 12:21:25 +0100 Subject: [PATCH 11/11] Moves implementation to internal package #26156 --- .../src/main/mima-filters/2.5.21.backwards.excludes | 9 +++++---- akka-actor/src/main/scala/akka/pattern/Backoff.scala | 1 + .../src/main/scala/akka/pattern/BackoffOptions.scala | 1 + .../src/main/scala/akka/pattern/BackoffSupervisor.scala | 1 + .../src/main/scala/akka/pattern/HandleBackoff.scala | 1 + .../{ => internal}/BackoffOnRestartSupervisor.scala | 5 +++-- .../pattern/{ => internal}/BackoffOnStopSupervisor.scala | 5 +++-- 7 files changed, 15 insertions(+), 8 deletions(-) rename akka-actor/src/main/scala/akka/pattern/{ => internal}/BackoffOnRestartSupervisor.scala (95%) rename akka-actor/src/main/scala/akka/pattern/{ => internal}/BackoffOnStopSupervisor.scala (94%) diff --git a/akka-actor/src/main/mima-filters/2.5.21.backwards.excludes b/akka-actor/src/main/mima-filters/2.5.21.backwards.excludes index 71fbb8f8da..77d2cc2cae 100644 --- a/akka-actor/src/main/mima-filters/2.5.21.backwards.excludes +++ b/akka-actor/src/main/mima-filters/2.5.21.backwards.excludes @@ -6,9 +6,10 @@ ProblemFilters.exclude[DirectMissingMethodProblem]("akka.util.ccompat.package.fr ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.annotation.ApiMayChange.issue") # Simplify backoff supervision API #19016 -ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestartSupervisor.replyWhileStopped") -ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestartSupervisor.finalStopMessage") -ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestartSupervisor.this") +ProblemFilters.exclude[MissingClassProblem]("akka.pattern.BackoffOnRestartSupervisor") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.internal.BackoffOnRestartSupervisor.replyWhileStopped") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.internal.BackoffOnRestartSupervisor.finalStopMessage") +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.internal.BackoffOnRestartSupervisor.this") ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.HandleBackoff.replyWhileStopped") ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.HandleBackoff.finalStopMessage") -ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.pattern.HandleBackoff.handleMessageToChild") \ No newline at end of file +ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.pattern.HandleBackoff.handleMessageToChild") diff --git a/akka-actor/src/main/scala/akka/pattern/Backoff.scala b/akka-actor/src/main/scala/akka/pattern/Backoff.scala index 938995518c..0531bc72a5 100644 --- a/akka-actor/src/main/scala/akka/pattern/Backoff.scala +++ b/akka-actor/src/main/scala/akka/pattern/Backoff.scala @@ -6,6 +6,7 @@ package akka.pattern import akka.actor.{ OneForOneStrategy, Props, SupervisorStrategy } import akka.annotation.DoNotInherit +import akka.pattern.internal.{ BackoffOnRestartSupervisor, BackoffOnStopSupervisor } import akka.util.JavaDurationConverters._ import scala.concurrent.duration.{ Duration, FiniteDuration } diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala index 03f79517eb..f71f07e526 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala @@ -6,6 +6,7 @@ package akka.pattern import akka.actor.{ OneForOneStrategy, Props, SupervisorStrategy } import akka.annotation.DoNotInherit +import akka.pattern.internal.{ BackoffOnRestartSupervisor, BackoffOnStopSupervisor } import akka.util.JavaDurationConverters._ import scala.concurrent.duration.{ Duration, FiniteDuration } diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala index 08ccaafc00..e691c1a055 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala @@ -8,6 +8,7 @@ import java.util.Optional import java.util.concurrent.ThreadLocalRandom import akka.actor.{ ActorRef, DeadLetterSuppression, OneForOneStrategy, Props, SupervisorStrategy } +import akka.pattern.internal.BackoffOnStopSupervisor import akka.util.JavaDurationConverters._ import scala.concurrent.duration.{ Duration, FiniteDuration } diff --git a/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala b/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala index d34b915760..82e414a92b 100644 --- a/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala +++ b/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala @@ -6,6 +6,7 @@ package akka.pattern import akka.actor.{ Actor, ActorRef, Props } import akka.annotation.InternalApi +import akka.pattern.internal.{ BackoffOnRestartSupervisor, BackoffOnStopSupervisor } /** * INTERNAL API diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/internal/BackoffOnRestartSupervisor.scala similarity index 95% rename from akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala rename to akka-actor/src/main/scala/akka/pattern/internal/BackoffOnRestartSupervisor.scala index 87ede87c63..38e455a34a 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/internal/BackoffOnRestartSupervisor.scala @@ -2,11 +2,12 @@ * Copyright (C) 2015-2019 Lightbend Inc. */ -package akka.pattern +package akka.pattern.internal import akka.actor.SupervisorStrategy._ import akka.actor.{ OneForOneStrategy, _ } import akka.annotation.InternalApi +import akka.pattern.{ BackoffReset, BackoffSupervisor, HandleBackoff } import scala.concurrent.duration._ @@ -17,7 +18,7 @@ import scala.concurrent.duration._ * This back-off supervisor is created by using ``akka.pattern.BackoffSupervisor.props`` * with ``akka.pattern.BackoffOpts.onFailure``. */ -@InternalApi private class BackoffOnRestartSupervisor( +@InternalApi private[pattern] class BackoffOnRestartSupervisor( val childProps: Props, val childName: String, minBackoff: FiniteDuration, diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/internal/BackoffOnStopSupervisor.scala similarity index 94% rename from akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala rename to akka-actor/src/main/scala/akka/pattern/internal/BackoffOnStopSupervisor.scala index 666d2a8293..10cbaceed3 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOnStopSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/internal/BackoffOnStopSupervisor.scala @@ -2,11 +2,12 @@ * Copyright (C) 2018-2019 Lightbend Inc. */ -package akka.pattern +package akka.pattern.internal import akka.actor.SupervisorStrategy.{ Directive, Escalate } import akka.actor.{ Actor, ActorLogging, OneForOneStrategy, Props, SupervisorStrategy, Terminated } import akka.annotation.InternalApi +import akka.pattern.{ BackoffReset, BackoffSupervisor, HandleBackoff } import scala.concurrent.duration.FiniteDuration @@ -17,7 +18,7 @@ import scala.concurrent.duration.FiniteDuration * This back-off supervisor is created by using `akka.pattern.BackoffSupervisor.props` * with `BackoffOpts.onStop`. */ -@InternalApi private[akka] class BackoffOnStopSupervisor( +@InternalApi private[pattern] class BackoffOnStopSupervisor( val childProps: Props, val childName: String, minBackoff: FiniteDuration,