diff --git a/akka-actor/src/main/mima-filters/2.5.22.backwards.excludes b/akka-actor/src/main/mima-filters/2.5.22.backwards.excludes index 6cf3f129cd..d0b5506993 100644 --- a/akka-actor/src/main/mima-filters/2.5.22.backwards.excludes +++ b/akka-actor/src/main/mima-filters/2.5.22.backwards.excludes @@ -4,6 +4,4 @@ ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestar ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.BackoffOnRestartSupervisor.this") ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.HandleBackoff.replyWhileStopped") ProblemFilters.exclude[DirectMissingMethodProblem]("akka.pattern.HandleBackoff.finalStopMessage") -ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.pattern.HandleBackoff.handleMessageToChild") -ProblemFilters.exclude[IncompatibleResultTypeProblem]("akka.pattern.Backoff.onFailure") -ProblemFilters.exclude[IncompatibleResultTypeProblem]("akka.pattern.Backoff.onStop") \ No newline at end of file +ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.pattern.HandleBackoff.handleMessageToChild") \ No newline at end of file diff --git a/akka-actor/src/main/scala/akka/pattern/Backoff.scala b/akka-actor/src/main/scala/akka/pattern/Backoff.scala new file mode 100644 index 0000000000..a127760571 --- /dev/null +++ b/akka-actor/src/main/scala/akka/pattern/Backoff.scala @@ -0,0 +1,610 @@ +/* + * Copyright (C) 2015-2019 Lightbend Inc. + */ + +package akka.pattern + +import akka.actor.{ OneForOneStrategy, Props, SupervisorStrategy } +import akka.annotation.DoNotInherit +import akka.util.JavaDurationConverters._ + +import scala.concurrent.duration.{ Duration, FiniteDuration } + +/** + * @deprecated This API is superseded by the [[BackoffOptions]] object. + */ +@Deprecated +@deprecated("Use new API from BackoffOptions object instead", since = "2.5.20") +object Backoff { + /** + * Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. + * + * This explicit supervisor behaves similarly to the normal implicit supervision where + * if an actor throws an exception, the decider on the supervisor will decide when to + * `Stop`, `Restart`, `Escalate`, `Resume` the child actor. + * + * When the `Restart` directive is specified, the supervisor will delay the restart + * using an exponential back off strategy (bounded by minBackoff and maxBackoff). + * + * This supervisor is intended to be transparent to both the child actor and external actors. + * Where external actors can send messages to the supervisor as if it was the child and the + * messages will be forwarded. And when the child is `Terminated`, the supervisor is also + * `Terminated`. + * Transparent to the child means that the child does not have to be aware that it is being + * supervised specifically by this actor. Just like it does + * not need to know when it is being supervised by the usual implicit supervisors. + * The only caveat is that the `ActorRef` of the child is not stable, so any user storing the + * `sender()` `ActorRef` from the child response may eventually not be able to communicate with + * the stored `ActorRef`. In general all messages to the child should be directed through this actor. + * + * An example of where this supervisor might be used is when you may have an actor that is + * responsible for continuously polling on a server for some resource that sometimes may be down. + * Instead of hammering the server continuously when the resource is unavailable, the actor will + * be restarted with an exponentially increasing back off until the resource is available again. + * + * '''*** + * This supervisor should not be used with `Akka Persistence` child actors. + * `Akka Persistence` actors shutdown unconditionally on `persistFailure()`s rather + * than throw an exception on a failure like normal actors. + * [[#onStop]] should be used instead for cases where the child actor + * terminates itself as a failure signal instead of the normal behavior of throwing an exception. + * ***''' + * You can define another + * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + * @param maxNrOfRetries maximum number of attempts to restart the child actor. + * The supervisor will terminate itself after the maxNoOfRetries is reached. + * In order to restart infinitely pass in `-1`. + * + */ + @deprecated("Use BackoffOptions.onFailure instead", "2.5.20") + def onFailure( + childProps: Props, + childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + randomFactor: Double, + maxNrOfRetries: Int): BackoffOptions = + BackoffOptionsImpl(RestartImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor).withMaxNrOfRetries(maxNrOfRetries) + + /** + * Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. + * + * This explicit supervisor behaves similarly to the normal implicit supervision where + * if an actor throws an exception, the decider on the supervisor will decide when to + * `Stop`, `Restart`, `Escalate`, `Resume` the child actor. + * + * When the `Restart` directive is specified, the supervisor will delay the restart + * using an exponential back off strategy (bounded by minBackoff and maxBackoff). + * + * This supervisor is intended to be transparent to both the child actor and external actors. + * Where external actors can send messages to the supervisor as if it was the child and the + * messages will be forwarded. And when the child is `Terminated`, the supervisor is also + * `Terminated`. + * Transparent to the child means that the child does not have to be aware that it is being + * supervised specifically by this actor. Just like it does + * not need to know when it is being supervised by the usual implicit supervisors. + * The only caveat is that the `ActorRef` of the child is not stable, so any user storing the + * `sender()` `ActorRef` from the child response may eventually not be able to communicate with + * the stored `ActorRef`. In general all messages to the child should be directed through this actor. + * + * An example of where this supervisor might be used is when you may have an actor that is + * responsible for continuously polling on a server for some resource that sometimes may be down. + * Instead of hammering the server continuously when the resource is unavailable, the actor will + * be restarted with an exponentially increasing back off until the resource is available again. + * + * '''*** + * This supervisor should not be used with `Akka Persistence` child actors. + * `Akka Persistence` actors shutdown unconditionally on `persistFailure()`s rather + * than throw an exception on a failure like normal actors. + * [[#onStop]] should be used instead for cases where the child actor + * terminates itself as a failure signal instead of the normal behavior of throwing an exception. + * ***''' + * You can define another + * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + */ + @deprecated("Use BackoffOptions.onFailure instead", "2.5.20") + def onFailure( + childProps: Props, + childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + randomFactor: Double): BackoffOptions = + BackoffOptionsImpl(RestartImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor) + + /** + * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. + * + * This explicit supervisor behaves similarly to the normal implicit supervision where + * if an actor throws an exception, the decider on the supervisor will decide when to + * `Stop`, `Restart`, `Escalate`, `Resume` the child actor. + * + * When the `Restart` directive is specified, the supervisor will delay the restart + * using an exponential back off strategy (bounded by minBackoff and maxBackoff). + * + * This supervisor is intended to be transparent to both the child actor and external actors. + * Where external actors can send messages to the supervisor as if it was the child and the + * messages will be forwarded. And when the child is `Terminated`, the supervisor is also + * `Terminated`. + * Transparent to the child means that the child does not have to be aware that it is being + * supervised specifically by this actor. Just like it does + * not need to know when it is being supervised by the usual implicit supervisors. + * The only caveat is that the `ActorRef` of the child is not stable, so any user storing the + * `sender()` `ActorRef` from the child response may eventually not be able to communicate with + * the stored `ActorRef`. In general all messages to the child should be directed through this actor. + * + * An example of where this supervisor might be used is when you may have an actor that is + * responsible for continuously polling on a server for some resource that sometimes may be down. + * Instead of hammering the server continuously when the resource is unavailable, the actor will + * be restarted with an exponentially increasing back off until the resource is available again. + * + * '''*** + * This supervisor should not be used with `Akka Persistence` child actors. + * `Akka Persistence` actors shutdown unconditionally on `persistFailure()`s rather + * than throw an exception on a failure like normal actors. + * [[#onStop]] should be used instead for cases where the child actor + * terminates itself as a failure signal instead of the normal behavior of throwing an exception. + * ***''' + * You can define another + * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + * @param maxNrOfRetries maximum number of attempts to restart the child actor. + * The supervisor will terminate itself after the maxNoOfRetries is reached. + * In order to restart infinitely pass in `-1`. + */ + @Deprecated + @deprecated("Use BackoffOptions.onFailure instead", "2.5.20") + def onFailure( + childProps: Props, + childName: String, + minBackoff: java.time.Duration, + maxBackoff: java.time.Duration, + randomFactor: Double, + maxNrOfRetries: Int): BackoffOptions = + onFailure(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, maxNrOfRetries) + + /** + * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. + * + * This explicit supervisor behaves similarly to the normal implicit supervision where + * if an actor throws an exception, the decider on the supervisor will decide when to + * `Stop`, `Restart`, `Escalate`, `Resume` the child actor. + * + * When the `Restart` directive is specified, the supervisor will delay the restart + * using an exponential back off strategy (bounded by minBackoff and maxBackoff). + * + * This supervisor is intended to be transparent to both the child actor and external actors. + * Where external actors can send messages to the supervisor as if it was the child and the + * messages will be forwarded. And when the child is `Terminated`, the supervisor is also + * `Terminated`. + * Transparent to the child means that the child does not have to be aware that it is being + * supervised specifically by this actor. Just like it does + * not need to know when it is being supervised by the usual implicit supervisors. + * The only caveat is that the `ActorRef` of the child is not stable, so any user storing the + * `sender()` `ActorRef` from the child response may eventually not be able to communicate with + * the stored `ActorRef`. In general all messages to the child should be directed through this actor. + * + * An example of where this supervisor might be used is when you may have an actor that is + * responsible for continuously polling on a server for some resource that sometimes may be down. + * Instead of hammering the server continuously when the resource is unavailable, the actor will + * be restarted with an exponentially increasing back off until the resource is available again. + * + * '''*** + * This supervisor should not be used with `Akka Persistence` child actors. + * `Akka Persistence` actors shutdown unconditionally on `persistFailure()`s rather + * than throw an exception on a failure like normal actors. + * [[#onStop]] should be used instead for cases where the child actor + * terminates itself as a failure signal instead of the normal behavior of throwing an exception. + * ***''' + * You can define another + * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + */ + @Deprecated + @deprecated("Use the overloaded one which accepts maxNrOfRetries instead.", "2.5.17") + def onFailure( + childProps: Props, + childName: String, + minBackoff: java.time.Duration, + maxBackoff: java.time.Duration, + randomFactor: Double): BackoffOptions = + onFailure(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, -1) + + /** + * Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. + * + * This actor can be used to supervise a child actor and start it again + * after a back-off duration if the child actor is stopped. + * + * This is useful in situations where the re-start of the child actor should be + * delayed e.g. in order to give an external resource time to recover before the + * child actor tries contacting it again (after being restarted). + * + * Specifically this pattern is useful for persistent actors, + * which are stopped in case of persistence failures. + * Just restarting them immediately would probably fail again (since the data + * store is probably unavailable). It is better to try again after a delay. + * + * It supports exponential back-off between the given `minBackoff` and + * `maxBackoff` durations. For example, if `minBackoff` is 3 seconds and + * `maxBackoff` 30 seconds the start attempts will be delayed with + * 3, 6, 12, 24, 30, 30 seconds. The exponential back-off counter is reset + * if the actor is not terminated within the `minBackoff` duration. + * + * In addition to the calculated exponential back-off an additional + * random delay based the given `randomFactor` is added, e.g. 0.2 adds up to 20% + * delay. The reason for adding a random delay is to avoid that all failing + * actors hit the backend resource at the same time. + * + * You can retrieve the current child `ActorRef` by sending `BackoffSupervisor.GetCurrentChild` + * message to this actor and it will reply with [[akka.pattern.BackoffSupervisor.CurrentChild]] + * containing the `ActorRef` of the current child, if any. + * + * The `BackoffSupervisor`delegates all messages from the child to the parent of the + * `BackoffSupervisor`, with the supervisor as sender. + * + * The `BackoffSupervisor` forwards all other messages to the child, if it is currently running. + * + * The child can stop itself and send a [[akka.actor.PoisonPill]] to the parent supervisor + * if it wants to do an intentional stop. + * + * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using + * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A + * `Restart` will perform a normal immediate restart of the child. A `Stop` will + * stop the child, but it will be started again after the back-off duration. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + * @param maxNrOfRetries maximum number of attempts to restart the child actor. + * The supervisor will terminate itself after the maxNoOfRetries is reached. + * In order to restart infinitely pass in `-1`. + */ + @deprecated("Use BackoffOptions.onStop instead", "2.5.20") + def onStop( + childProps: Props, + childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + randomFactor: Double, + maxNrOfRetries: Int): BackoffOptions = + BackoffOptionsImpl(StopImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor).withMaxNrOfRetries(maxNrOfRetries) + + /** + * Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. + * + * This actor can be used to supervise a child actor and start it again + * after a back-off duration if the child actor is stopped. + * + * This is useful in situations where the re-start of the child actor should be + * delayed e.g. in order to give an external resource time to recover before the + * child actor tries contacting it again (after being restarted). + * + * Specifically this pattern is useful for persistent actors, + * which are stopped in case of persistence failures. + * Just restarting them immediately would probably fail again (since the data + * store is probably unavailable). It is better to try again after a delay. + * + * It supports exponential back-off between the given `minBackoff` and + * `maxBackoff` durations. For example, if `minBackoff` is 3 seconds and + * `maxBackoff` 30 seconds the start attempts will be delayed with + * 3, 6, 12, 24, 30, 30 seconds. The exponential back-off counter is reset + * if the actor is not terminated within the `minBackoff` duration. + * + * In addition to the calculated exponential back-off an additional + * random delay based the given `randomFactor` is added, e.g. 0.2 adds up to 20% + * delay. The reason for adding a random delay is to avoid that all failing + * actors hit the backend resource at the same time. + * + * You can retrieve the current child `ActorRef` by sending `BackoffSupervisor.GetCurrentChild` + * message to this actor and it will reply with [[akka.pattern.BackoffSupervisor.CurrentChild]] + * containing the `ActorRef` of the current child, if any. + * + * The `BackoffSupervisor`delegates all messages from the child to the parent of the + * `BackoffSupervisor`, with the supervisor as sender. + * + * The `BackoffSupervisor` forwards all other messages to the child, if it is currently running. + * + * The child can stop itself and send a [[akka.actor.PoisonPill]] to the parent supervisor + * if it wants to do an intentional stop. + * + * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using + * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A + * `Restart` will perform a normal immediate restart of the child. A `Stop` will + * stop the child, but it will be started again after the back-off duration. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + */ + @deprecated("Use BackoffOptions.onStop instead", "2.5.20") + def onStop( + childProps: Props, + childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + randomFactor: Double): BackoffOptions = + BackoffOptionsImpl(StopImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor) + + /** + * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. + * + * This actor can be used to supervise a child actor and start it again + * after a back-off duration if the child actor is stopped. + * + * This is useful in situations where the re-start of the child actor should be + * delayed e.g. in order to give an external resource time to recover before the + * child actor tries contacting it again (after being restarted). + * + * Specifically this pattern is useful for persistent actors, + * which are stopped in case of persistence failures. + * Just restarting them immediately would probably fail again (since the data + * store is probably unavailable). It is better to try again after a delay. + * + * It supports exponential back-off between the given `minBackoff` and + * `maxBackoff` durations. For example, if `minBackoff` is 3 seconds and + * `maxBackoff` 30 seconds the start attempts will be delayed with + * 3, 6, 12, 24, 30, 30 seconds. The exponential back-off counter is reset + * if the actor is not terminated within the `minBackoff` duration. + * + * In addition to the calculated exponential back-off an additional + * random delay based the given `randomFactor` is added, e.g. 0.2 adds up to 20% + * delay. The reason for adding a random delay is to avoid that all failing + * actors hit the backend resource at the same time. + * + * You can retrieve the current child `ActorRef` by sending `BackoffSupervisor.GetCurrentChild` + * message to this actor and it will reply with [[akka.pattern.BackoffSupervisor.CurrentChild]] + * containing the `ActorRef` of the current child, if any. + * + * The `BackoffSupervisor`delegates all messages from the child to the parent of the + * `BackoffSupervisor`, with the supervisor as sender. + * + * The `BackoffSupervisor` forwards all other messages to the child, if it is currently running. + * + * The child can stop itself and send a [[akka.actor.PoisonPill]] to the parent supervisor + * if it wants to do an intentional stop. + * + * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using + * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A + * `Restart` will perform a normal immediate restart of the child. A `Stop` will + * stop the child, but it will be started again after the back-off duration. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + * @param maxNrOfRetries maximum number of attempts to restart the child actor. + * The supervisor will terminate itself after the maxNoOfRetries is reached. + * In order to restart infinitely pass in `-1`. + */ + @Deprecated + @deprecated("Use BackoffOptions.onStop instead", "2.5.20") + def onStop( + childProps: Props, + childName: String, + minBackoff: java.time.Duration, + maxBackoff: java.time.Duration, + randomFactor: Double, + maxNrOfRetries: Int): BackoffOptions = + onStop(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, maxNrOfRetries) + + /** + * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. + * + * This actor can be used to supervise a child actor and start it again + * after a back-off duration if the child actor is stopped. + * + * This is useful in situations where the re-start of the child actor should be + * delayed e.g. in order to give an external resource time to recover before the + * child actor tries contacting it again (after being restarted). + * + * Specifically this pattern is useful for persistent actors, + * which are stopped in case of persistence failures. + * Just restarting them immediately would probably fail again (since the data + * store is probably unavailable). It is better to try again after a delay. + * + * It supports exponential back-off between the given `minBackoff` and + * `maxBackoff` durations. For example, if `minBackoff` is 3 seconds and + * `maxBackoff` 30 seconds the start attempts will be delayed with + * 3, 6, 12, 24, 30, 30 seconds. The exponential back-off counter is reset + * if the actor is not terminated within the `minBackoff` duration. + * + * In addition to the calculated exponential back-off an additional + * random delay based the given `randomFactor` is added, e.g. 0.2 adds up to 20% + * delay. The reason for adding a random delay is to avoid that all failing + * actors hit the backend resource at the same time. + * + * You can retrieve the current child `ActorRef` by sending `BackoffSupervisor.GetCurrentChild` + * message to this actor and it will reply with [[akka.pattern.BackoffSupervisor.CurrentChild]] + * containing the `ActorRef` of the current child, if any. + * + * The `BackoffSupervisor`delegates all messages from the child to the parent of the + * `BackoffSupervisor`, with the supervisor as sender. + * + * The `BackoffSupervisor` forwards all other messages to the child, if it is currently running. + * + * The child can stop itself and send a [[akka.actor.PoisonPill]] to the parent supervisor + * if it wants to do an intentional stop. + * + * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using + * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A + * `Restart` will perform a normal immediate restart of the child. A `Stop` will + * stop the child, but it will be started again after the back-off duration. + * + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. + */ + @Deprecated + @deprecated("Use the overloaded one which accepts maxNrOfRetries instead.", "2.5.17") + def onStop( + childProps: Props, + childName: String, + minBackoff: java.time.Duration, + maxBackoff: java.time.Duration, + randomFactor: Double): BackoffOptions = + onStop(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, -1) +} + +/** + * Configures a back-off supervisor actor. Start with `Backoff.onStop` or `Backoff.onFailure`. + * BackoffOptions is immutable, so be sure to chain methods like: + * {{{ + * val options = Backoff.onFailure(childProps, childName, minBackoff, maxBackoff, randomFactor) + * .withManualReset + * context.actorOf(BackoffSupervisor.props(options), name) + * }}} + */ +@DoNotInherit +@Deprecated +@deprecated("Use new API from BackoffOptions object instead", since = "2.5.20") +trait BackoffOptions { + + /** + * @see [[ExtendedBackoffOptions.withAutoReset()]] + */ + def withAutoReset(resetBackoff: FiniteDuration): BackoffOptions + + /** + * @see [[ExtendedBackoffOptions.withManualReset()]] + */ + def withManualReset: BackoffOptions + + /** + * @see [[ExtendedBackoffOptions.withSupervisorStrategy()]] + */ + def withSupervisorStrategy(supervisorStrategy: OneForOneStrategy): BackoffOptions + + /** + * @see [[ExtendedBackoffOptions.withDefaultStoppingStrategy()]] + */ + def withDefaultStoppingStrategy: BackoffOptions + + /** + * @see [[ExtendedBackoffOptions.withMaxNrOfRetries()]] + */ + def withMaxNrOfRetries(maxNrOfRetries: Int): BackoffOptions + + /** + * @see [[ExtendedBackoffOptions.withReplyWhileStopped()]] + */ + def withReplyWhileStopped(replyWhileStopped: Any): BackoffOptions + + /** + * @see [[BackoffOnStopOptions.withFinalStopMessage()]] + */ + def withFinalStopMessage(isFinalStopMessage: Any ⇒ Boolean): BackoffOptions + + /** + * Returns the props to create the back-off supervisor. + */ + private[akka] def props: Props +} + +private final case class BackoffOptionsImpl( + backoffType: BackoffType = RestartImpliesFailure, + childProps: Props, + childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + randomFactor: Double, + reset: Option[BackoffReset] = None, + supervisorStrategy: OneForOneStrategy = OneForOneStrategy()(SupervisorStrategy.defaultStrategy.decider), + replyWhileStopped: Option[Any] = None, + finalStopMessage: Option[Any ⇒ Boolean] = None +) extends akka.pattern.BackoffOptions { + + val backoffReset = reset.getOrElse(AutoReset(minBackoff)) + + def withAutoReset(resetBackoff: FiniteDuration) = copy(reset = Some(AutoReset(resetBackoff))) + def withManualReset = copy(reset = Some(ManualReset)) + def withSupervisorStrategy(supervisorStrategy: OneForOneStrategy) = copy(supervisorStrategy = supervisorStrategy) + def withDefaultStoppingStrategy = copy(supervisorStrategy = OneForOneStrategy(supervisorStrategy.maxNrOfRetries)(SupervisorStrategy.stoppingStrategy.decider)) + def withReplyWhileStopped(replyWhileStopped: Any) = copy(replyWhileStopped = Some(replyWhileStopped)) + def withMaxNrOfRetries(maxNrOfRetries: Int) = copy(supervisorStrategy = supervisorStrategy.withMaxNrOfRetries(maxNrOfRetries)) + def withFinalStopMessage(action: Any ⇒ Boolean) = copy(finalStopMessage = Some(action)) + + def props = { + require(minBackoff > Duration.Zero, "minBackoff must be > 0") + require(maxBackoff >= minBackoff, "maxBackoff must be >= minBackoff") + require(0.0 <= randomFactor && randomFactor <= 1.0, "randomFactor must be between 0.0 and 1.0") + backoffReset match { + case AutoReset(resetBackoff) ⇒ + require(minBackoff <= resetBackoff && resetBackoff <= maxBackoff) + case _ ⇒ // ignore + } + + backoffType match { + //onFailure method in companion object + case RestartImpliesFailure ⇒ + Props(new BackoffOnRestartSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped)) + //onStop method in companion object + case StopImpliesFailure ⇒ + Props(new BackoffOnStopSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped, finalStopMessage)) + } + } +} + +private sealed trait BackoffType +private final case object StopImpliesFailure extends BackoffType +private final case object RestartImpliesFailure extends BackoffType diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala index c927223ed1..28dcda8c3c 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffOptions.scala @@ -4,29 +4,16 @@ package akka.pattern -import scala.concurrent.duration.{ Duration, FiniteDuration } -import akka.util.JavaDurationConverters._ import akka.actor.{ OneForOneStrategy, Props, SupervisorStrategy } import akka.annotation.DoNotInherit +import akka.util.JavaDurationConverters._ + +import scala.concurrent.duration.{ Duration, FiniteDuration } /** - * Builds back-off options for creating a back-off supervisor. - * You can pass `BackoffOptions` to `akka.pattern.BackoffSupervisor.props`. - * An example of creating back-off options: - * {{{ - * Backoff.onFailure(childProps, childName, minBackoff, maxBackoff, randomFactor) - * .withManualReset - * .withSupervisorStrategy( - * OneforOneStrategy(){ - * case e: GivingUpException => Stop - * case e: RetryableException => Restart - * } - * ) - * .withReplyWhileStopped(TheSystemIsDown) - * - * }}} + * Backoff options allow to specify a number of properties for backoff supervisors. */ -object Backoff { +object BackoffOpts { /** * Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. * @@ -61,76 +48,17 @@ object Backoff { * terminates itself as a failure signal instead of the normal behavior of throwing an exception. * ***''' * You can define another - * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. + * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOnFailureOptions]]. * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. - * @param maxNrOfRetries maximum number of attempts to restart the child actor. - * The supervisor will terminate itself after the maxNoOfRetries is reached. - * In order to restart infinitely pass in `-1`. - * - */ - def onFailure( - childProps: Props, - childName: String, - minBackoff: FiniteDuration, - maxBackoff: FiniteDuration, - randomFactor: Double, - maxNrOfRetries: Int): BackoffOnFailureOptions = - BackoffOptionsImpl(RestartImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor).withMaxNrOfRetries(maxNrOfRetries) - - /** - * Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. - * - * This explicit supervisor behaves similarly to the normal implicit supervision where - * if an actor throws an exception, the decider on the supervisor will decide when to - * `Stop`, `Restart`, `Escalate`, `Resume` the child actor. - * - * When the `Restart` directive is specified, the supervisor will delay the restart - * using an exponential back off strategy (bounded by minBackoff and maxBackoff). - * - * This supervisor is intended to be transparent to both the child actor and external actors. - * Where external actors can send messages to the supervisor as if it was the child and the - * messages will be forwarded. And when the child is `Terminated`, the supervisor is also - * `Terminated`. - * Transparent to the child means that the child does not have to be aware that it is being - * supervised specifically by this actor. Just like it does - * not need to know when it is being supervised by the usual implicit supervisors. - * The only caveat is that the `ActorRef` of the child is not stable, so any user storing the - * `sender()` `ActorRef` from the child response may eventually not be able to communicate with - * the stored `ActorRef`. In general all messages to the child should be directed through this actor. - * - * An example of where this supervisor might be used is when you may have an actor that is - * responsible for continuously polling on a server for some resource that sometimes may be down. - * Instead of hammering the server continuously when the resource is unavailable, the actor will - * be restarted with an exponentially increasing back off until the resource is available again. - * - * '''*** - * This supervisor should not be used with `Akka Persistence` child actors. - * `Akka Persistence` actors shutdown unconditionally on `persistFailure()`s rather - * than throw an exception on a failure like normal actors. - * [[#onStop]] should be used instead for cases where the child actor - * terminates itself as a failure signal instead of the normal behavior of throwing an exception. - * ***''' - * You can define another - * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. - * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration - * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. */ def onFailure( childProps: Props, @@ -138,7 +66,7 @@ object Backoff { minBackoff: FiniteDuration, maxBackoff: FiniteDuration, randomFactor: Double): BackoffOnFailureOptions = - BackoffOptionsImpl(RestartImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor) + BackoffOnFailureOptionsImpl(childProps, childName, minBackoff, maxBackoff, randomFactor) /** * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. @@ -174,84 +102,25 @@ object Backoff { * terminates itself as a failure signal instead of the normal behavior of throwing an exception. * ***''' * You can define another - * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. + * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOnFailureOptions]]. * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration - * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. - * @param maxNrOfRetries maximum number of attempts to restart the child actor. - * The supervisor will terminate itself after the maxNoOfRetries is reached. - * In order to restart infinitely pass in `-1`. + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. */ - def onFailure( - childProps: Props, - childName: String, - minBackoff: java.time.Duration, - maxBackoff: java.time.Duration, - randomFactor: Double, - maxNrOfRetries: Int): BackoffOnFailureOptions = - onFailure(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, maxNrOfRetries) - - /** - * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to restart on failure. - * - * This explicit supervisor behaves similarly to the normal implicit supervision where - * if an actor throws an exception, the decider on the supervisor will decide when to - * `Stop`, `Restart`, `Escalate`, `Resume` the child actor. - * - * When the `Restart` directive is specified, the supervisor will delay the restart - * using an exponential back off strategy (bounded by minBackoff and maxBackoff). - * - * This supervisor is intended to be transparent to both the child actor and external actors. - * Where external actors can send messages to the supervisor as if it was the child and the - * messages will be forwarded. And when the child is `Terminated`, the supervisor is also - * `Terminated`. - * Transparent to the child means that the child does not have to be aware that it is being - * supervised specifically by this actor. Just like it does - * not need to know when it is being supervised by the usual implicit supervisors. - * The only caveat is that the `ActorRef` of the child is not stable, so any user storing the - * `sender()` `ActorRef` from the child response may eventually not be able to communicate with - * the stored `ActorRef`. In general all messages to the child should be directed through this actor. - * - * An example of where this supervisor might be used is when you may have an actor that is - * responsible for continuously polling on a server for some resource that sometimes may be down. - * Instead of hammering the server continuously when the resource is unavailable, the actor will - * be restarted with an exponentially increasing back off until the resource is available again. - * - * '''*** - * This supervisor should not be used with `Akka Persistence` child actors. - * `Akka Persistence` actors shutdown unconditionally on `persistFailure()`s rather - * than throw an exception on a failure like normal actors. - * [[#onStop]] should be used instead for cases where the child actor - * terminates itself as a failure signal instead of the normal behavior of throwing an exception. - * ***''' - * You can define another - * supervision strategy by using `akka.pattern.BackoffOptions.withSupervisorStrategy` on [[akka.pattern.BackoffOptions]]. - * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration - * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. - */ - @deprecated("Use the overloaded one which accepts maxNrOfRetries instead.", "2.5.17") def onFailure( childProps: Props, childName: String, minBackoff: java.time.Duration, maxBackoff: java.time.Duration, randomFactor: Double): BackoffOnFailureOptions = - onFailure(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, -1) + onFailure(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor) /** * Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. @@ -292,84 +161,19 @@ object Backoff { * if it wants to do an intentional stop. * * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using - * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A + * [[BackoffOnStopOptions#withSupervisorStrategy]] or [[BackoffOnStopOptions#withDefaultStoppingStrategy]]. A * `Restart` will perform a normal immediate restart of the child. A `Stop` will * stop the child, but it will be started again after the back-off duration. * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. - * @param maxNrOfRetries maximum number of attempts to restart the child actor. - * The supervisor will terminate itself after the maxNoOfRetries is reached. - * In order to restart infinitely pass in `-1`. - */ - def onStop( - childProps: Props, - childName: String, - minBackoff: FiniteDuration, - maxBackoff: FiniteDuration, - randomFactor: Double, - maxNrOfRetries: Int): BackoffOnStopOptions = - BackoffOptionsImpl(StopImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor).withMaxNrOfRetries(maxNrOfRetries) - - /** - * Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. - * - * This actor can be used to supervise a child actor and start it again - * after a back-off duration if the child actor is stopped. - * - * This is useful in situations where the re-start of the child actor should be - * delayed e.g. in order to give an external resource time to recover before the - * child actor tries contacting it again (after being restarted). - * - * Specifically this pattern is useful for persistent actors, - * which are stopped in case of persistence failures. - * Just restarting them immediately would probably fail again (since the data - * store is probably unavailable). It is better to try again after a delay. - * - * It supports exponential back-off between the given `minBackoff` and - * `maxBackoff` durations. For example, if `minBackoff` is 3 seconds and - * `maxBackoff` 30 seconds the start attempts will be delayed with - * 3, 6, 12, 24, 30, 30 seconds. The exponential back-off counter is reset - * if the actor is not terminated within the `minBackoff` duration. - * - * In addition to the calculated exponential back-off an additional - * random delay based the given `randomFactor` is added, e.g. 0.2 adds up to 20% - * delay. The reason for adding a random delay is to avoid that all failing - * actors hit the backend resource at the same time. - * - * You can retrieve the current child `ActorRef` by sending `BackoffSupervisor.GetCurrentChild` - * message to this actor and it will reply with [[akka.pattern.BackoffSupervisor.CurrentChild]] - * containing the `ActorRef` of the current child, if any. - * - * The `BackoffSupervisor`delegates all messages from the child to the parent of the - * `BackoffSupervisor`, with the supervisor as sender. - * - * The `BackoffSupervisor` forwards all other messages to the child, if it is currently running. - * - * The child can stop itself and send a [[akka.actor.PoisonPill]] to the parent supervisor - * if it wants to do an intentional stop. - * - * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using - * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A - * `Restart` will perform a normal immediate restart of the child. A `Stop` will - * stop the child, but it will be started again after the back-off duration. - * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration - * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. */ def onStop( childProps: Props, @@ -377,7 +181,7 @@ object Backoff { minBackoff: FiniteDuration, maxBackoff: FiniteDuration, randomFactor: Double): BackoffOnStopOptions = - BackoffOptionsImpl(StopImpliesFailure, childProps, childName, minBackoff, maxBackoff, randomFactor) + BackoffOnStopOptionsImpl(childProps, childName, minBackoff, maxBackoff, randomFactor) /** * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. @@ -418,158 +222,83 @@ object Backoff { * if it wants to do an intentional stop. * * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using - * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A + * [[BackoffOnStopOptions#withSupervisorStrategy]] or [[BackoffOnStopOptions#withDefaultStoppingStrategy]]. A * `Restart` will perform a normal immediate restart of the child. A `Stop` will * stop the child, but it will be started again after the back-off duration. * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration - * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. - * @param maxNrOfRetries maximum number of attempts to restart the child actor. - * The supervisor will terminate itself after the maxNoOfRetries is reached. - * In order to restart infinitely pass in `-1`. + * @param childProps the [[akka.actor.Props]] of the child actor that + * will be started and supervised + * @param childName name of the child actor + * @param minBackoff minimum (initial) duration until the child actor will + * started again, if it is terminated + * @param maxBackoff the exponential back-off is capped to this duration + * @param randomFactor after calculation of the exponential back-off an additional + * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. + * In order to skip this additional delay pass in `0`. */ - def onStop( - childProps: Props, - childName: String, - minBackoff: java.time.Duration, - maxBackoff: java.time.Duration, - randomFactor: Double, - maxNrOfRetries: Int): BackoffOnStopOptions = - onStop(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, maxNrOfRetries) - - /** - * Java API: Back-off options for creating a back-off supervisor actor that expects a child actor to stop on failure. - * - * This actor can be used to supervise a child actor and start it again - * after a back-off duration if the child actor is stopped. - * - * This is useful in situations where the re-start of the child actor should be - * delayed e.g. in order to give an external resource time to recover before the - * child actor tries contacting it again (after being restarted). - * - * Specifically this pattern is useful for persistent actors, - * which are stopped in case of persistence failures. - * Just restarting them immediately would probably fail again (since the data - * store is probably unavailable). It is better to try again after a delay. - * - * It supports exponential back-off between the given `minBackoff` and - * `maxBackoff` durations. For example, if `minBackoff` is 3 seconds and - * `maxBackoff` 30 seconds the start attempts will be delayed with - * 3, 6, 12, 24, 30, 30 seconds. The exponential back-off counter is reset - * if the actor is not terminated within the `minBackoff` duration. - * - * In addition to the calculated exponential back-off an additional - * random delay based the given `randomFactor` is added, e.g. 0.2 adds up to 20% - * delay. The reason for adding a random delay is to avoid that all failing - * actors hit the backend resource at the same time. - * - * You can retrieve the current child `ActorRef` by sending `BackoffSupervisor.GetCurrentChild` - * message to this actor and it will reply with [[akka.pattern.BackoffSupervisor.CurrentChild]] - * containing the `ActorRef` of the current child, if any. - * - * The `BackoffSupervisor`delegates all messages from the child to the parent of the - * `BackoffSupervisor`, with the supervisor as sender. - * - * The `BackoffSupervisor` forwards all other messages to the child, if it is currently running. - * - * The child can stop itself and send a [[akka.actor.PoisonPill]] to the parent supervisor - * if it wants to do an intentional stop. - * - * Exceptions in the child are handled with the default supervisionStrategy, which can be changed by using - * [[BackoffOptions#withSupervisorStrategy]] or [[BackoffOptions#withDefaultStoppingStrategy]]. A - * `Restart` will perform a normal immediate restart of the child. A `Stop` will - * stop the child, but it will be started again after the back-off duration. - * - * @param childProps the [[akka.actor.Props]] of the child actor that - * will be started and supervised - * @param childName name of the child actor - * @param minBackoff minimum (initial) duration until the child actor will - * started again, if it is terminated - * @param maxBackoff the exponential back-off is capped to this duration - * @param randomFactor after calculation of the exponential back-off an additional - * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. - * In order to skip this additional delay pass in `0`. - */ - @deprecated("Use the overloaded one which accepts maxNrOfRetries instead.", "2.5.17") def onStop( childProps: Props, childName: String, minBackoff: java.time.Duration, maxBackoff: java.time.Duration, randomFactor: Double): BackoffOnStopOptions = - onStop(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor, -1) - + onStop(childProps, childName, minBackoff.asScala, maxBackoff.asScala, randomFactor) } -/** - * Configures a back-off supervisor actor. Start with `Backoff.onStop` or `Backoff.onFailure`. - * BackoffOptions is immutable, so be sure to chain methods like: - * {{{ - * val options = Backoff.onFailure(childProps, childName, minBackoff, maxBackoff, randomFactor) - * .withManualReset - * context.actorOf(BackoffSupervisor.props(options), name) - * }}} - */ @DoNotInherit -trait BackoffOptions { +private[akka] sealed trait ExtendedBackoffOptions[T <: ExtendedBackoffOptions[T]] { + /** * Returns a new BackoffOptions with automatic back-off reset. * The back-off algorithm is reset if the child does not crash within the specified `resetBackoff`. + * * @param resetBackoff The back-off is reset if the child does not crash within this duration. */ - def withAutoReset(resetBackoff: FiniteDuration): BackoffOptions + def withAutoReset(resetBackoff: FiniteDuration): T /** * Returns a new BackoffOptions with manual back-off reset. The back-off is only reset * if the child sends a `BackoffSupervisor.Reset` to its parent (the backoff-supervisor actor). */ - def withManualReset: BackoffOptions + def withManualReset: T /** * Returns a new BackoffOptions with the supervisorStrategy. + * * @param supervisorStrategy the supervisorStrategy that the back-off supervisor will use. - * The default supervisor strategy is used as fallback if the specified supervisorStrategy (its decider) - * does not explicitly handle an exception. As the BackoffSupervisor creates a separate actor to handle the - * backoff process, only a [[OneForOneStrategy]] makes sense here. - * Note that changing the strategy will replace the previously defined maxNrOfRetries. + * The default supervisor strategy is used as fallback if the specified supervisorStrategy (its decider) + * does not explicitly handle an exception. As the BackoffSupervisor creates a separate actor to handle the + * backoff process, only a [[OneForOneStrategy]] makes sense here. + * Note that changing the strategy will replace the previously defined maxNrOfRetries. */ - def withSupervisorStrategy(supervisorStrategy: OneForOneStrategy): BackoffOptions + def withSupervisorStrategy(supervisorStrategy: OneForOneStrategy): T /** * Returns a new BackoffOptions with a default `SupervisorStrategy.stoppingStrategy`. * The default supervisor strategy is used as fallback for throwables not handled by `SupervisorStrategy.stoppingStrategy`. */ - def withDefaultStoppingStrategy: BackoffOptions + def withDefaultStoppingStrategy: T /** * Returns a new BackoffOptions with a maximum number of retries to restart the child actor. * By default, the supervisor will retry infinitely. * With this option, the supervisor will terminate itself after the maxNoOfRetries is reached. + * * @param maxNrOfRetries the number of times a child actor is allowed to be restarted. * If negative, the value is unbounded, otherwise the provided * limit is used. If the limit is exceeded the child actor will be stopped. */ - def withMaxNrOfRetries(maxNrOfRetries: Int): BackoffOptions + def withMaxNrOfRetries(maxNrOfRetries: Int): T /** * Returns a new BackoffOptions with a constant reply to messages that the supervisor receives while its * child is stopped. By default, a message received while the child is stopped is forwarded to `deadLetters`. * With this option, the supervisor will reply to the sender instead. + * * @param replyWhileStopped The message that the supervisor will send in response to all messages while - * its child is stopped. + * its child is stopped. */ - def withReplyWhileStopped(replyWhileStopped: Any): BackoffOptions - - // for backwards compability with 2.5.19 - @deprecated("Use through BackoffOnStopOptions instead of BackoffOptions", since = "2.5.20") - def withFinalStopMessage(isFinalStopMessage: Any ⇒ Boolean): BackoffOptions + def withReplyWhileStopped(replyWhileStopped: Any): T /** * Returns the props to create the back-off supervisor. @@ -577,7 +306,8 @@ trait BackoffOptions { private[akka] def props: Props } -sealed trait BackoffOnStopOptions extends BackoffOptions { +@DoNotInherit +sealed trait BackoffOnStopOptions extends ExtendedBackoffOptions[BackoffOnStopOptions] { /** * Predicate evaluated for each message, if it returns true and the supervised actor is @@ -585,17 +315,13 @@ sealed trait BackoffOnStopOptions extends BackoffOptions { * the supervised actor is running then it will be forwarded to the supervised actor and * when the supervised actor stops its self the supervisor will stop its self. */ - def withFinalStopMessage(isFinalStopMessage: Any ⇒ Boolean): BackoffOptions + def withFinalStopMessage(isFinalStopMessage: Any ⇒ Boolean): BackoffOnStopOptions } -sealed trait BackoffOnFailureOptions extends BackoffOptions { - // for backwards compability with 2.5.19 - @deprecated("This has no effect for backoff on failure", since = "2.5.20") - def withFinalStopMessage(isFinalStopMessage: Any ⇒ Boolean): BackoffOptions -} +@DoNotInherit +sealed trait BackoffOnFailureOptions extends ExtendedBackoffOptions[BackoffOnFailureOptions] -private final case class BackoffOptionsImpl( - backoffType: BackoffType = RestartImpliesFailure, +private final case class BackoffOnStopOptionsImpl[T]( childProps: Props, childName: String, minBackoff: FiniteDuration, @@ -605,7 +331,7 @@ private final case class BackoffOptionsImpl( supervisorStrategy: OneForOneStrategy = OneForOneStrategy()(SupervisorStrategy.defaultStrategy.decider), replyWhileStopped: Option[Any] = None, finalStopMessage: Option[Any ⇒ Boolean] = None -) extends BackoffOptions with BackoffOnStopOptions with BackoffOnFailureOptions { +) extends BackoffOnStopOptions { val backoffReset = reset.getOrElse(AutoReset(minBackoff)) @@ -627,20 +353,43 @@ private final case class BackoffOptionsImpl( case _ ⇒ // ignore } - backoffType match { - //onFailure method in companion object - case RestartImpliesFailure ⇒ - Props(new BackoffOnRestartSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped)) - //onStop method in companion object - case StopImpliesFailure ⇒ - Props(new BackoffOnStopSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped, finalStopMessage)) - } + Props(new BackoffOnStopSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped, finalStopMessage)) } } -private sealed trait BackoffType -private final case object StopImpliesFailure extends BackoffType -private final case object RestartImpliesFailure extends BackoffType +private final case class BackoffOnFailureOptionsImpl[T]( + childProps: Props, + childName: String, + minBackoff: FiniteDuration, + maxBackoff: FiniteDuration, + randomFactor: Double, + reset: Option[BackoffReset] = None, + supervisorStrategy: OneForOneStrategy = OneForOneStrategy()(SupervisorStrategy.defaultStrategy.decider), + replyWhileStopped: Option[Any] = None +) extends BackoffOnFailureOptions { + + val backoffReset = reset.getOrElse(AutoReset(minBackoff)) + + def withAutoReset(resetBackoff: FiniteDuration) = copy(reset = Some(AutoReset(resetBackoff))) + def withManualReset = copy(reset = Some(ManualReset)) + def withSupervisorStrategy(supervisorStrategy: OneForOneStrategy) = copy(supervisorStrategy = supervisorStrategy) + def withDefaultStoppingStrategy = copy(supervisorStrategy = OneForOneStrategy(supervisorStrategy.maxNrOfRetries)(SupervisorStrategy.stoppingStrategy.decider)) + def withReplyWhileStopped(replyWhileStopped: Any) = copy(replyWhileStopped = Some(replyWhileStopped)) + def withMaxNrOfRetries(maxNrOfRetries: Int) = copy(supervisorStrategy = supervisorStrategy.withMaxNrOfRetries(maxNrOfRetries)) + + def props = { + require(minBackoff > Duration.Zero, "minBackoff must be > 0") + require(maxBackoff >= minBackoff, "maxBackoff must be >= minBackoff") + require(0.0 <= randomFactor && randomFactor <= 1.0, "randomFactor must be between 0.0 and 1.0") + backoffReset match { + case AutoReset(resetBackoff) ⇒ + require(minBackoff <= resetBackoff && resetBackoff <= maxBackoff) + case _ ⇒ // ignore + } + + Props(new BackoffOnRestartSupervisor(childProps, childName, minBackoff, maxBackoff, backoffReset, randomFactor, supervisorStrategy, replyWhileStopped)) + } +} private[akka] sealed trait BackoffReset private[akka] final case object ManualReset extends BackoffReset diff --git a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala index 1e848ba76e..8afb50f4f6 100644 --- a/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala +++ b/akka-actor/src/main/scala/akka/pattern/BackoffSupervisor.scala @@ -32,6 +32,7 @@ object BackoffSupervisor { * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. * In order to skip this additional delay pass in `0`. */ + @deprecated("Use props with BackoffOptions instead", since = "2.5.20") def props( childProps: Props, childName: String, @@ -61,6 +62,7 @@ object BackoffSupervisor { * The supervisor will terminate itself after the maxNoOfRetries is reached. * In order to restart infinitely pass in `-1`. */ + @deprecated("Use props with BackoffOptions instead", since = "2.5.20") def props( childProps: Props, childName: String, @@ -92,6 +94,7 @@ object BackoffSupervisor { * random delay based on this factor is added, e.g. `0.2` adds up to `20%` delay. * In order to skip this additional delay pass in `0`. */ + @deprecated("Use props with BackoffOptions instead", since = "2.5.20") def props( childProps: Props, childName: String, @@ -121,6 +124,7 @@ object BackoffSupervisor { * The supervisor will terminate itself after the maxNoOfRetries is reached. * In order to restart infinitely pass in `-1`. */ + @deprecated("Use props with BackoffOptions instead", since = "2.5.20") def props( childProps: Props, childName: String, @@ -152,6 +156,7 @@ object BackoffSupervisor { * in the child. As the BackoffSupervisor creates a separate actor to handle the * backoff process, only a [[OneForOneStrategy]] makes sense here. */ + @deprecated("Use props with BackoffOptions instead", since = "2.5.20") def propsWithSupervisorStrategy( childProps: Props, childName: String, @@ -186,6 +191,7 @@ object BackoffSupervisor { * in the child. As the BackoffSupervisor creates a separate actor to handle the * backoff process, only a [[OneForOneStrategy]] makes sense here. */ + @deprecated("Use props with BackoffOptions instead", since = "2.5.20") def propsWithSupervisorStrategy( childProps: Props, childName: String, @@ -201,8 +207,23 @@ object BackoffSupervisor { * * @param options the [[BackoffOptions]] that specify how to construct a backoff-supervisor. */ + @deprecated("Use new API from BackoffOptions object instead", since = "2.5.20") def props(options: BackoffOptions): Props = options.props + /** + * Props for creating a `BackoffSupervisor` actor from [[BackoffOnStopOptions]]. + * + * @param options the [[BackoffOnStopOptions]] that specify how to construct a backoff-supervisor. + */ + def props(options: BackoffOnStopOptions): Props = options.props + + /** + * Props for creating a `BackoffSupervisor` actor from [[BackoffOnFailureOptions]]. + * + * @param options the [[BackoffOnFailureOptions]] that specify how to construct a backoff-supervisor. + */ + def props(options: BackoffOnFailureOptions): Props = options.props + /** * Send this message to the `BackoffSupervisor` and it will reply with * [[BackoffSupervisor.CurrentChild]] containing the `ActorRef` of the current child, if any.