Simplify backoff supervision API #26156

This commit is contained in:
Johan Andrén 2019-03-07 14:27:27 +01:00 committed by GitHub
commit c01e6b6d73
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 1186 additions and 628 deletions

View file

@ -4,3 +4,12 @@ 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[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")

View file

@ -0,0 +1,611 @@
/*
* Copyright (C) 2015-2019 Lightbend Inc. <https://www.lightbend.com>
*/
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 }
/**
* @deprecated This API is superseded by the [[BackoffOpts]] object.
*/
@Deprecated
@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.
*
* 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 BackoffOpts.onFailure instead", "2.5.22")
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 BackoffOpts.onFailure instead", "2.5.22")
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 BackoffOpts.onFailure instead", "2.5.22")
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 BackoffOpts.onStop instead", "2.5.22")
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 BackoffOpts.onStop instead", "2.5.22")
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 BackoffOpts.onStop instead", "2.5.22")
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 BackoffOpts object instead", since = "2.5.22")
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

View file

@ -4,29 +4,17 @@
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.pattern.internal.{ BackoffOnRestartSupervisor, BackoffOnStopSupervisor }
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,84 +49,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 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): 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`.
* 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: FiniteDuration,
maxBackoff: FiniteDuration,
randomFactor: Double): BackoffOptions =
BackoffOptionsImpl(RestartImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor)
randomFactor: Double): BackoffOnFailureOptions =
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 +103,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): 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
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)
randomFactor: Double): BackoffOnFailureOptions =
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,92 +162,27 @@ 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): 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`.
* 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: FiniteDuration,
maxBackoff: FiniteDuration,
randomFactor: Double): BackoffOptions =
BackoffOptionsImpl(StopImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor)
randomFactor: Double): BackoffOnStopOptions =
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,161 +223,80 @@ 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): 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
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)
randomFactor: Double): BackoffOnStopOptions =
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)
* }}}
* Not for user extension
*/
@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
/**
* 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
/**
* 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
def withSupervisorStrategy(supervisorStrategy: OneForOneStrategy): 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, 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
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.
*/
def withReplyWhileStopped(replyWhileStopped: Any): T
/**
* Returns the props to create the back-off supervisor.
@ -580,8 +304,28 @@ trait BackoffOptions {
private[akka] def props: Props
}
private final case class BackoffOptionsImpl(
backoffType: BackoffType = RestartImpliesFailure,
@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
* 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): BackoffOnStopOptions
}
@DoNotInherit
sealed trait BackoffOnFailureOptions extends ExtendedBackoffOptions[BackoffOnFailureOptions]
private final case class BackoffOnStopOptionsImpl[T](
childProps: Props,
childName: String,
minBackoff: FiniteDuration,
@ -591,19 +335,22 @@ private final case class BackoffOptionsImpl(
supervisorStrategy: OneForOneStrategy = OneForOneStrategy()(SupervisorStrategy.defaultStrategy.decider),
replyWhileStopped: Option[Any] = None,
finalStopMessage: Option[Any Boolean] = None
) extends BackoffOptions {
) extends BackoffOnStopOptions {
val backoffReset = reset.getOrElse(AutoReset(minBackoff))
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 = {
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")
@ -613,20 +360,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, finalStopMessage))
//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))
}
}
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 {
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 withReplyWhileStopped(replyWhileStopped: Any) = copy(replyWhileStopped = Some(replyWhileStopped))
def withMaxNrOfRetries(maxNrOfRetries: Int) = copy(supervisorStrategy = supervisorStrategy.withMaxNrOfRetries(maxNrOfRetries))
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")
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

View file

@ -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.{ ActorRef, DeadLetterSuppression, OneForOneStrategy, Props, SupervisorStrategy }
import akka.pattern.internal.BackoffOnStopSupervisor
import akka.util.JavaDurationConverters._
import scala.concurrent.duration.{ Duration, FiniteDuration }
@ -17,22 +17,23 @@ 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
* 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`.
*/
@deprecated("Use props with BackoffOpts instead", since = "2.5.22")
def props(
childProps: Props,
childName: String,
@ -43,25 +44,26 @@ 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
* 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`.
*/
@deprecated("Use props with BackoffOpts instead", since = "2.5.22")
def props(
childProps: Props,
childName: String,
@ -77,22 +79,23 @@ 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
* 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`.
*/
@deprecated("Use props with BackoffOpts instead", since = "2.5.22")
def props(
childProps: Props,
childName: String,
@ -103,25 +106,26 @@ 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
* 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`.
*/
@deprecated("Use props with BackoffOpts instead", since = "2.5.22")
def props(
childProps: Props,
childName: String,
@ -133,26 +137,27 @@ 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
* `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.
*/
@deprecated("Use props with BackoffOpts instead", since = "2.5.22")
def propsWithSupervisorStrategy(
childProps: Props,
childName: String,
@ -163,30 +168,31 @@ 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))
}
/**
* 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
* `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.
*/
@deprecated("Use props with BackoffOpts instead", since = "2.5.22")
def propsWithSupervisorStrategy(
childProps: Props,
childName: String,
@ -198,26 +204,41 @@ 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.
*/
@deprecated("Use new API from BackoffOpts object instead", since = "2.5.22")
def props(options: BackoffOptions): Props = options.props
/**
* Send this message to the [[BackoffSupervisor]] and it will reply with
* 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.
*/
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]) {
@ -228,25 +249,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
@ -278,39 +299,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.22")
final class BackoffSupervisor(
val childProps: Props,
val childName: String,
minBackoff: FiniteDuration,
maxBackoff: FiniteDuration,
val reset: BackoffReset,
randomFactor: Double,
strategy: SupervisorStrategy,
val replyWhileStopped: Option[Any],
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(
@ -341,105 +342,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 = 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
}
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 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)
}
}
}
}
}

View file

@ -0,0 +1,69 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.pattern
import akka.actor.{ Actor, ActorRef, Props }
import akka.annotation.InternalApi
import akka.pattern.internal.{ BackoffOnRestartSupervisor, BackoffOnStopSupervisor }
/**
* INTERNAL API
*
* Implements basic backoff handling for [[BackoffOnRestartSupervisor]] and [[BackoffOnStopSupervisor]].
*/
@InternalApi 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: Actor.Receive = {
case StartChild
startChild()
reset match {
case AutoReset(resetBackoff)
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)
}
}

View file

@ -2,34 +2,36 @@
* Copyright (C) 2015-2019 Lightbend Inc. <https://www.lightbend.com>
*/
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._
import akka.actor._
import akka.actor.OneForOneStrategy
import akka.actor.SupervisorStrategy._
/**
* 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``.
* with ``akka.pattern.BackoffOpts.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])
@InternalApi private[pattern] class BackoffOnRestartSupervisor(
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 +83,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
}
}
}

View file

@ -0,0 +1,93 @@
/*
* Copyright (C) 2018-2019 Lightbend Inc. <https://www.lightbend.com>
*/
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
/**
* 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 `BackoffOpts.onStop`.
*/
@InternalApi private[pattern] 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
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 match {
case Some(fsm) finalStopMessageReceived = fsm(msg)
case None
}
case None
replyWhileStopped match {
case Some(r) sender() ! r
case None context.system.deadLetters.forward(msg)
}
finalStopMessage match {
case Some(fsm) if fsm(msg) context.stop(self)
case _
}
}
}

View file

@ -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)
}
}
}

View file

@ -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

View file

@ -207,6 +207,26 @@ to recover before the persistent actor is started.
> <a id="1" href="#^1">[1]</a> A failure can be indicated in two different ways; by an actor stopping or crashing.
<a id="supervision-strategies"></a>
#### 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 +259,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.
* `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:
@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-custom-stop }

View file

@ -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.

View file

@ -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),

View file

@ -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();
}

View file

@ -4,8 +4,9 @@
package docs.pattern
import akka.actor.{ ActorSystem, Props, OneForOneStrategy, SupervisorStrategy }
import akka.pattern.{ Backoff, BackoffSupervisor }
import akka.actor.{ ActorContext, ActorSystem, OneForOneStrategy, Props, SupervisorStrategy }
import akka.cluster.sharding.ShardRegion.Passivate
import akka.pattern.{ BackoffOpts, BackoffSupervisor }
import akka.testkit.TestActors.EchoActor
class BackoffSupervisorDocSpec {
@ -18,13 +19,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 +39,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 +59,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 +81,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() {
@ -103,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
}
}

View file

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