diff --git a/akka-actor/src/main/mima-filters/2.5.21.backwards.excludes b/akka-actor/src/main/mima-filters/2.5.21.backwards.excludes index ed4a04254e..77d2cc2cae 100644 --- a/akka-actor/src/main/mima-filters/2.5.21.backwards.excludes +++ b/akka-actor/src/main/mima-filters/2.5.21.backwards.excludes @@ -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") diff --git a/akka-actor/src/main/scala/akka/pattern/Backoff.scala b/akka-actor/src/main/scala/akka/pattern/Backoff.scala new file mode 100644 index 0000000000..0531bc72a5 --- /dev/null +++ b/akka-actor/src/main/scala/akka/pattern/Backoff.scala @@ -0,0 +1,611 @@ +/* + * Copyright (C) 2015-2019 Lightbend Inc. + */ + +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 diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala index de6157a0be..f71f07e526 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala @@ -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 diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala index c131b7f39d..e691c1a055 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala @@ -4,11 +4,11 @@ package akka.pattern -import java.util.concurrent.ThreadLocalRandom import java.util.Optional +import java.util.concurrent.ThreadLocalRandom -import akka.actor.{ Actor, ActorLogging, ActorRef, DeadLetterSuppression, OneForOneStrategy, Props, SupervisorStrategy, Terminated } -import akka.actor.SupervisorStrategy.{ Directive, Escalate } +import akka.actor.{ 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) - } - } - } - } } diff --git a/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala b/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala new file mode 100644 index 0000000000..82e414a92b --- /dev/null +++ b/akka-actor/src/main/scala/akka/pattern/HandleBackoff.scala @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2018-2019 Lightbend Inc. + */ + +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) + } +} diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/internal/BackoffOnRestartSupervisor.scala similarity index 75% rename from akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala rename to akka-actor/src/main/scala/akka/pattern/internal/BackoffOnRestartSupervisor.scala index 985a5f6351..38e455a34a 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOnRestartSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/internal/BackoffOnRestartSupervisor.scala @@ -2,34 +2,36 @@ * Copyright (C) 2015-2019 Lightbend Inc. */ -package akka.pattern +package akka.pattern.internal + +import akka.actor.SupervisorStrategy._ +import akka.actor.{ OneForOneStrategy, _ } +import akka.annotation.InternalApi +import akka.pattern.{ BackoffReset, BackoffSupervisor, HandleBackoff } import scala.concurrent.duration._ -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 + } + } } diff --git a/akka-actor/src/main/scala/akka/pattern/internal/BackoffOnStopSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/internal/BackoffOnStopSupervisor.scala new file mode 100644 index 0000000000..10cbaceed3 --- /dev/null +++ b/akka-actor/src/main/scala/akka/pattern/internal/BackoffOnStopSupervisor.scala @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2018-2019 Lightbend Inc. + */ + +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 _ ⇒ + } + } +} diff --git a/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/SupervisionSpec.scala b/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/SupervisionSpec.scala index 1159f65cfb..9f37e60281 100644 --- a/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/SupervisionSpec.scala +++ b/akka-cluster-sharding/src/test/scala/akka/cluster/sharding/SupervisionSpec.scala @@ -7,7 +7,7 @@ package akka.cluster.sharding import akka.actor.{ Actor, ActorLogging, ActorRef, PoisonPill, Props } import akka.cluster.Cluster import akka.cluster.sharding.ShardRegion.Passivate -import akka.pattern.{ Backoff, BackoffSupervisor } +import akka.pattern.{ Backoff, BackoffOpts, BackoffSupervisor } import akka.testkit.{ AkkaSpec, ImplicitSender } import com.typesafe.config.ConfigFactory @@ -64,7 +64,7 @@ class SupervisionSpec extends AkkaSpec(SupervisionSpec.config) with ImplicitSend import SupervisionSpec._ - "Supervision for a sharded actor" must { + "Supervision for a sharded actor (deprecated)" must { "allow passivation" in { @@ -99,4 +99,38 @@ class SupervisionSpec extends AkkaSpec(SupervisionSpec.config) with ImplicitSend } } + "Supervision for a sharded actor" must { + + "allow passivation" in { + + val supervisedProps = BackoffSupervisor.props(BackoffOpts.onStop( + Props(new PassivatingActor()), + childName = "child", + minBackoff = 1.seconds, + maxBackoff = 30.seconds, + randomFactor = 0.2 + ).withFinalStopMessage(_ == StopMessage)) + + Cluster(system).join(Cluster(system).selfAddress) + val region = ClusterSharding(system).start( + "passy", + supervisedProps, + ClusterShardingSettings(system), + idExtractor, + shardResolver + ) + + region ! Msg(10, "hello") + val response = expectMsgType[Response](5.seconds) + watch(response.self) + + region ! Msg(10, "passivate") + expectTerminated(response.self) + + // This would fail before as sharded actor would be stuck passivating + region ! Msg(10, "hello") + expectMsgType[Response](20.seconds) + } + } + } diff --git a/akka-docs/src/main/paradox/cluster-sharding.md b/akka-docs/src/main/paradox/cluster-sharding.md index 4d7ef2b490..bc34ceaff5 100644 --- a/akka-docs/src/main/paradox/cluster-sharding.md +++ b/akka-docs/src/main/paradox/cluster-sharding.md @@ -413,6 +413,8 @@ Java Note that stopped entities will be started again when a new message is targeted to the entity. +If 'on stop' backoff supervision strategy is used, a final termination message must be set and used for passivation, see @ref:[Supervision](general/supervision.md#Sharding) + ## Graceful Shutdown You can send the @scala[`ShardRegion.GracefulShutdown`] @java[`ShardRegion.gracefulShutdownInstance`] message diff --git a/akka-docs/src/main/paradox/general/supervision.md b/akka-docs/src/main/paradox/general/supervision.md index 7f6c84daf9..58d102e632 100644 --- a/akka-docs/src/main/paradox/general/supervision.md +++ b/akka-docs/src/main/paradox/general/supervision.md @@ -207,6 +207,26 @@ to recover before the persistent actor is started. > [1] A failure can be indicated in two different ways; by an actor stopping or crashing. + +#### Supervision strategies + +There are two basic supervision strategies available for backoff: +* 'On failure': The supervisor will restart the supervised actor once it crashes, but terminate if the actor stops normaly (e.g. through `context.stop`) +* 'On stop': The supervisor will restart the supervised actor if it terminates in any way (consider this for `PersistentActor` since they stop on persistence failures instead of crashing) + +#### Sharding +If the 'on stop' strategy is used for sharded actors a final termination message should be configured and used to terminate the actor on passivation. Otherwise the supervisor will just restart the actor again. + +The termination message is configured with: + +@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-sharded } + +And must be used for passivation: + +@@snip [BackoffSupervisorDocSpec.scala](/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala) { #backoff-sharded-passivation } + +#### Simple backoff + The following Scala snippet shows how to create a backoff supervisor which will start the given echo actor after it has stopped because of a failure, in increasing intervals of 3, 6, 12, 24 and finally 30 seconds: @@ -239,7 +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 } diff --git a/akka-docs/src/main/paradox/persistence.md b/akka-docs/src/main/paradox/persistence.md index 2aafd25a10..6f15c4505b 100644 --- a/akka-docs/src/main/paradox/persistence.md +++ b/akka-docs/src/main/paradox/persistence.md @@ -466,6 +466,8 @@ Scala Java : @@snip [LambdaPersistenceDocTest.java](/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java) { #backoff } +See @ref:[Supervision strategies](general/supervision.md#supervision-strategies) for more details about actor supervision. + If persistence of an event is rejected before it is stored, e.g. due to serialization error, `onPersistRejected` will be invoked (logging a warning by default), and the actor continues with next message. diff --git a/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java b/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java index e1b68b421b..5f7755837f 100644 --- a/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java +++ b/akka-docs/src/test/java/jdocs/pattern/BackoffSupervisorDocTest.java @@ -6,6 +6,7 @@ package jdocs.pattern; import akka.actor.*; import akka.pattern.Backoff; +import akka.pattern.BackoffOpts; import akka.pattern.BackoffSupervisor; import akka.testkit.TestActors.EchoActor; // #backoff-imports @@ -20,7 +21,7 @@ public class BackoffSupervisorDocTest { final Props supervisorProps = BackoffSupervisor.props( - Backoff.onStop( + BackoffOpts.onStop( childProps, "myEcho", Duration.ofSeconds(3), @@ -37,7 +38,7 @@ public class BackoffSupervisorDocTest { final Props supervisorProps = BackoffSupervisor.props( - Backoff.onFailure( + BackoffOpts.onFailure( childProps, "myEcho", Duration.ofSeconds(3), diff --git a/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java b/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java index a2f01bf42e..7307148caf 100644 --- a/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java +++ b/akka-docs/src/test/java/jdocs/persistence/LambdaPersistenceDocTest.java @@ -6,11 +6,12 @@ package jdocs.persistence; import akka.actor.*; import akka.japi.Procedure; +import akka.pattern.BackoffOpts; import akka.pattern.BackoffSupervisor; import akka.persistence.*; -import java.time.Duration; import java.io.Serializable; +import java.time.Duration; import java.util.Optional; public class LambdaPersistenceDocTest { @@ -20,6 +21,7 @@ public class LambdaPersistenceDocTest { public interface PersistentActorMethods { // #persistence-id public String persistenceId(); + // #persistence-id // #recovery-status public boolean recoveryRunning(); @@ -136,7 +138,8 @@ public class LambdaPersistenceDocTest { final Props childProps = Props.create(MyPersistentActor1.class); final Props props = BackoffSupervisor.props( - childProps, "myActor", Duration.ofSeconds(3), Duration.ofSeconds(30), 0.2); + BackoffOpts.onStop( + childProps, "myActor", Duration.ofSeconds(3), Duration.ofSeconds(30), 0.2)); getContext().actorOf(props, "mySupervisor"); super.preStart(); } diff --git a/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala b/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala index 5e9d2efa98..c2f1893bb3 100644 --- a/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala +++ b/akka-docs/src/test/scala/docs/pattern/BackoffSupervisorDocSpec.scala @@ -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 + } } diff --git a/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala b/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala index d2341d89e9..5fe99ebdd9 100644 --- a/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala +++ b/akka-docs/src/test/scala/docs/persistence/PersistenceDocSpec.scala @@ -5,10 +5,10 @@ package docs.persistence import akka.actor._ -import akka.pattern.{ Backoff, BackoffSupervisor } +import akka.pattern.{ Backoff, BackoffOpts, BackoffSupervisor } import akka.persistence._ import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{ Source, Sink, Flow } +import akka.stream.scaladsl.{ Flow, Sink, Source } import scala.concurrent.duration._ import scala.language.postfixOps @@ -96,15 +96,14 @@ object PersistenceDocSpec { abstract class MyActor extends Actor { import PersistAsync.MyPersistentActor //#backoff - val childProps = Props[MyPersistentActor] + val childProps = Props[MyPersistentActor]() val props = BackoffSupervisor.props( - Backoff.onStop( + BackoffOpts.onStop( childProps, childName = "myActor", minBackoff = 3.seconds, maxBackoff = 30.seconds, - randomFactor = 0.2, - maxNrOfRetries = -1)) + randomFactor = 0.2)) context.actorOf(props, name = "mySupervisor") //#backoff }