ZIO Fiber orElse generate exception messages
Asked Answered
F

2

5

I want to use the combinator orElse on ZIO Fibers. From docs:

If the first fiber succeeds, the composed fiber will succeed with its result; otherwise, the composed fiber will complete with the exit value of the second fiber (whether success or failure).

import zio._
import zio.console._

object MyApp extends App {

  def f1 :Task[Int] = IO.fail(new Exception("f1 fail"))
  def f2 :Task[Int] = IO.succeed(2)

  val myAppLogic =
    for {
      f1f <- f1.fork
      f2f <- f2.fork
      ff  =  f1f.orElse(f2f)
      r   <- ff.join
      _   <- putStrLn(s"Result is [$r]")
    } yield ()

  def run(args: List[String]) =
    myAppLogic.fold(_ => 1, _ => 0)
}

I run it with sbt in console. And output:

[info] Running MyApp
Fiber failed.
A checked error was not handled.
java.lang.Exception: f1 fail
        at MyApp$.f1(MyApp.scala:6)
        at MyApp$.<init>(MyApp.scala:11)
        at MyApp$.<clinit>(MyApp.scala)
        at MyApp.main(MyApp.scala)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Result is [2]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at sbt.Run.invokeMain(Run.scala:93)
        at sbt.Run.run0(Run.scala:87)
        at sbt.Run.execute$1(Run.scala:65)
        at sbt.Run.$anonfun$run$4(Run.scala:77)
        at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12)
        at sbt.util.InterfaceUtil$$anon$1.get(InterfaceUtil.scala:10)
        at sbt.TrapExit$App.run(TrapExit.scala:252)
        at java.lang.Thread.run(Thread.java:748)

Fiber:Id(1574829590403,2) was supposed to continue to: <empty trace>

Fiber:Id(1574829590403,2) ZIO Execution trace: <empty trace>

Fiber:Id(1574829590403,2) was spawned by:

Fiber:Id(1574829590397,1) was supposed to continue to:
  a future continuation at MyApp$.myAppLogic(MyApp.scala:12)
  a future continuation at MyApp$.run(MyApp.scala:19)

Fiber:Id(1574829590397,1) ZIO Execution trace: <empty trace>

Fiber:Id(1574829590397,1) was spawned by:

Fiber:Id(1574829590379,0) was supposed to continue to:
  a future continuation at zio.App.main(App.scala:57)
  a future continuation at zio.App.main(App.scala:56)

[Fiber:Id(1574829590379,0) ZIO Execution trace: <empty trace>

I see the result of seconds Fiber, is Result is [2] But why it output these unnecessary exception/warning messages?

Fledgy answered 27/11, 2019 at 4:48 Comment(3)
Is this on the latest ZIO version (RC17)?Brussels
Checked on the latest version. The issues does existTreponema
Maybe related to this bug github.com/zio/zio/issues/2309Brussels
I
5

By default a fiber failure warning is generated when a fiber that is not joined back fails so that errors do not get lost. But as you correctly note in some cases this is not necessary as the error is handled internally by the program logic, in this case by the orElse combinator. We have been working through a couple of other cases of spurious warnings being generated and I just opened a ticket for this one here. I expect we will have this resolved in the next release.

Inga answered 27/11, 2019 at 14:30 Comment(0)
D
4

This happens because the default instance of Platform being created by zio.App has a default which reports uninterrupted, failed fibers to the console:

def reportFailure(cause: Cause[_]): Unit =
    if (!cause.interrupted)
      System.err.println(cause.prettyPrint)

To avoid this, you can provide your own Platform instance which doesn't do so:

import zio._
import zio.console._
import zio.internal.{Platform, PlatformLive}

object MyApp extends App {
  override val Platform: Platform = PlatformLive.Default.withReportFailure(_ => ())

  def f1: Task[Int] = IO.fail(new Exception("f1 fail"))
  def f2: Task[Int] = IO.succeed(2)

  val myAppLogic =
    for {
      f1f <- f1.fork
      f2f <- f2.fork
      ff = f1f.orElse(f2f)
      r <- ff.join
      _ <- putStrLn(s"Result is [$r]")
    } yield ()

  def run(args: List[String]) =
    myAppLogic.fold(_ => 1, _ => 0)
}

Which yields:

Result is [2]

As @Adam Fraser noted, this will probably get fixed in a nearby release.

Edit:

Should be fixed after https://github.com/zio/zio/pull/2339 was merged

Danit answered 27/11, 2019 at 14:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.