Restart(Source|Flow|Sink): Configurable stream restart deadline (#29591)

This commit is contained in:
r-glyde 2020-10-05 08:12:15 +01:00 committed by GitHub
parent 4cc3c58a08
commit a4acf23d05
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 727 additions and 347 deletions

View file

@ -6,22 +6,31 @@ Wrap the given @apidoc[Flow] with a @apidoc[Flow] that will restart it when it f
## Signature
@apidoc[RestartFlow.onFailuresWithBackoff](RestartFlow$) { scala="#onFailuresWithBackoff[In,Out](minBackoff:scala.concurrent.duration.FiniteDuration,maxBackoff:scala.concurrent.duration.FiniteDuration,randomFactor:Double,maxRestarts:Int)(flowFactory:()=>akka.stream.scaladsl.Flow[In,Out,_]):akka.stream.scaladsl.Flow[In,Out,akka.NotUsed]" java="#onFailuresWithBackoff(java.time.Duration,java.time.Duration,double,int,akka.japi.function.Creator)" }
@apidoc[RestartFlow.onFailuresWithBackoff](RestartFlow$) { scala="#onFailuresWithBackoff[In,Out](settings:akka.stream.RestartSettings)(flowFactory:()=>akka.stream.scaladsl.Flow[In,Out,_]):akka.stream.scaladsl.Flow[In,Out,akka.NotUsed]" java="#onFailuresWithBackoff(akka.stream.RestartSettings,akka.japi.function.Creator)" }
## Description
This @apidoc[Flow] will not emit any failure
The failures by the wrapped @apidoc[Flow] will be handled by
restarting the wrapping @apidoc[Flow] as long as maxRestarts is not reached.
Any termination signals sent to this @apidoc[Flow] however will terminate the wrapped @apidoc[Flow], if it's
Wrap the given @apidoc[Flow] with a @apidoc[Flow] that will restart it when it fails using exponential backoff.
The backoff resets back to `minBackoff` if there hasn't been a restart within `maxRestartsWithin` (which defaults to `minBackoff` if max restarts).
This @apidoc[Flow] will not emit any failure as long as maxRestarts is not reached.
The failure of the wrapped @apidoc[Flow] will be handled by restarting it.
However, any termination signals sent to this @apidoc[Flow] will terminate the wrapped @apidoc[Flow], if it's
running, and then the @apidoc[Flow] will be allowed to terminate without being restarted.
The restart process is inherently lossy, since there is no coordination between cancelling and the sending of
messages. A termination signal from either end of the wrapped @apidoc[Flow] will cause the other end to be terminated,
and any in transit messages will be lost. During backoff, this @apidoc[Flow] will backpressure.
This uses the same exponential backoff algorithm as @apidoc[Backoff$].
This uses the same exponential backoff algorithm as @apidoc[BackoffOpts$].
See also:
* @ref:[RestartSource.withBackoff](../RestartSource/withBackoff.md)
* @ref:[RestartSource.onFailuresWithBackoff](../RestartSource/onFailuresWithBackoff.md)
* @ref:[RestartFlow.withBackoff](../RestartFlow/withBackoff.md)
* @ref:[RestartSink.withBackoff](../RestartSink/withBackoff.md)
## Reactive Streams semantics
@ -31,4 +40,6 @@ This uses the same exponential backoff algorithm as @apidoc[Backoff$].
**backpressures** during backoff and when the wrapped flow backpressures
**completes** when the wrapped flow completes or `maxRestarts` are reached within the given time limit
@@@

View file

@ -6,20 +6,30 @@ Wrap the given @apidoc[Flow] with a @apidoc[Flow] that will restart it when it f
## Signature
@apidoc[RestartFlow.withBackoff](RestartFlow$) { scala="#withBackoff[In,Out](minBackoff:scala.concurrent.duration.FiniteDuration,maxBackoff:scala.concurrent.duration.FiniteDuration,randomFactor:Double)(flowFactory:()=>akka.stream.scaladsl.Flow[In,Out,_]):akka.stream.scaladsl.Flow[In,Out,akka.NotUsed]" java="#withBackoff(java.time.Duration,java.time.Duration,double,int,akka.japi.function.Creator)" }
@apidoc[RestartFlow.withBackoff](RestartFlow$) { scala="#withBackoff[In,Out](settings:akka.stream.RestartSettings)(flowFactory:()=>akka.stream.scaladsl.Flow[In,Out,_]):akka.stream.scaladsl.Flow[In,Out,akka.NotUsed]" java="#withBackoff(akka.stream.RestartSettings,akka.japi.function.Creator)" }
## Description
The resulting @apidoc[Flow] will not cancel, complete or emit a failure, until the opposite end of it has been cancelled or
completed. Any termination by the @apidoc[Flow] before that time will be handled by restarting it. Any termination
signals sent to this @apidoc[Flow] however will terminate the wrapped @apidoc[Flow], if it's running, and then the @apidoc[Flow]
will be allowed to terminate without being restarted.
Wrap the given @apidoc[Flow] with a @apidoc[Flow] that will restart it when it completes or fails using exponential backoff.
The backoff resets back to `minBackoff` if there hasn't been a restart within `maxRestartsWithin` (which defaults to `minBackoff`).
This @apidoc[Flow] will not cancel, complete or emit a failure, until the opposite end of it has been cancelled or
completed. Any termination by the @apidoc[Flow] before that time will be handled by restarting it as long as maxRestarts
is not reached. Any termination signals sent to this @apidoc[Flow] however will terminate the wrapped @apidoc[Flow], if it's
running, and then the @apidoc[Flow] will be allowed to terminate without being restarted.
The restart process is inherently lossy, since there is no coordination between cancelling and the sending of
messages. A termination signal from either end of the wrapped @apidoc[Flow] will cause the other end to be terminated,
and any in transit messages will be lost. During backoff, this @apidoc[Flow] will backpressure.
This uses the same exponential backoff algorithm as @apidoc[Backoff$].
This uses the same exponential backoff algorithm as @apidoc[BackoffOpts$].
See also:
* @ref:[RestartSource.withBackoff](../RestartSource/withBackoff.md)
* @ref:[RestartSource.onFailuresWithBackoff](../RestartSource/onFailuresWithBackoff.md)
* @ref:[RestartFlow.onFailuresWithBackoff](../RestartFlow/onFailuresWithBackoff.md)
* @ref:[RestartSink.withBackoff](../RestartSink/withBackoff.md)
## Reactive Streams semantics
@ -29,6 +39,6 @@ This uses the same exponential backoff algorithm as @apidoc[Backoff$].
**backpressures** during backoff and when the wrapped flow backpressures
**completes** when the wrapped flow completes
**completes** when `maxRestarts` are reached within the given time limit
@@@

View file

@ -6,19 +6,38 @@ Wrap the given @apidoc[Sink] with a @apidoc[Sink] that will restart it when it f
## Signature
@apidoc[RestartSink.withBackoff](RestartSink$) { scala="#withBackoff[T](minBackoff:scala.concurrent.duration.FiniteDuration,maxBackoff:scala.concurrent.duration.FiniteDuration,randomFactor:Double,maxRestarts:Int)(sinkFactory:()=>akka.stream.scaladsl.Sink[T,_]):akka.stream.scaladsl.Sink[T,akka.NotUsed]" java="#withBackoff(java.time.Duration,java.time.Duration,double,int,akka.japi.function.Creator)" }
@apidoc[RestartSink.withBackoff](RestartSink$) { scala="#withBackoff[T](settings:akka.stream.RestartSettings)(sinkFactory:()=>akka.stream.scaladsl.Sink[T,_]):akka.stream.scaladsl.Sink[T,akka.NotUsed]" java="#withBackoff(akka.stream.RestartSettings,akka.japi.function.Creator)" }
## Description
This @apidoc[Sink] will never cancel, since cancellation by the wrapped @apidoc[Sink] is always handled by restarting it.
The wrapped @apidoc[Sink] can however be completed by feeding a completion or error into this @apidoc[Sink]. When that
happens, the @apidoc[Sink], if currently running, will terminate and will not be restarted. This can be triggered
simply by the upstream completing, or externally by introducing a @apidoc[KillSwitch] right before this @apidoc[Sink] in the
graph.
Wrap the given @apidoc[Sink] with a @apidoc[Sink] that will restart it when it completes or fails using exponential backoff.
The backoff resets back to `minBackoff` if there hasn't been a restart within `maxRestartsWithin` (which defaults to `minBackoff`).
This @apidoc[Sink] will not cancel as long as maxRestarts is not reached, since cancellation by the wrapped @apidoc[Sink]
is handled by restarting it. The wrapped @apidoc[Sink] can however be completed by feeding a completion or error into
this @apidoc[Sink]. When that happens, the @apidoc[Sink], if currently running, will terminate and will not be restarted.
This can be triggered simply by the upstream completing, or externally by introducing a @ref[KillSwitch](../../stream-dynamic.md#controlling-stream-completion-with-killswitch) right
before this @apidoc[Sink] in the graph.
The restart process is inherently lossy, since there is no coordination between cancelling and the sending of
messages. When the wrapped @apidoc[Sink] does cancel, this @apidoc[Sink] will backpressure, however any elements already
sent may have been lost.
This uses the same exponential backoff algorithm as @apidoc[Backoff$].
This uses the same exponential backoff algorithm as @apidoc[BackoffOpts$].
See also:
* @ref:[RestartSource.withBackoff](../RestartSource/withBackoff.md)
* @ref:[RestartSource.onFailuresWithBackoff](../RestartSource/onFailuresWithBackoff.md)
* @ref:[RestartFlow.onFailuresWithBackoff](../RestartFlow/onFailuresWithBackoff.md)
* @ref:[RestartFlow.withBackoff](../RestartFlow/withBackoff.md)
## Reactive Streams semantics
@@@div { .callout }
**backpressures** during backoff and when the wrapped sink backpressures
**completes** when upstream completes or when `maxRestarts` are reached within the given time limit
@@@

View file

@ -1,26 +1,29 @@
# RestartSource.onFailuresWithBackoff
Wrap the given @apidoc[Source] with a @apidoc[Source] that will restart it when it fails using an exponential backoff.
Wrap the given @apidoc[Source] with a @apidoc[Source] that will restart it when it fails using an exponential backoff. Notice that this @apidoc[Source] will not restart on completion of the wrapped flow.
@ref[Error handling](../index.md#error-handling)
## Signature
@apidoc[RestartSource.onFailuresWithBackoff](RestartSource$) { scala="#onFailuresWithBackoff[T](minBackoff:scala.concurrent.duration.FiniteDuration,maxBackoff:scala.concurrent.duration.FiniteDuration,randomFactor:Double)(sourceFactory:()=>akka.stream.scaladsl.Source[T,_]):akka.stream.scaladsl.Source[T,akka.NotUsed]" java="#onFailuresWithBackoff(java.time.Duration,java.time.Duration,double,int,akka.japi.function.Creator)" }
@apidoc[RestartSource.onFailuresWithBackoff](RestartSource$) { scala="#onFailuresWithBackoff[T](settings:akka.stream.RestartSettings)(sourceFactory:()=>akka.stream.scaladsl.Source[T,_]):akka.stream.scaladsl.Source[T,akka.NotUsed]" java="#onFailuresWithBackoff(akka.stream.RestartSettings,akka.japi.function.Creator)" }
## Description
Wraps the given @apidoc[Source] with a @apidoc[Source] that will restart it when it fails using an exponential backoff.
The backoff resets back to `minBackoff` if there hasn't been a failure within `minBackoff`.
Wraps the given @apidoc[Source] with a @apidoc[Source] that will restart it when it fails using exponential backoff.
The backoff resets back to `minBackoff` if there hasn't been a restart within `maxRestartsWithin` (which defaults to `minBackoff`).
This @apidoc[Source] will never emit a failure, since the failure of the wrapped @apidoc[Source] is always handled by
restarting. The wrapped @apidoc[Source] can be completed by completing this @apidoc[Source].
When that happens, the wrapped @apidoc[Source], if currently running will be cancelled, and it will not be restarted.
This can be triggered by the downstream cancelling, or externally by introducing a @ref[KillSwitch](../../stream-dynamic.md#controlling-stream-completion-with-killswitch) right
after this @apidoc[Source] in the graph.
This @apidoc[Source] will not emit a failure as long as maxRestarts is not reached.
The failure of the wrapped @apidoc[Source] is handled by restarting it.
However, the wrapped @apidoc[Source] can be cancelled by cancelling this @apidoc[Source].
When that happens, the wrapped @apidoc[Source], if currently running will, be cancelled and not restarted.
This can be triggered by the downstream cancelling, or externally by introducing a @ref[KillSwitch](../../stream-dynamic.md#controlling-stream-completion-with-killswitch) right after this @apidoc[Source] in the graph.
This uses the same exponential backoff algorithm as @apidoc[BackoffOpts$].
See also:
* @ref:[RestartSource.withBackoff](../RestartSource/withBackoff.md)
* @ref:[RestartFlow.onFailuresWithBackoff](../RestartFlow/onFailuresWithBackoff.md)
* @ref:[RestartFlow.withBackoff](../RestartFlow/withBackoff.md)
* @ref:[RestartSink.withBackoff](../RestartSink/withBackoff.md)
@ -61,4 +64,10 @@ Java
**emits** when the wrapped source emits
**backpressures** during backoff and when downstream backpressures
**completes** when the wrapped source completes or `maxRestarts` are reached within the given time limit
**cancels** when downstream cancels
@@@

View file

@ -1,22 +1,33 @@
# RestartSource.withBackoff
Wrap the given @apidoc[Source] with a @apidoc[Source] that will restart it when it fails or complete using an exponential backoff.
Wrap the given @apidoc[Source] with a @apidoc[Source] that will restart it when it fails or completes using an exponential backoff.
@ref[Error handling](../index.md#error-handling)
## Signature
@apidoc[RestartSource.withBackoff](RestartSource$) { scala="#withBackoff[T](minBackoff:scala.concurrent.duration.FiniteDuration,maxBackoff:scala.concurrent.duration.FiniteDuration,randomFactor:Double,maxRestarts:Int)(sourceFactory:()=>akka.stream.scaladsl.Source[T,_]):akka.stream.scaladsl.Source[T,akka.NotUsed]" java="#withBackoff(java.time.Duration,java.time.Duration,double,int,akka.japi.function.Creator)" }
@apidoc[RestartSource.withBackoff](RestartSource$) { scala="#withBackoff[T](settings:akka.stream.RestartSettings)(sourceFactory:()=>akka.stream.scaladsl.Source[T,_]):akka.stream.scaladsl.Source[T,akka.NotUsed]" java="#withBackoff(akka.stream.RestartSettings,akka.japi.function.Creator)" }
## Description
This @apidoc[Flow] will never emit a complete or failure, since the completion or failure of the wrapped @apidoc[Source]
is always handled by restarting it. The wrapped @apidoc[Source] can however be cancelled by cancelling this @apidoc[Source].
When that happens, the wrapped @apidoc[Source], if currently running will be cancelled, and it will not be restarted.
This can be triggered simply by the downstream cancelling, or externally by introducing a @apidoc[KillSwitch] right
Wrap the given @apidoc[Source] with a @apidoc[Source] that will restart it when it completes or fails using exponential backoff.
The backoff resets back to `minBackoff` if there hasn't been a restart within `maxRestartsWithin` (which defaults to `minBackoff`).
This @apidoc[Source] will not emit a complete or fail as long as maxRestarts is not reached, since the completion
or failure of the wrapped @apidoc[Source] is handled by restarting it. The wrapped @apidoc[Source] can however be cancelled
by cancelling this @apidoc[Source]. When that happens, the wrapped @apidoc[Source], if currently running, will be cancelled,
and it will not be restarted.
This can be triggered simply by the downstream cancelling, or externally by introducing a @ref[KillSwitch](../../stream-dynamic.md#controlling-stream-completion-with-killswitch) right
after this @apidoc[Source] in the graph.
This uses the same exponential backoff algorithm as @apidoc[Backoff$].
This uses the same exponential backoff algorithm as @apidoc[BackoffOpts$].
See also:
* @ref:[RestartSource.onFailuresWithBackoff](../RestartSource/onFailuresWithBackoff.md)
* @ref:[RestartFlow.onFailuresWithBackoff](../RestartFlow/onFailuresWithBackoff.md)
* @ref:[RestartFlow.withBackoff](../RestartFlow/withBackoff.md)
* @ref:[RestartSink.withBackoff](../RestartSink/withBackoff.md)
## Reactive Streams semantics
@ -24,6 +35,10 @@ This uses the same exponential backoff algorithm as @apidoc[Backoff$].
**emits** when the wrapped source emits
**completes** when the wrapped source completes
**backpressures** during backoff and when downstream backpressures
**completes** when `maxRestarts` are reached within the given time limit
**cancels** when downstream cancels
@@@

View file

@ -344,9 +344,9 @@ For more background see the @ref[Error Handling in Streams](../stream-error.md)
| |Operator|Description|
|--|--|--|
|RestartSource|<a name="onfailureswithbackoff"></a>@ref[onFailuresWithBackoff](RestartSource/onFailuresWithBackoff.md)|Wrap the given @apidoc[Source] with a @apidoc[Source] that will restart it when it fails using an exponential backoff.|
|RestartSource|<a name="onfailureswithbackoff"></a>@ref[onFailuresWithBackoff](RestartSource/onFailuresWithBackoff.md)|Wrap the given @apidoc[Source] with a @apidoc[Source] that will restart it when it fails using an exponential backoff. Notice that this @apidoc[Source] will not restart on completion of the wrapped flow.|
|RestartFlow|<a name="onfailureswithbackoff"></a>@ref[onFailuresWithBackoff](RestartFlow/onFailuresWithBackoff.md)|Wrap the given @apidoc[Flow] with a @apidoc[Flow] that will restart it when it fails using an exponential backoff. Notice that this @apidoc[Flow] will not restart on completion of the wrapped flow.|
|RestartSource|<a name="withbackoff"></a>@ref[withBackoff](RestartSource/withBackoff.md)|Wrap the given @apidoc[Source] with a @apidoc[Source] that will restart it when it fails or complete using an exponential backoff.|
|RestartSource|<a name="withbackoff"></a>@ref[withBackoff](RestartSource/withBackoff.md)|Wrap the given @apidoc[Source] with a @apidoc[Source] that will restart it when it fails or completes using an exponential backoff.|
|RestartFlow|<a name="withbackoff"></a>@ref[withBackoff](RestartFlow/withBackoff.md)|Wrap the given @apidoc[Flow] with a @apidoc[Flow] that will restart it when it fails or complete using an exponential backoff.|
|RestartSink|<a name="withbackoff"></a>@ref[withBackoff](RestartSink/withBackoff.md)|Wrap the given @apidoc[Sink] with a @apidoc[Sink] that will restart it when it fails or complete using an exponential backoff.|
|RetryFlow|<a name="withbackoff"></a>@ref[withBackoff](RetryFlow/withBackoff.md)|Wrap the given @apidoc[Flow] and retry individual elements in that stream with an exponential backoff. A decider function tests every emitted element and can return a new element to be sent to the wrapped flow for another try.|

View file

@ -114,6 +114,15 @@ when a WebSocket connection fails due to the HTTP server it's running on going d
By using an exponential backoff, we avoid going into a tight reconnect loop, which both gives the HTTP server some time
to recover, and it avoids using needless resources on the client side.
The various restart shapes mentioned all expect an `akka.stream.RestartSettings` which configures the restart behaviour.
Configurable parameters are:
* `minBackoff` is the initial duration until the underlying stream is restarted
* `maxBackoff` caps the exponential backoff
* `randomFactor` allows addition of a random delay following backoff calculation
* `maxRestarts` caps the total number of restarts
* `maxRestartsWithin` sets a timeframe during which restarts are counted towards the same total for `maxRestarts`
The following snippet shows how to create a backoff supervisor using @scala[`akka.stream.scaladsl.RestartSource`]
@java[`akka.stream.javadsl.RestartSource`] which will supervise the given `Source`. The `Source` in this case is a
stream of Server Sent Events, produced by akka-http. If the stream fails or completes at any point, the request will

View file

@ -9,6 +9,7 @@ import akka.actor.ActorSystem;
import akka.stream.KillSwitch;
import akka.stream.KillSwitches;
import akka.stream.Materializer;
import akka.stream.RestartSettings;
import akka.stream.javadsl.Keep;
import akka.stream.javadsl.RestartSource;
import akka.stream.javadsl.Sink;
@ -61,12 +62,18 @@ public class RestartDocTest {
public void recoverWithBackoffSource() {
// #restart-with-backoff-source
RestartSettings settings =
RestartSettings.create(
Duration.ofSeconds(3), // min backoff
Duration.ofSeconds(30), // max backoff
0.2 // adds 20% "noise" to vary the intervals slightly
)
.withMaxRestarts(
20, Duration.ofMinutes(5)); // limits the amount of restarts to 20 within 5 minutes
Source<ServerSentEvent, NotUsed> eventStream =
RestartSource.withBackoff(
Duration.ofSeconds(3), // min backoff
Duration.ofSeconds(30), // max backoff
0.2, // adds 20% "noise" to vary the intervals slightly
20, // limits the amount of restarts to 20
settings,
() ->
// Create a source from a future of a source
Source.completionStageSource(

View file

@ -8,6 +8,7 @@ import akka.NotUsed;
import akka.actor.Cancellable;
import akka.japi.Creator;
import akka.stream.KillSwitches;
import akka.stream.RestartSettings;
import akka.stream.UniqueKillSwitch;
import akka.stream.javadsl.Keep;
import akka.stream.javadsl.RestartSource;
@ -34,7 +35,8 @@ public class Restart {
}));
Source<Creator<Integer>, NotUsed> forever =
RestartSource.onFailuresWithBackoff(
Duration.ofSeconds(1), Duration.ofSeconds(10), 0.1, () -> flakySource);
RestartSettings.create(Duration.ofSeconds(1), Duration.ofSeconds(10), 0.1),
() -> flakySource);
forever.runWith(
Sink.foreach((Creator<Integer> nr) -> system.log().info("{}", nr.create())), system);
// logs
@ -99,7 +101,8 @@ public class Restart {
}));
UniqueKillSwitch stopRestarting =
RestartSource.onFailuresWithBackoff(
Duration.ofSeconds(1), Duration.ofSeconds(10), 0.1, () -> flakySource)
RestartSettings.create(Duration.ofSeconds(1), Duration.ofSeconds(10), 0.1),
() -> flakySource)
.viaMat(KillSwitches.single(), Keep.right())
.toMat(Sink.foreach(nr -> System.out.println("nr " + nr.create())), Keep.left())
.run(system);

View file

@ -5,7 +5,7 @@
package docs.stream
import akka.NotUsed
import akka.stream.KillSwitches
import akka.stream.{ KillSwitches, RestartSettings }
import akka.stream.scaladsl._
import akka.testkit.AkkaSpec
import docs.CompileOnlySpec
@ -34,12 +34,13 @@ class RestartDocSpec extends AkkaSpec with CompileOnlySpec {
"demonstrate a restart with backoff source" in compileOnlySpec {
//#restart-with-backoff-source
val restartSource = RestartSource.withBackoff(
val settings = RestartSettings(
minBackoff = 3.seconds,
maxBackoff = 30.seconds,
randomFactor = 0.2, // adds 20% "noise" to vary the intervals slightly
maxRestarts = 20 // limits the amount of restarts to 20
) { () =>
randomFactor = 0.2 // adds 20% "noise" to vary the intervals slightly
).withMaxRestarts(20, 5.minutes) // limits the amount of restarts to 20 within 5 minutes
val restartSource = RestartSource.withBackoff(settings) { () =>
// Create a source from a future of a source
Source.futureSource {
// Make a single request with akka-http

View file

@ -6,8 +6,7 @@ package docs.stream.operators.source
import akka.NotUsed
import akka.actor.ActorSystem
import akka.stream.KillSwitches
import akka.stream.UniqueKillSwitch
import akka.stream.{ KillSwitches, RestartSettings, UniqueKillSwitch }
import akka.stream.scaladsl.Keep
import akka.stream.scaladsl.RestartSource
import akka.stream.scaladsl.Sink
@ -31,7 +30,8 @@ object Restart extends App {
val flakySource: Source[() => Int, NotUsed] =
Source(List(() => 1, () => 2, () => 3, () => throw CantConnectToDatabase("darn")))
val forever =
RestartSource.onFailuresWithBackoff(minBackoff = 1.second, maxBackoff = 10.seconds, 0.1)(() => flakySource)
RestartSource.onFailuresWithBackoff(
RestartSettings(minBackoff = 1.second, maxBackoff = 10.seconds, randomFactor = 0.1))(() => flakySource)
forever.runWith(Sink.foreach(nr => system.log.info("{}", nr())))
// logs
//[INFO] [12/10/2019 13:51:58.300] [default-akka.test.stream-dispatcher-7] [akka.actor.ActorSystemImpl(default)] 1
@ -56,7 +56,7 @@ object Restart extends App {
//#restart-failure-inner-complete
val finiteSource = Source.tick(1.second, 1.second, "tick").take(3)
val forever = RestartSource.onFailuresWithBackoff(1.second, 10.seconds, 0.1)(() => finiteSource)
val forever = RestartSource.onFailuresWithBackoff(RestartSettings(1.second, 10.seconds, 0.1))(() => finiteSource)
forever.runWith(Sink.foreach(println))
// prints
// tick
@ -71,7 +71,7 @@ object Restart extends App {
Source(List(() => 1, () => 2, () => 3, () => throw CantConnectToDatabase("darn")))
val stopRestarting: UniqueKillSwitch =
RestartSource
.onFailuresWithBackoff(1.second, 10.seconds, 0.1)(() => flakySource)
.onFailuresWithBackoff(RestartSettings(1.second, 10.seconds, 0.1))(() => flakySource)
.viaMat(KillSwitches.single)(Keep.right)
.toMat(Sink.foreach(nr => println(s"Nr ${nr()}")))(Keep.left)
.run()