This is an automated email from the ASF dual-hosted git repository.

mdedetrich pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-pekko.git


The following commit(s) were added to refs/heads/main by this push:
     new b818925edc Replace SubstreamCancelStrategy with SupervisionDecider for 
Split
b818925edc is described below

commit b818925edc28f14939a10b7c468b3a7bdeb3946b
Author: Matthew de Detrich <[email protected]>
AuthorDate: Sat Mar 18 12:15:46 2023 +0100

    Replace SubstreamCancelStrategy with SupervisionDecider for Split
---
 .../paradox/project/migration-guide-1.0.x-1.1.x.md | 72 ++++++++++++++++++++++
 docs/src/main/paradox/project/migration-guides.md  | 10 ++-
 .../stream/operators/Source-or-Flow/splitAfter.md  |  6 +-
 .../stream/operators/Source-or-Flow/splitWhen.md   |  6 +-
 .../pekko/stream/scaladsl/FlowSplitAfterSpec.scala | 15 +++--
 .../pekko/stream/scaladsl/FlowSplitWhenSpec.scala  | 36 ++++++++---
 .../pekko/stream/SubstreamCancelStrategy.scala     | 10 +++
 .../pekko/stream/impl/fusing/StreamOfStreams.scala | 46 ++++++++------
 .../org/apache/pekko/stream/javadsl/Flow.scala     |  6 ++
 .../org/apache/pekko/stream/javadsl/Source.scala   |  6 ++
 .../org/apache/pekko/stream/scaladsl/Flow.scala    | 56 ++++++++++++++---
 11 files changed, 218 insertions(+), 51 deletions(-)

diff --git a/docs/src/main/paradox/project/migration-guide-1.0.x-1.1.x.md 
b/docs/src/main/paradox/project/migration-guide-1.0.x-1.1.x.md
new file mode 100644
index 0000000000..7e7bd630c9
--- /dev/null
+++ b/docs/src/main/paradox/project/migration-guide-1.0.x-1.1.x.md
@@ -0,0 +1,72 @@
+---
+project.description: Migrating to Apache Pekko 1.1.
+---
+# Migration Guide 1.0.x to 1.1.x
+
+Apache Pekko 1.1.x is binary backwards compatible with 1.0.x with the ordinary 
exceptions listed in the
+@ref:[Binary Compatibility Rules](../common/binary-compatibility-rules.md).
+
+This means that updating an application from Apache Pekko 1.0.x to 1.1.x 
should be a smooth process, and
+that libraries built for Apache Pekko 1.0.x can also be used with Apache Pekko 
1.1.x. For example
+Apache Pekko HTTP 1.0.0 and Apache Pekko Management 1.0.3 can be used with 
Apache Pekko 1.1.x dependencies.
+You may have to add explicit dependencies to the new Apache Pekko version in 
your build.
+
+That said, there are some changes to configuration and behavior that should be 
considered, so
+reading this migration guide and testing your application thoroughly is 
recommended.
+
+## `Source.splitWhen`/`Flow.splitWhen`/`Source.splitAfter`/`Flow.splitAfter` 
behavioural change
+
+In Apache Pekko 1.0.x, the `splitWhen`/`splitAfter` stream operators on 
`Source`/`Flow` had an optional
+`SubstreamCancelStrategy` parameter which defaulted to 
`SubstreamCancelStrategy.drain`. In Apache Pekko
+1.1.x the usage of `SubstreamCancelStrategy` has been deprecated and instead 
`splitWhen`/`splitAfter`
+inherits the already existing `Supervision` strategy `Attribute` to achieve 
the same effect with the
+following translation
+
+* `Supervision.resume` behaves the same way as `SubstreamCancelStrategy.drain`
+* `Supervision.stop` behaves the same way as 
`SubstreamCancelStrategy.propagate`
+* `Supervision.restart` doesn't have an equivalent `SubstreamCancelStrategy`. 
Since it doesn't make semantic
+sense for `SubFlow`s it behaves the same way as `Supervision.stop`.
+
+The potential behavioural change results from the fact that in Apache Pekko 
Streams the default `Supervision`
+strategy is `Supervision.stop` whereas the default `SubstreamCancelStrategy` 
for Apache Pekko 1.0.x is
+`SubstreamCancelStrategy.drain` (which translates to `Supervision.resume` in 
Apache Pekko 1.1.x). This means
+that when you upgrade from Apache Pekko 1.0.x to 1.1.x its possible that 
previously if `SubFlow`s
+resulted in errors the parent stream would continue to operate whereas in 
Apache Pekko 1.1.x the stream would cancel.
+
+If you would like to keep the previous Apache Pekko 1.0.x behaviour you can 
just specify the
+`Supervision.resumingDecider` attribute on your stream in the standard way, 
i.e. you would change
+
+@@@div { .group-java }
+```java
+source.splitAfter(somePredicate);
+```
+@@@
+
+@@@div { .group-scala }
+```scala
+source.splitAfter(somePredicate)
+```
+@@@
+
+to
+
+@@@div { .group-java }
+```java
+source
+    .splitAfter(somePredicate)
+    
.withAttributes(ActorAttributes.supervisionStrategy(Supervision.resumingDecider));
+```
+@@@
+
+@@@div { .group-scala }
+```scala
+source
+  .splitAfter(_ == somePredicate)
+  
.withAttributes(ActorAttributes.supervisionStrategy(Supervision.resumingDecider))
+```
+@@@
+
+If you already happen to have already explicitly defined a 
`SubstreamCancelStrategy` in the
+`splitWhen`/`splitAfter` operators then there won't be any behavioural change 
albeit you
+will get deprecation warnings on compilation so its recommended to migrate 
your code to use
+the equivalent `SupervisionStrategy` as described earlier.
diff --git a/docs/src/main/paradox/project/migration-guides.md 
b/docs/src/main/paradox/project/migration-guides.md
index 7f03436dbd..ab204e82be 100644
--- a/docs/src/main/paradox/project/migration-guides.md
+++ b/docs/src/main/paradox/project/migration-guides.md
@@ -6,7 +6,7 @@ project.description: Apache Pekko version migration guides.
 Apache Pekko is based on the latest version of Akka in the v2.6.x series. If 
migrating from an earlier version of Akka, 
 please [migrate to Akka 
2.6](https://doc.akka.io/docs/akka/current/project/migration-guides.html) 
before migrating to Pekko.
 
-## Migration to Apache Pekko
+## Migration from Akka to Apache Pekko
 
 These migration notes are designed for users migrating from Akka 2.6 to Pekko 
1.0 and assume a basic level of Akka experience. Please feel free to submit an 
issue or a patch if you feel like the notes can be improved.
 
@@ -44,3 +44,11 @@ These migration notes are designed for users migrating from 
Akka 2.6 to Pekko 1.
 It appears that data persisted with "akka-persistence" is usable with 
"pekko-persistence" and vice versa (@ref:[Persistence](../persistence.md)).
 
 We currently do not expect that Akka and Pekko nodes combined will be able to 
form a @ref:[cluster](../cluster-usage.md).
+
+@@toc { depth=1 }
+
+@@@ index
+
+* [migration-guide-1.0.x-1.1.x](migration-guide-1.0.x-1.1.x.md)
+
+@@@
diff --git 
a/docs/src/main/paradox/stream/operators/Source-or-Flow/splitAfter.md 
b/docs/src/main/paradox/stream/operators/Source-or-Flow/splitAfter.md
index 7ced7b40a4..7534422125 100644
--- a/docs/src/main/paradox/stream/operators/Source-or-Flow/splitAfter.md
+++ b/docs/src/main/paradox/stream/operators/Source-or-Flow/splitAfter.md
@@ -6,9 +6,11 @@ End the current substream whenever a predicate returns `true`, 
starting a new su
 
 ## Signature
 
-@apidoc[Source.splitAfter](Source) { 
scala="#splitAfter(substreamCancelStrategy:org.apache.pekko.stream.SubstreamCancelStrategy)(p:Out=&gt;Boolean):org.apache.pekko.stream.scaladsl.SubFlow[Out,Mat,FlowOps.this.Repr,FlowOps.this.Closed]"
 
java="#splitAfter(org.apache.pekko.stream.SubstreamCancelStrategy,org.apache.pekko.japi.function.Predicate)"
 }
-@apidoc[Flow.splitAfter](Flow) { 
scala="#splitAfter(substreamCancelStrategy:org.apache.pekko.stream.SubstreamCancelStrategy)(p:Out=&gt;Boolean):org.apache.pekko.stream.scaladsl.SubFlow[Out,Mat,FlowOps.this.Repr,FlowOps.this.Closed]"
 
java="#splitAfter(org.apache.pekko.stream.SubstreamCancelStrategy,org.apache.pekko.japi.function.Predicate)"
 }
+@apidoc[Source.splitAfter](Source) { 
scala="#splitAfter(p:Out=&gt;Boolean):org.apache.pekko.stream.scaladsl.SubFlow[Out,Mat,FlowOps.this.Repr,FlowOps.this.Closed]"
 java="#splitAfter(org.apache.pekko.japi.function.Predicate)" }
+@apidoc[Flow.splitAfter](Flow) { 
scala="#splitAfter(p:Out=&gt;Boolean):org.apache.pekko.stream.scaladsl.SubFlow[Out,Mat,FlowOps.this.Repr,FlowOps.this.Closed]"
 java="#splitAfter(org.apache.pekko.japi.function.Predicate)" }
 
+The `splitAfter` operator adheres to the ActorAttributes.SupervisionStrategy 
attribute with the caveat that
+`Supervision.restart` behaves the same way as `Supervision.resume` since 
`Supervision.restart` for `SubFlow`s semantically doesn't make sense.
 
 ## Description
 
diff --git a/docs/src/main/paradox/stream/operators/Source-or-Flow/splitWhen.md 
b/docs/src/main/paradox/stream/operators/Source-or-Flow/splitWhen.md
index 891b34dc6f..3b5dd5b9e7 100644
--- a/docs/src/main/paradox/stream/operators/Source-or-Flow/splitWhen.md
+++ b/docs/src/main/paradox/stream/operators/Source-or-Flow/splitWhen.md
@@ -6,9 +6,11 @@ Split off elements into a new substream whenever a predicate 
function return `tr
 
 ## Signature
 
-@apidoc[Source.splitWhen](Source) { 
scala="#splitWhen(substreamCancelStrategy:org.apache.pekko.stream.SubstreamCancelStrategy)(p:Out=&gt;Boolean):org.apache.pekko.stream.scaladsl.SubFlow[Out,Mat,FlowOps.this.Repr,FlowOps.this.Closed]"
 
java="#splitWhen(org.apache.pekko.stream.SubstreamCancelStrategy,org.apache.pekko.japi.function.Predicate)"
 }
-@apidoc[Flow.splitWhen](Flow) { 
scala="#splitWhen(substreamCancelStrategy:org.apache.pekko.stream.SubstreamCancelStrategy)(p:Out=&gt;Boolean):org.apache.pekko.stream.scaladsl.SubFlow[Out,Mat,FlowOps.this.Repr,FlowOps.this.Closed]"
 
java="#splitWhen(org.apache.pekko.stream.SubstreamCancelStrategy,org.apache.pekko.japi.function.Predicate)"
 }
+@apidoc[Source.splitWhen](Source) { 
scala="#splitWhen(p:Out=&gt;Boolean):org.apache.pekko.stream.scaladsl.SubFlow[Out,Mat,FlowOps.this.Repr,FlowOps.this.Closed]"
 java="#splitWhen(org.apache.pekko.japi.function.Predicate)" }
+@apidoc[Flow.splitWhen](Flow) { 
scala="#splitWhen(p:Out=&gt;Boolean):org.apache.pekko.stream.scaladsl.SubFlow[Out,Mat,FlowOps.this.Repr,FlowOps.this.Closed]"
 java="#splitWhen(org.apache.pekko.japi.function.Predicate)" }
 
+The `splitWhen` operator adheres to the ActorAttributes.SupervisionStrategy 
attribute with the caveat that
+`Supervision.restart` behaves the same way as `Supervision.resume` since 
`Supervision.restart` for `SubFlow`s semantically doesn't make sense.
 
 ## Description
 
diff --git 
a/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/FlowSplitAfterSpec.scala
 
b/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/FlowSplitAfterSpec.scala
index 1dc340e377..16b5e30675 100644
--- 
a/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/FlowSplitAfterSpec.scala
+++ 
b/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/FlowSplitAfterSpec.scala
@@ -22,7 +22,7 @@ import org.apache.pekko
 import pekko.NotUsed
 import pekko.stream._
 import pekko.stream.StreamSubscriptionTimeoutTerminationMode
-import pekko.stream.Supervision.resumingDecider
+import pekko.stream.Supervision.{ resumingDecider, Decider }
 import pekko.stream.impl.SubscriptionTimeoutException
 import pekko.stream.testkit.StreamSpec
 import pekko.stream.testkit.TestPublisher
@@ -58,13 +58,16 @@ class FlowSplitAfterSpec extends StreamSpec("""
     def cancel(): Unit = subscription.cancel()
   }
 
-  class SubstreamsSupport(
-      splitAfter: Int = 3,
+  class SubstreamsSupport(splitAfter: Int = 3,
       elementCount: Int = 6,
-      substreamCancelStrategy: SubstreamCancelStrategy = 
SubstreamCancelStrategy.drain) {
+      decider: Decider = Supervision.resumingDecider) {
 
     val source = Source(1 to elementCount)
-    val groupStream = source.splitAfter(substreamCancelStrategy)(_ == 
splitAfter).lift.runWith(Sink.asPublisher(false))
+    val groupStream = source
+      .splitAfter(_ == splitAfter)
+      .lift
+      .withAttributes(ActorAttributes.supervisionStrategy(decider))
+      .runWith(Sink.asPublisher(false))
     val masterSubscriber = TestSubscriber.manualProbe[Source[Int, _]]()
 
     groupStream.subscribe(masterSubscriber)
@@ -272,7 +275,7 @@ class FlowSplitAfterSpec extends StreamSpec("""
     }
 
     "support eager cancellation of master stream on cancelling substreams" in {
-      new SubstreamsSupport(splitAfter = 5, elementCount = 8, 
SubstreamCancelStrategy.propagate) {
+      new SubstreamsSupport(splitAfter = 5, elementCount = 8, 
Supervision.stoppingDecider) {
         val s1 = StreamPuppet(expectSubFlow().runWith(Sink.asPublisher(false)))
         s1.cancel()
         masterSubscriber.expectComplete()
diff --git 
a/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/FlowSplitWhenSpec.scala
 
b/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/FlowSplitWhenSpec.scala
index d2a748725e..5eaaa1a9d3 100644
--- 
a/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/FlowSplitWhenSpec.scala
+++ 
b/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/FlowSplitWhenSpec.scala
@@ -23,7 +23,7 @@ import org.apache.pekko
 import pekko.Done
 import pekko.NotUsed
 import pekko.stream._
-import pekko.stream.Supervision.resumingDecider
+import pekko.stream.Supervision.Decider
 import pekko.stream.impl.SubscriptionTimeoutException
 import pekko.stream.impl.fusing.Split
 import pekko.stream.testkit._
@@ -51,13 +51,16 @@ class FlowSplitWhenSpec extends StreamSpec("""
     def cancel(): Unit = subscription.cancel()
   }
 
-  class SubstreamsSupport(
-      splitWhen: Int = 3,
+  class SubstreamsSupport(splitWhen: Int = 3,
       elementCount: Int = 6,
-      substreamCancelStrategy: SubstreamCancelStrategy = 
SubstreamCancelStrategy.drain) {
+      decider: Decider = Supervision.resumingDecider) {
 
     val source = Source(1 to elementCount)
-    val groupStream = source.splitWhen(substreamCancelStrategy)(_ == 
splitWhen).lift.runWith(Sink.asPublisher(false))
+    val groupStream = source
+      .splitWhen(_ == splitWhen)
+      .lift
+      .withAttributes(ActorAttributes.supervisionStrategy(decider))
+      .runWith(Sink.asPublisher(false))
     val masterSubscriber = TestSubscriber.manualProbe[Source[Int, _]]()
 
     groupStream.subscribe(masterSubscriber)
@@ -162,6 +165,7 @@ class FlowSplitWhenSpec extends StreamSpec("""
         .fromPublisher(inputs)
         .splitWhen(_ == 2)
         .lift
+        
.withAttributes(ActorAttributes.supervisionStrategy(Supervision.resumingDecider))
         .map(_.runWith(Sink.fromSubscriber(substream)))
         .runWith(Sink.fromSubscriber(masterStream))
 
@@ -176,7 +180,13 @@ class FlowSplitWhenSpec extends StreamSpec("""
       inputs.expectCancellation()
 
       val inputs2 = TestPublisher.probe[Int]()
-      Source.fromPublisher(inputs2).splitWhen(_ == 
2).lift.map(_.runWith(Sink.cancelled)).runWith(Sink.cancelled)
+      Source
+        .fromPublisher(inputs2)
+        .splitWhen(_ == 2)
+        .lift
+        
.withAttributes(ActorAttributes.supervisionStrategy(Supervision.resumingDecider))
+        .map(_.runWith(Sink.cancelled))
+        .runWith(Sink.cancelled)
 
       inputs2.expectCancellation()
 
@@ -184,7 +194,12 @@ class FlowSplitWhenSpec extends StreamSpec("""
 
       val masterStream3 = TestSubscriber.probe[Source[Int, Any]]()
 
-      Source.fromPublisher(inputs3).splitWhen(_ == 
2).lift.runWith(Sink.fromSubscriber(masterStream3))
+      Source
+        .fromPublisher(inputs3)
+        .splitWhen(_ == 2)
+        .lift
+        
.withAttributes(ActorAttributes.supervisionStrategy(Supervision.resumingDecider))
+        .runWith(Sink.fromSubscriber(masterStream3))
 
       masterStream3.request(1)
       inputs3.sendNext(1)
@@ -267,6 +282,7 @@ class FlowSplitWhenSpec extends StreamSpec("""
         Source(1 to 100)
           .splitWhen(_ => true)
           .lift
+          
.withAttributes(ActorAttributes.supervisionStrategy(Supervision.resumingDecider))
           .mapAsync(1)(_.runWith(Sink.head)) // Please note that this line 
*also* implicitly asserts nonempty substreams
           .grouped(200)
           .runWith(Sink.head),
@@ -281,7 +297,7 @@ class FlowSplitWhenSpec extends StreamSpec("""
           // `lift` doesn't cut here because it will prevent the behavior we'd 
like to see.
           // In fact, this test is somewhat useless, as a user cannot trigger 
double materialization using
           // the public splitWhen => SubFlow API.
-          .via(Split.when(_ => true, SubstreamCancelStrategy.drain))
+          .via(Split.when(_ => true))
           .map { source =>
             // run twice, but make sure we return the result of the 
materialization that ran second
             source.runWith(Sink.ignore).flatMap(_ => 
source.runWith(Sink.ignore))
@@ -320,7 +336,7 @@ class FlowSplitWhenSpec extends StreamSpec("""
         .fromPublisher(publisherProbeProbe)
         .splitWhen(elem => if (elem == 3) throw exc else elem % 3 == 0)
         .lift
-        .withAttributes(ActorAttributes.supervisionStrategy(resumingDecider))
+        
.withAttributes(ActorAttributes.supervisionStrategy(Supervision.resumingDecider))
         .runWith(Sink.asPublisher(false))
       val subscriber = TestSubscriber.manualProbe[Source[Int, NotUsed]]()
       publisher.subscribe(subscriber)
@@ -374,7 +390,7 @@ class FlowSplitWhenSpec extends StreamSpec("""
     }
 
     "support eager cancellation of master stream on cancelling substreams" in {
-      new SubstreamsSupport(splitWhen = 5, elementCount = 8, 
SubstreamCancelStrategy.propagate) {
+      new SubstreamsSupport(splitWhen = 5, elementCount = 8, 
Supervision.stoppingDecider) {
         val s1 = StreamPuppet(getSubFlow().runWith(Sink.asPublisher(false)))
         s1.cancel()
         masterSubscriber.expectComplete()
diff --git 
a/stream/src/main/scala/org/apache/pekko/stream/SubstreamCancelStrategy.scala 
b/stream/src/main/scala/org/apache/pekko/stream/SubstreamCancelStrategy.scala
index e260a0d280..563ee91f61 100644
--- 
a/stream/src/main/scala/org/apache/pekko/stream/SubstreamCancelStrategy.scala
+++ 
b/stream/src/main/scala/org/apache/pekko/stream/SubstreamCancelStrategy.scala
@@ -15,9 +15,14 @@ package org.apache.pekko.stream
 
 import SubstreamCancelStrategies._
 
+import scala.annotation.nowarn
+
 /**
  * Represents a strategy that decides how to deal with substream events.
  */
+@deprecated(
+  "Use .withAttributes(ActorAttributes.supervisionStrategy(equivalentDecider)) 
rather than a SubstreamCancelStrategy",
+  since = "1.1.0")
 sealed abstract class SubstreamCancelStrategy
 
 private[pekko] object SubstreamCancelStrategies {
@@ -25,14 +30,19 @@ private[pekko] object SubstreamCancelStrategies {
   /**
    * INTERNAL API
    */
+  @nowarn("msg=deprecated")
   private[pekko] case object Propagate extends SubstreamCancelStrategy
 
   /**
    * INTERNAL API
    */
+  @nowarn("msg=deprecated")
   private[pekko] case object Drain extends SubstreamCancelStrategy
 }
 
+@deprecated(
+  "Use .withAttributes(ActorAttributes.supervisionStrategy(equivalentDecider)) 
rather than a SubstreamCancelStrategy",
+  since = "1.1.0")
 object SubstreamCancelStrategy {
 
   /**
diff --git 
a/stream/src/main/scala/org/apache/pekko/stream/impl/fusing/StreamOfStreams.scala
 
b/stream/src/main/scala/org/apache/pekko/stream/impl/fusing/StreamOfStreams.scala
index 8e5bdc2b77..94abf377ef 100644
--- 
a/stream/src/main/scala/org/apache/pekko/stream/impl/fusing/StreamOfStreams.scala
+++ 
b/stream/src/main/scala/org/apache/pekko/stream/impl/fusing/StreamOfStreams.scala
@@ -15,7 +15,7 @@ package org.apache.pekko.stream.impl.fusing
 
 import java.util.Collections
 import java.util.concurrent.atomic.AtomicReference
-import scala.annotation.tailrec
+import scala.annotation.{ nowarn, tailrec }
 import scala.collection.immutable
 import scala.concurrent.duration.FiniteDuration
 import scala.util.control.NonFatal
@@ -27,6 +27,7 @@ import pekko.stream.ActorAttributes.StreamSubscriptionTimeout
 import pekko.stream.ActorAttributes.SupervisionStrategy
 import pekko.stream.Attributes.SourceLocation
 import pekko.stream.impl.{ Buffer => BufferImpl }
+import pekko.stream.Supervision.Decider
 import pekko.stream.impl.ActorSubscriberMessage
 import pekko.stream.impl.ActorSubscriberMessage.OnError
 import pekko.stream.impl.Stages.DefaultAttributes
@@ -467,24 +468,25 @@ import pekko.util.ccompat.JavaConverters._
   /** Splits after the current element. The current element will be the last 
element in the current substream. */
   case object SplitAfter extends SplitDecision
 
-  def when[T](
-      p: T => Boolean,
-      substreamCancelStrategy: SubstreamCancelStrategy): Graph[FlowShape[T, 
Source[T, NotUsed]], NotUsed] =
-    new Split(Split.SplitBefore, p, substreamCancelStrategy)
+  @nowarn("msg=deprecated")
+  def cancelStrategyToDecider(substreamCancelStrategy: 
SubstreamCancelStrategy): Decider =
+    substreamCancelStrategy match {
+      case SubstreamCancelStrategies.Propagate => Supervision.stoppingDecider
+      case SubstreamCancelStrategies.Drain     => Supervision.resumingDecider
+    }
+
+  def when[T](p: T => Boolean): Graph[FlowShape[T, Source[T, NotUsed]], 
NotUsed] = {
+    new Split(Split.SplitBefore, p)
+  }
 
-  def after[T](
-      p: T => Boolean,
-      substreamCancelStrategy: SubstreamCancelStrategy): Graph[FlowShape[T, 
Source[T, NotUsed]], NotUsed] =
-    new Split(Split.SplitAfter, p, substreamCancelStrategy)
+  def after[T](p: T => Boolean): Graph[FlowShape[T, Source[T, NotUsed]], 
NotUsed] =
+    new Split(Split.SplitAfter, p)
 }
 
 /**
  * INTERNAL API
  */
-@InternalApi private[pekko] final class Split[T](
-    val decision: Split.SplitDecision,
-    val p: T => Boolean,
-    val substreamCancelStrategy: SubstreamCancelStrategy)
+@InternalApi private[pekko] final class Split[T](val decision: 
Split.SplitDecision, val p: T => Boolean)
     extends GraphStage[FlowShape[T, Source[T, NotUsed]]] {
 
   val in: Inlet[T] = Inlet("Split.in")
@@ -492,24 +494,28 @@ import pekko.util.ccompat.JavaConverters._
 
   override val shape: FlowShape[T, Source[T, NotUsed]] = FlowShape(in, out)
 
-  private val propagateSubstreamCancel = substreamCancelStrategy match {
-    case SubstreamCancelStrategies.Propagate => true
-    case SubstreamCancelStrategies.Drain     => false
-  }
-
-  override protected def initialAttributes: Attributes = 
DefaultAttributes.split and SourceLocation.forLambda(p)
+  override def initialAttributes: Attributes = DefaultAttributes.split and 
SourceLocation.forLambda(p)
 
   override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = 
new TimerGraphStageLogic(shape) {
     import Split._
 
     private val SubscriptionTimer = "SubstreamSubscriptionTimer"
 
+    private lazy val decider = 
inheritedAttributes.mandatoryAttribute[SupervisionStrategy].decider
+
     private val timeout: FiniteDuration =
       
inheritedAttributes.mandatoryAttribute[ActorAttributes.StreamSubscriptionTimeout].timeout
     private var substreamSource: SubSourceOutlet[T] = null
     private var substreamWaitingToBePushed = false
     private var substreamCancelled = false
 
+    def propagateSubstreamCancel(ex: Throwable): Boolean =
+      decider(ex) match {
+        case Supervision.Stop    => true
+        case Supervision.Resume  => false
+        case Supervision.Restart => false
+      }
+
     setHandler(
       out,
       new OutHandler {
@@ -606,7 +612,7 @@ import pekko.util.ccompat.JavaConverters._
 
       override def onDownstreamFinish(cause: Throwable): Unit = {
         substreamCancelled = true
-        if (isClosed(in) || propagateSubstreamCancel) {
+        if (isClosed(in) || propagateSubstreamCancel(cause)) {
           cancelStage(cause)
         } else {
           // Start draining
diff --git a/stream/src/main/scala/org/apache/pekko/stream/javadsl/Flow.scala 
b/stream/src/main/scala/org/apache/pekko/stream/javadsl/Flow.scala
index 3e2330abf3..8aaf3a45ee 100755
--- a/stream/src/main/scala/org/apache/pekko/stream/javadsl/Flow.scala
+++ b/stream/src/main/scala/org/apache/pekko/stream/javadsl/Flow.scala
@@ -2414,6 +2414,9 @@ final class Flow[In, Out, Mat](delegate: 
scaladsl.Flow[In, Out, Mat]) extends Gr
    *
    * @see [[#splitWhen]]
    */
+  @deprecated(
+    "Use 
.withAttributes(ActorAttributes.supervisionStrategy(equivalentDecider)) rather 
than a SubstreamCancelStrategy",
+    since = "1.1.0")
   def splitWhen(substreamCancelStrategy: SubstreamCancelStrategy)(p: 
function.Predicate[Out]): SubFlow[In, Out, Mat] =
     new SubFlow(delegate.splitWhen(substreamCancelStrategy)(p.test))
 
@@ -2472,6 +2475,9 @@ final class Flow[In, Out, Mat](delegate: 
scaladsl.Flow[In, Out, Mat]) extends Gr
    *
    * @see [[#splitAfter]]
    */
+  @deprecated(
+    "Use 
.withAttributes(ActorAttributes.supervisionStrategy(equivalentDecider)) rather 
than a SubstreamCancelStrategy",
+    since = "1.1.0")
   def splitAfter(substreamCancelStrategy: SubstreamCancelStrategy)(p: 
function.Predicate[Out]): SubFlow[In, Out, Mat] =
     new SubFlow(delegate.splitAfter(substreamCancelStrategy)(p.test))
 
diff --git a/stream/src/main/scala/org/apache/pekko/stream/javadsl/Source.scala 
b/stream/src/main/scala/org/apache/pekko/stream/javadsl/Source.scala
index edaff38f1b..2e6db54589 100755
--- a/stream/src/main/scala/org/apache/pekko/stream/javadsl/Source.scala
+++ b/stream/src/main/scala/org/apache/pekko/stream/javadsl/Source.scala
@@ -3905,6 +3905,9 @@ final class Source[Out, Mat](delegate: 
scaladsl.Source[Out, Mat]) extends Graph[
    *
    * @see [[#splitWhen]]
    */
+  @deprecated(
+    "Use 
.withAttributes(ActorAttributes.supervisionStrategy(equivalentDecider)) rather 
than a SubstreamCancelStrategy",
+    since = "1.1.0")
   def splitWhen(substreamCancelStrategy: SubstreamCancelStrategy)(p: 
function.Predicate[Out]): SubSource[Out, Mat] =
     new SubSource(delegate.splitWhen(substreamCancelStrategy)(p.test))
 
@@ -3962,6 +3965,9 @@ final class Source[Out, Mat](delegate: 
scaladsl.Source[Out, Mat]) extends Graph[
    *
    * @see [[#splitAfter]]
    */
+  @deprecated(
+    "Use 
.withAttributes(ActorAttributes.supervisionStrategy(equivalentDecider)) rather 
than a SubstreamCancelStrategy",
+    since = "1.1.0")
   def splitAfter(substreamCancelStrategy: SubstreamCancelStrategy)(p: 
function.Predicate[Out]): SubSource[Out, Mat] =
     new SubSource(delegate.splitAfter(substreamCancelStrategy)(p.test))
 
diff --git a/stream/src/main/scala/org/apache/pekko/stream/scaladsl/Flow.scala 
b/stream/src/main/scala/org/apache/pekko/stream/scaladsl/Flow.scala
index a73822fec1..6e0f205c68 100755
--- a/stream/src/main/scala/org/apache/pekko/stream/scaladsl/Flow.scala
+++ b/stream/src/main/scala/org/apache/pekko/stream/scaladsl/Flow.scala
@@ -2338,15 +2338,25 @@ trait FlowOps[+Out, +Mat] {
    *
    * See also [[FlowOps.splitAfter]].
    */
+  @deprecated(
+    "Use 
.withAttributes(ActorAttributes.supervisionStrategy(equivalentDecider)) rather 
than a SubstreamCancelStrategy",
+    since = "1.1.0")
   def splitWhen(substreamCancelStrategy: SubstreamCancelStrategy)(
       p: Out => Boolean): SubFlow[Out, Mat, Repr, Closed] = {
     val merge = new SubFlowImpl.MergeBack[Out, Repr] {
       override def apply[T](flow: Flow[Out, T, NotUsed], breadth: Int): 
Repr[T] =
-        via(Split.when(p, substreamCancelStrategy)).map(_.via(flow)).via(new 
FlattenMerge(breadth))
+        via(Split
+          .when(p)
+          
.withAttributes(ActorAttributes.supervisionStrategy(Split.cancelStrategyToDecider(substreamCancelStrategy))))
+          .map(_.via(flow))
+          .via(new FlattenMerge(breadth))
     }
 
-    val finish: (Sink[Out, NotUsed]) => Closed = s =>
-      via(Split.when(p, substreamCancelStrategy))
+    val finish: Sink[Out, NotUsed] => Closed = s =>
+      via(
+        Split
+          .when(p)
+          
.withAttributes(ActorAttributes.supervisionStrategy(Split.cancelStrategyToDecider(substreamCancelStrategy))))
         
.to(Sink.foreach(_.runWith(s)(GraphInterpreter.currentInterpreter.materializer)))
 
     new SubFlowImpl(Flow[Out], merge, finish)
@@ -2359,8 +2369,17 @@ trait FlowOps[+Out, +Mat] {
    *
    * @see [[#splitWhen]]
    */
-  def splitWhen(p: Out => Boolean): SubFlow[Out, Mat, Repr, Closed] =
-    splitWhen(SubstreamCancelStrategy.drain)(p)
+  def splitWhen(p: Out => Boolean): SubFlow[Out, Mat, Repr, Closed] = {
+    val merge = new SubFlowImpl.MergeBack[Out, Repr] {
+      override def apply[T](flow: Flow[Out, T, NotUsed], breadth: Int): 
Repr[T] =
+        via(Split.when(p)).map(_.via(flow)).via(new FlattenMerge(breadth))
+    }
+
+    val finish: Sink[Out, NotUsed] => Closed = s =>
+      
via(Split.when(p)).to(Sink.foreach(_.runWith(s)(GraphInterpreter.currentInterpreter.materializer)))
+
+    new SubFlowImpl(Flow[Out], merge, finish)
+  }
 
   /**
    * This operation applies the given predicate to all incoming elements and
@@ -2407,14 +2426,24 @@ trait FlowOps[+Out, +Mat] {
    *
    * See also [[FlowOps.splitWhen]].
    */
+  @deprecated(
+    "Use 
.withAttributes(ActorAttributes.supervisionStrategy(equivalentDecider)) rather 
than a SubstreamCancelStrategy",
+    since = "1.1.0")
   def splitAfter(substreamCancelStrategy: SubstreamCancelStrategy)(
       p: Out => Boolean): SubFlow[Out, Mat, Repr, Closed] = {
     val merge = new SubFlowImpl.MergeBack[Out, Repr] {
       override def apply[T](flow: Flow[Out, T, NotUsed], breadth: Int): 
Repr[T] =
-        via(Split.after(p, substreamCancelStrategy)).map(_.via(flow)).via(new 
FlattenMerge(breadth))
+        via(Split
+          .after(p)
+          
.withAttributes(ActorAttributes.supervisionStrategy(Split.cancelStrategyToDecider(substreamCancelStrategy))))
+          .map(_.via(flow))
+          .via(new FlattenMerge(breadth))
     }
-    val finish: (Sink[Out, NotUsed]) => Closed = s =>
-      via(Split.after(p, substreamCancelStrategy))
+    val finish: Sink[Out, NotUsed] => Closed = s =>
+      via(
+        Split
+          .after(p)
+          
.withAttributes(ActorAttributes.supervisionStrategy(Split.cancelStrategyToDecider(substreamCancelStrategy))))
         
.to(Sink.foreach(_.runWith(s)(GraphInterpreter.currentInterpreter.materializer)))
     new SubFlowImpl(Flow[Out], merge, finish)
   }
@@ -2426,8 +2455,15 @@ trait FlowOps[+Out, +Mat] {
    *
    * @see [[#splitAfter]]
    */
-  def splitAfter(p: Out => Boolean): SubFlow[Out, Mat, Repr, Closed] =
-    splitAfter(SubstreamCancelStrategy.drain)(p)
+  def splitAfter(p: Out => Boolean): SubFlow[Out, Mat, Repr, Closed] = {
+    val merge = new SubFlowImpl.MergeBack[Out, Repr] {
+      override def apply[T](flow: Flow[Out, T, NotUsed], breadth: Int): 
Repr[T] =
+        via(Split.after(p)).map(_.via(flow)).via(new FlattenMerge(breadth))
+    }
+    val finish: Sink[Out, NotUsed] => Closed = s =>
+      
via(Split.after(p)).to(Sink.foreach(_.runWith(s)(GraphInterpreter.currentInterpreter.materializer)))
+    new SubFlowImpl(Flow[Out], merge, finish)
+  }
 
   /**
    * Transform each input element into a `Source` of output elements that is


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to