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

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


The following commit(s) were added to refs/heads/main by this push:
     new 0b9755a079 fix: add supervision strategy support for zipWith and 
zipWithN zipper functions (#3186)
0b9755a079 is described below

commit 0b9755a079fe3c66e2bf1f8516ba613cb2410535
Author: He-Pin(kerr) <[email protected]>
AuthorDate: Sun Jun 28 05:57:28 2026 +0800

    fix: add supervision strategy support for zipWith and zipWithN zipper 
functions (#3186)
    
    * fix: add supervision strategy support for zipWith and zipWithN zipper 
functions
    
    Motivation:
    zipWith and zipWithN accepted user zipper/combiner functions but did not 
consult
    SupervisionStrategy. Zipper exceptions failed the stream unconditionally, 
even
    when Resume/Restart was configured.
    
    Modification:
    Add zipper exception supervision handling in both implementations:
    - ZipWith boilerplate template (all arities)
    - Graph.ZipWithN
    
    Stop fails the stage; Resume/Restart drop the failing zipped element and
    continue by pulling new inputs. Pending bookkeeping is updated on resumed/
    restarted failures so the stage can satisfy the current downstream demand
    without stalling.
    
    Update Scala/Java API docs and operator docs (`zipWith.md`, `zipWithN.md`) 
to
    document supervision behavior for combiner function failures.
    
    Add directional tests in:
    - FlowZipWithSpec
    - GraphZipWithNSpec
    covering explicit Stop, default Stop, Resume, and Restart.
    
    Result:
    zipWith/zipWithN now honor ActorAttributes.SupervisionStrategy for zipper
    failures with deterministic regression coverage for dropped-element paths.
    
    Tests:
    - sbt "stream-tests/Test/testOnly 
org.apache.pekko.stream.scaladsl.FlowZipWithSpec 
org.apache.pekko.stream.scaladsl.GraphZipWithNSpec" -- 37/37 passed
    - sbt "stream/mimaReportBinaryIssues" -- clean
    - sbt "docs/paradox" -- passed
    
    References:
    Refs #3110
    
    * fix: remove stale EventFilter from GraphZipWithSpec
    
    Motivation:
    The supervision support added in the previous commit causes ZipWith
    zipper exceptions to be handled by the decider instead of being logged
    as ERROR. GraphZipWithSpec still wrapped its sad-case test in
    EventFilter[ArithmeticException], which now times out waiting for an
    ERROR log message that is never emitted.
    
    Modification:
    Remove the EventFilter wrapping and unused import from
    GraphZipWithSpec's "work in the sad case" test, matching the same
    cleanup already applied to FlowZipWithSpec and GraphZipWithNSpec.
    
    Result:
    GraphZipWithSpec sad-case test passes without EventFilter timeout.
    
    Tests:
    - sbt "stream-tests/Test/testOnly 
org.apache.pekko.stream.scaladsl.GraphZipWithSpec" -- 15/15 passed
    
    References:
    Refs #3110
---
 .../stream/operators/Source-or-Flow/zipWith.md     |  4 ++
 .../paradox/stream/operators/Source/zipWithN.md    |  5 ++-
 .../pekko/stream/scaladsl/FlowZipWithSpec.scala    | 41 ++++++++++++++++++--
 .../pekko/stream/scaladsl/GraphZipWithNSpec.scala  | 44 +++++++++++++++++++---
 .../pekko/stream/scaladsl/GraphZipWithSpec.scala   |  5 +--
 .../stream/scaladsl/ZipWithApply.scala.template    | 21 ++++++++++-
 .../org/apache/pekko/stream/javadsl/Flow.scala     |  6 +++
 .../org/apache/pekko/stream/javadsl/Source.scala   | 12 ++++++
 .../org/apache/pekko/stream/javadsl/SubFlow.scala  |  6 +++
 .../apache/pekko/stream/javadsl/SubSource.scala    |  6 +++
 .../org/apache/pekko/stream/scaladsl/Flow.scala    |  6 +++
 .../org/apache/pekko/stream/scaladsl/Graph.scala   | 29 +++++++++++++-
 .../org/apache/pekko/stream/scaladsl/Source.scala  |  6 +++
 13 files changed, 175 insertions(+), 16 deletions(-)

diff --git a/docs/src/main/paradox/stream/operators/Source-or-Flow/zipWith.md 
b/docs/src/main/paradox/stream/operators/Source-or-Flow/zipWith.md
index 0512ff1b22..1a54c919c5 100644
--- a/docs/src/main/paradox/stream/operators/Source-or-Flow/zipWith.md
+++ b/docs/src/main/paradox/stream/operators/Source-or-Flow/zipWith.md
@@ -15,6 +15,10 @@ Combines elements from multiple sources through a `combine` 
function and passes
 Combines elements from multiple sources through a `combine` function and 
passes the
 returned value downstream.
 
+This operator adheres to the `ActorAttributes.SupervisionStrategy` attribute 
for exceptions thrown by the
+`combine` function. On `Supervision.Stop` the stream fails; on 
`Supervision.Resume` and `Supervision.Restart`
+the failing zipped element is dropped and the stream continues.
+
 See also:
 
  * @ref:[zip](zip.md)
diff --git a/docs/src/main/paradox/stream/operators/Source/zipWithN.md 
b/docs/src/main/paradox/stream/operators/Source/zipWithN.md
index 63c688fdc0..16527f3c4a 100644
--- a/docs/src/main/paradox/stream/operators/Source/zipWithN.md
+++ b/docs/src/main/paradox/stream/operators/Source/zipWithN.md
@@ -15,6 +15,10 @@ Combine the elements of multiple streams into a stream of 
sequences using a comb
 This operator is essentially the same as using @ref:[zipN](zipN.md) followed 
by @ref[map](../Source-or-Flow/map.md)
 to turn the zipped sequence into an arbitrary object to emit downstream.
 
+This operator adheres to the `ActorAttributes.SupervisionStrategy` attribute 
for exceptions thrown by the `zipper`
+function. On `Supervision.Stop` the stream fails; on `Supervision.Resume` and 
`Supervision.Restart` the failing zipped
+element is dropped and the stream continues.
+
 See also:
 
  * @ref:[zipN](zipN.md)
@@ -48,4 +52,3 @@ Note how it stops as soon as any of the original sources 
reaches its end.
 
 @@@
 
-
diff --git 
a/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/FlowZipWithSpec.scala
 
b/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/FlowZipWithSpec.scala
index cb4827d126..526d619c9d 100644
--- 
a/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/FlowZipWithSpec.scala
+++ 
b/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/FlowZipWithSpec.scala
@@ -14,11 +14,13 @@
 package org.apache.pekko.stream.scaladsl
 
 import org.apache.pekko
+import pekko.stream.ActorAttributes
+import pekko.stream.Supervision
 import pekko.stream.testkit.{ BaseTwoStreamsSetup, TestSubscriber }
 import org.reactivestreams.Publisher
 
+import scala.concurrent.Await
 import scala.concurrent.duration._
-import pekko.testkit.EventFilter
 import scala.annotation.nowarn
 
 @nowarn // keep unused imports
@@ -68,15 +70,46 @@ class FlowZipWithSpec extends BaseTwoStreamsSetup {
       probe.expectNext(1 / -2)
       probe.expectNext(2 / -1)
 
-      EventFilter[ArithmeticException](occurrences = 1).intercept {
-        subscription.request(2)
-      }
+      subscription.request(2)
       probe.expectError() match {
         case a: java.lang.ArithmeticException => a.getMessage should be("/ by 
zero")
       }
       probe.expectNoMessage(200.millis)
     }
 
+    "fail stream when zipper throws and supervision is Stop" in {
+      val ex = new RuntimeException("boom")
+      val result = Source(1 to 4)
+        .zipWith(Source(1 to 4))((a, b) => if (a == 3) throw ex else a + b)
+        
.withAttributes(ActorAttributes.supervisionStrategy(Supervision.stoppingDecider))
+        .runWith(Sink.seq)
+      result.failed.futureValue shouldBe ex
+    }
+
+    "fail stream when zipper throws and supervision defaults to Stop" in {
+      val ex = new RuntimeException("boom")
+      val result = Source(1 to 4)
+        .zipWith(Source(1 to 4))((a, b) => if (a == 3) throw ex else a + b)
+        .runWith(Sink.seq)
+      result.failed.futureValue shouldBe ex
+    }
+
+    "resume when zipper throws and drop failed zipped element" in {
+      val future = Source(1 to 4)
+        .zipWith(Source(1 to 4))((a, b) => if (a == 3) throw new 
RuntimeException("boom") else a + b)
+        
.withAttributes(ActorAttributes.supervisionStrategy(Supervision.resumingDecider))
+        .runWith(Sink.seq)
+      Await.result(future, 3.seconds) shouldBe Seq(2, 4, 8)
+    }
+
+    "restart when zipper throws and drop failed zipped element" in {
+      val future = Source(1 to 4)
+        .zipWith(Source(1 to 4))((a, b) => if (a == 3) throw new 
RuntimeException("boom") else a + b)
+        
.withAttributes(ActorAttributes.supervisionStrategy(Supervision.restartingDecider))
+        .runWith(Sink.seq)
+      Await.result(future, 3.seconds) shouldBe Seq(2, 4, 8)
+    }
+
     commonTests()
 
     "work with one immediately completed and one nonempty publisher" in {
diff --git 
a/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/GraphZipWithNSpec.scala
 
b/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/GraphZipWithNSpec.scala
index deacff1f2b..260456aba1 100644
--- 
a/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/GraphZipWithNSpec.scala
+++ 
b/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/GraphZipWithNSpec.scala
@@ -14,12 +14,13 @@
 package org.apache.pekko.stream.scaladsl
 
 import scala.collection.immutable
+import scala.concurrent.Await
 import scala.concurrent.duration._
 
 import org.apache.pekko
 import pekko.stream._
-import pekko.stream.testkit._
-import pekko.testkit.EventFilter
+import pekko.stream.testkit.TestSubscriber
+import pekko.stream.testkit.TwoStreamsSetup
 
 class GraphZipWithNSpec extends TwoStreamsSetup {
   import GraphDSL.Implicits._
@@ -86,9 +87,7 @@ class GraphZipWithNSpec extends TwoStreamsSetup {
       probe.expectNext(1 / 1 / -2)
       probe.expectNext(1 / 2 / -1)
 
-      EventFilter[ArithmeticException](occurrences = 1).intercept {
-        subscription.request(2)
-      }
+      subscription.request(2)
       probe.expectError() match {
         case a: java.lang.ArithmeticException => a.getMessage should be("/ by 
zero")
         case unexpected                       => throw new 
RuntimeException(s"Unexpected: $unexpected")
@@ -96,6 +95,41 @@ class GraphZipWithNSpec extends TwoStreamsSetup {
       probe.expectNoMessage(200.millis)
     }
 
+    "fail stream when zipper throws and supervision is Stop" in {
+      val ex = new RuntimeException("boom")
+      val result = Source
+        .zipWithN[Int, Int](s => if (s.head == 3) throw ex else 
s.sum)(immutable.Seq(Source(1 to 4), Source(1 to 4)))
+        
.withAttributes(ActorAttributes.supervisionStrategy(Supervision.stoppingDecider))
+        .runWith(Sink.seq)
+      result.failed.futureValue shouldBe ex
+    }
+
+    "fail stream when zipper throws and supervision defaults to Stop" in {
+      val ex = new RuntimeException("boom")
+      val result = Source
+        .zipWithN[Int, Int](s => if (s.head == 3) throw ex else 
s.sum)(immutable.Seq(Source(1 to 4), Source(1 to 4)))
+        .runWith(Sink.seq)
+      result.failed.futureValue shouldBe ex
+    }
+
+    "resume when zipper throws and drop failed zipped element" in {
+      val future = Source
+        .zipWithN[Int, Int](s => if (s.head == 3) throw new 
RuntimeException("boom") else s.sum)(
+          immutable.Seq(Source(1 to 4), Source(1 to 4)))
+        
.withAttributes(ActorAttributes.supervisionStrategy(Supervision.resumingDecider))
+        .runWith(Sink.seq)
+      Await.result(future, 3.seconds) shouldBe Seq(2, 4, 8)
+    }
+
+    "restart when zipper throws and drop failed zipped element" in {
+      val future = Source
+        .zipWithN[Int, Int](s => if (s.head == 3) throw new 
RuntimeException("boom") else s.sum)(
+          immutable.Seq(Source(1 to 4), Source(1 to 4)))
+        
.withAttributes(ActorAttributes.supervisionStrategy(Supervision.restartingDecider))
+        .runWith(Sink.seq)
+      Await.result(future, 3.seconds) shouldBe Seq(2, 4, 8)
+    }
+
     commonTests()
 
     "work with one immediately completed and one nonempty publisher" in {
diff --git 
a/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/GraphZipWithSpec.scala
 
b/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/GraphZipWithSpec.scala
index b2a9d69856..5428dce8c7 100644
--- 
a/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/GraphZipWithSpec.scala
+++ 
b/stream-tests/src/test/scala/org/apache/pekko/stream/scaladsl/GraphZipWithSpec.scala
@@ -18,7 +18,6 @@ import scala.concurrent.duration._
 import org.apache.pekko
 import pekko.stream._
 import pekko.stream.testkit._
-import pekko.testkit.EventFilter
 
 class GraphZipWithSpec extends TwoStreamsSetup {
   import GraphDSL.Implicits._
@@ -85,9 +84,7 @@ class GraphZipWithSpec extends TwoStreamsSetup {
       probe.expectNext(1 / -2)
       probe.expectNext(2 / -1)
 
-      EventFilter[ArithmeticException](occurrences = 1).intercept {
-        subscription.request(2)
-      }
+      subscription.request(2)
       probe.expectError() match {
         case a: java.lang.ArithmeticException => a.getMessage should be("/ by 
zero")
         case unexpected                       => throw new 
RuntimeException(s"Unexpected: $unexpected")
diff --git 
a/stream/src/main/boilerplate/org/apache/pekko/stream/scaladsl/ZipWithApply.scala.template
 
b/stream/src/main/boilerplate/org/apache/pekko/stream/scaladsl/ZipWithApply.scala.template
index a1239db97a..2cdcdf5aaa 100644
--- 
a/stream/src/main/boilerplate/org/apache/pekko/stream/scaladsl/ZipWithApply.scala.template
+++ 
b/stream/src/main/boilerplate/org/apache/pekko/stream/scaladsl/ZipWithApply.scala.template
@@ -13,7 +13,10 @@
 
 package org.apache.pekko.stream.scaladsl
 
+import scala.util.control.NonFatal
+
 import org.apache.pekko.stream._
+import org.apache.pekko.stream.ActorAttributes.SupervisionStrategy
 import org.apache.pekko.stream.impl.ContextPropagation
 import org.apache.pekko.stream.stage._
 
@@ -41,6 +44,7 @@ class ZipWith1[[#A1#], O] (val zipper: ([#A1#]) => O) extends 
GraphStage[FanInSh
   ]
 
   override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = 
new GraphStageLogic(shape) {
+    private lazy val decider = 
inheritedAttributes.mandatoryAttribute[SupervisionStrategy].decider
     var pending = ##0
     // Without this field the completion signalling would take one extra pull
     var willShutDown = false
@@ -48,7 +52,22 @@ class ZipWith1[[#A1#], O] (val zipper: ([#A1#]) => O) 
extends GraphStage[FanInSh
 
     private def pushAll(): Unit = {
       contextPropagation.resumeContext()
-      push(out, zipper([#grab(in0)#]))
+      try push(out, zipper([#grab(in0)#]))
+      catch {
+        case NonFatal(ex) =>
+          decider(ex) match {
+            case Supervision.Stop =>
+              failStage(ex)
+            case Supervision.Resume | Supervision.Restart =>
+              if (willShutDown) completeStage()
+              else {
+                pending += shape.inlets.size
+                [#pull(in0)#
+                ]
+              }
+          }
+          return
+      }
       if (willShutDown) completeStage()
       else {
         [#pull(in0)#
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 3015f02e31..91fea73f05 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
@@ -3847,6 +3847,12 @@ final class Flow[In, Out, Mat](delegate: 
scaladsl.Flow[In, Out, Mat]) extends Gr
    * Put together the elements of current [[Flow]] and the given [[Source]]
    * into a stream of combined elements using a combiner function.
    *
+   * Adheres to the [[ActorAttributes.SupervisionStrategy]] attribute.
+   *
+   * If the combiner function throws and the supervision decision is 
[[pekko.stream.Supervision#stop]]
+   * the stream fails. If the supervision decision is 
[[pekko.stream.Supervision#resume]] or
+   * [[pekko.stream.Supervision#restart]] the zipped element is dropped and 
the stream continues.
+   *
    * '''Emits when''' all of the inputs have an element available
    *
    * '''Backpressures when''' downstream backpressures
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 479807d37f..5ccd8a3870 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
@@ -589,6 +589,12 @@ object Source {
 
   /**
    * Combine the elements of multiple streams into a stream of lists using a 
combiner function.
+   *
+   * Adheres to the [[ActorAttributes.SupervisionStrategy]] attribute.
+   *
+   * If the combiner function throws and the supervision decision is 
[[pekko.stream.Supervision#stop]]
+   * the stream fails. If the supervision decision is 
[[pekko.stream.Supervision#resume]] or
+   * [[pekko.stream.Supervision#restart]] the zipped element is dropped and 
the stream continues.
    */
   def zipWithN[T, O](
       zipper: function.Function[java.util.List[T], O],
@@ -1936,6 +1942,12 @@ final class Source[Out, Mat](delegate: 
scaladsl.Source[Out, Mat]) extends Graph[
    * Put together the elements of current [[Source]] and the given one
    * into a stream of combined elements using a combiner function.
    *
+   * Adheres to the [[ActorAttributes.SupervisionStrategy]] attribute.
+   *
+   * If the combiner function throws and the supervision decision is 
[[pekko.stream.Supervision#stop]]
+   * the stream fails. If the supervision decision is 
[[pekko.stream.Supervision#resume]] or
+   * [[pekko.stream.Supervision#restart]] the zipped element is dropped and 
the stream continues.
+   *
    * '''Emits when''' all of the inputs has an element available
    *
    * '''Backpressures when''' downstream backpressures
diff --git 
a/stream/src/main/scala/org/apache/pekko/stream/javadsl/SubFlow.scala 
b/stream/src/main/scala/org/apache/pekko/stream/javadsl/SubFlow.scala
index ca1651bc69..00e81790f8 100755
--- a/stream/src/main/scala/org/apache/pekko/stream/javadsl/SubFlow.scala
+++ b/stream/src/main/scala/org/apache/pekko/stream/javadsl/SubFlow.scala
@@ -2570,6 +2570,12 @@ final class SubFlow[In, Out, Mat](
    * Put together the elements of current [[Flow]] and the given [[Source]]
    * into a stream of combined elements using a combiner function.
    *
+   * Adheres to the [[ActorAttributes.SupervisionStrategy]] attribute.
+   *
+   * If the combiner function throws and the supervision decision is 
[[pekko.stream.Supervision#stop]]
+   * the stream fails. If the supervision decision is 
[[pekko.stream.Supervision#resume]] or
+   * [[pekko.stream.Supervision#restart]] the zipped element is dropped and 
the stream continues.
+   *
    * '''Emits when''' all of the inputs has an element available
    *
    * '''Backpressures when''' downstream backpressures
diff --git 
a/stream/src/main/scala/org/apache/pekko/stream/javadsl/SubSource.scala 
b/stream/src/main/scala/org/apache/pekko/stream/javadsl/SubSource.scala
index dc17efe2f1..bf55e25495 100755
--- a/stream/src/main/scala/org/apache/pekko/stream/javadsl/SubSource.scala
+++ b/stream/src/main/scala/org/apache/pekko/stream/javadsl/SubSource.scala
@@ -2537,6 +2537,12 @@ final class SubSource[Out, Mat](
    * Put together the elements of current [[Flow]] and the given [[Source]]
    * into a stream of combined elements using a combiner function.
    *
+   * Adheres to the [[ActorAttributes.SupervisionStrategy]] attribute.
+   *
+   * If the combiner function throws and the supervision decision is 
[[pekko.stream.Supervision#stop]]
+   * the stream fails. If the supervision decision is 
[[pekko.stream.Supervision#resume]] or
+   * [[pekko.stream.Supervision#restart]] the zipped element is dropped and 
the stream continues.
+   *
    * '''Emits when''' all of the inputs has an element available
    *
    * '''Backpressures when''' downstream backpressures
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 c35fa5dfb6..2a4657e5c8 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
@@ -3420,6 +3420,12 @@ trait FlowOps[+Out, +Mat] {
    * Put together the elements of current flow and the given [[Source]]
    * into a stream of combined elements using a combiner function.
    *
+   * Adheres to the [[ActorAttributes.SupervisionStrategy]] attribute.
+   *
+   * If the combiner function throws and the supervision decision is 
[[pekko.stream.Supervision.Stop]]
+   * the stream fails. If the supervision decision is 
[[pekko.stream.Supervision.Resume]] or
+   * [[pekko.stream.Supervision.Restart]] the zipped element is dropped and 
the stream continues.
+   *
    * '''Emits when''' all of the inputs have an element available
    *
    * '''Backpressures when''' downstream backpressures
diff --git a/stream/src/main/scala/org/apache/pekko/stream/scaladsl/Graph.scala 
b/stream/src/main/scala/org/apache/pekko/stream/scaladsl/Graph.scala
index 6cde3ffa9f..415af7ea12 100755
--- a/stream/src/main/scala/org/apache/pekko/stream/scaladsl/Graph.scala
+++ b/stream/src/main/scala/org/apache/pekko/stream/scaladsl/Graph.scala
@@ -1096,6 +1096,12 @@ final class ZipLatest[A, B](eagerComplete: Boolean) 
extends ZipLatestWith2[A, B,
  * '''Completes when''' any upstream completes
  *
  * '''Cancels when''' downstream cancels
+ *
+ * Adheres to the [[ActorAttributes.SupervisionStrategy]] attribute.
+ *
+ * If the combiner function throws and the supervision decision is 
[[pekko.stream.Supervision.Stop]]
+ * the stream fails. If the supervision decision is 
[[pekko.stream.Supervision.Resume]] or
+ * [[pekko.stream.Supervision.Restart]] the zipped element is dropped and the 
stream continues.
  */
 object ZipWith extends ZipWithApply
 
@@ -1219,6 +1225,12 @@ object ZipWithN {
  * '''Completes when''' any upstream completes
  *
  * '''Cancels when''' downstream cancels
+ *
+ * Adheres to the [[ActorAttributes.SupervisionStrategy]] attribute.
+ *
+ * If the combiner function throws and the supervision decision is 
[[pekko.stream.Supervision.Stop]]
+ * the stream fails. If the supervision decision is 
[[pekko.stream.Supervision.Resume]] or
+ * [[pekko.stream.Supervision.Restart]] the zipped element is dropped and the 
stream continues.
  */
 class ZipWithN[A, O](zipper: immutable.Seq[A] => O)(n: Int) extends 
GraphStage[UniformFanInShape[A, O]] {
   override def initialAttributes = DefaultAttributes.zipWithN
@@ -1227,6 +1239,7 @@ class ZipWithN[A, O](zipper: immutable.Seq[A] => O)(n: 
Int) extends GraphStage[U
 
   override def createLogic(inheritedAttributes: Attributes): GraphStageLogic =
     new GraphStageLogic(shape) with OutHandler {
+      private lazy val decider = 
inheritedAttributes.mandatoryAttribute[SupervisionStrategy].decider
       var pending = 0
       // Without this field the completion signalling would take one extra pull
       var willShutDown = false
@@ -1238,7 +1251,21 @@ class ZipWithN[A, O](zipper: immutable.Seq[A] => O)(n: 
Int) extends GraphStage[U
 
       private def pushAll(): Unit = {
         contextPropagation.resumeContext()
-        push(out, zipper(shape.inlets.map(grabInlet)))
+        try push(out, zipper(shape.inlets.map(grabInlet)))
+        catch {
+          case NonFatal(ex) =>
+            decider(ex) match {
+              case Supervision.Stop =>
+                failStage(ex)
+              case Supervision.Resume | Supervision.Restart =>
+                if (willShutDown) completeStage()
+                else {
+                  pending += n
+                  shape.inlets.foreach(pullInlet)
+                }
+            }
+            return
+        }
         if (willShutDown) completeStage()
         else shape.inlets.foreach(pullInlet)
       }
diff --git 
a/stream/src/main/scala/org/apache/pekko/stream/scaladsl/Source.scala 
b/stream/src/main/scala/org/apache/pekko/stream/scaladsl/Source.scala
index 4cd4594e1d..8b4e60d539 100644
--- a/stream/src/main/scala/org/apache/pekko/stream/scaladsl/Source.scala
+++ b/stream/src/main/scala/org/apache/pekko/stream/scaladsl/Source.scala
@@ -887,6 +887,12 @@ object Source {
 
   /**
    * Combine the elements of multiple streams into a stream of sequences using 
a combiner function.
+   *
+   * Adheres to the [[ActorAttributes.SupervisionStrategy]] attribute.
+   *
+   * If the combiner function throws and the supervision decision is 
[[pekko.stream.Supervision.Stop]]
+   * the stream fails. If the supervision decision is 
[[pekko.stream.Supervision.Resume]] or
+   * [[pekko.stream.Supervision.Restart]] the zipped element is dropped and 
the stream continues.
    */
   def zipWithN[T, O](zipper: immutable.Seq[T] => O)(sources: 
immutable.Seq[Source[T, ?]]): Source[O, NotUsed] = {
     val source = sources match {


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

Reply via email to