-
Notifications
You must be signed in to change notification settings - Fork 528
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
IO.race doesn't propagate winner's IOLocal context #3100
Comments
Thanks for reporting. Currently this is by-design. We also encountered this when investigating tracing for See also some discussion on more sophisticated semantics for |
So it's a "can't fix" from fs2 side and "won't fix" from cats-effect side? That's sad, IOLocals are amazing when they work properly. But I didn't see a discussion about race semantics in the initial merge request, which is not exactly the same as fork/join. Since |
I've taken a closer look on a So it's a bit more complexity than I expected initially, but we can still reimplement |
So the general solution that was envisioned for this when we decided the local semantics is actually IOLocalRef, which ironically wasn't implemented at the time because no one could come up with a practical scenario where it was needed. Fast forward a bit and this is actually the first time I've heard of this interaction with Fs2! Whoops. The problem in general with IOLocal itself having special interactions with fiber joining is that most interesting concurrent code uses Deferred or Queue rather than join, and obviously you can't enrich those with special magic. IOLocalRef solves this though by putting a Ref in the local and mutating that, so your context propagates around. |
This is the first I heard about |
@armanbilge That's exactly what it was proposed to be. Like I said, we never actually implemented it. :-) But basically, it was intended to be |
Just to throw it out there, another possible way we could try and support these usecases without implementing def timeout[A](ioa: IO[A], duration: Duration): IO[A] =
IO.fiber { fiber => // get handle to the current fiber
(IO.sleep(duration) *> fiber.cancel).background.surround {
ioa
}
} In this case, you never leave the fiber started the timeout, so locals propagate completely naturally. (You also save the "overhead" of creating and scheduling an extra fiber to run This would have limited applicability however e.g. you could not implement |
I think this would also change the definition of |
Oh you're right! I forgot that's how it's working currently, for some reason I thought it just canceled. So, yeah, tricky :) |
apparently I didn't see this thread but wrote a blogpost showcasing this very idea: https://blog.kubukoz.com/flavors-of-shared-state/ What's noteworthy, I think, is that putting a And for the record, I think IOLocal's semantics do make sense in some scenarios. |
We're using IOLocal for trace id propagation. It worked for us perfectly as expected when dealing just with IO instances. However, currently we can't use IOLocal magic with fs2 streams, due to them using IO.race to handle interruptions.
Here's a cats-effect example:
Scastie for this code snippet: https://scastie.scala-lang.org/jshavzY0STWiAESgLgTqHg
Wrapping
IOLocal.set
statement inIO.race
leads to context being enriched with a new value and then immediately discarded on exiting the race.And here's an example with fs2 that we've actually encountered in the wild:
Scastie for this code snippet: https://scastie.scala-lang.org/LghgY5G4RMWx0f4CRmUIqw
So any usage of
interruptWhen
on afs2.Stream
wraps everyeval
intoF.race
to handle actual interruptions.A possible solution to make IOLocal usable with fs2 would be preserving IOLocal changes made within the race if they're made from an effect that won the race. While it makes sense to avoid merging contexts after joining back from
IO.both
,IO.race
should be less problematic, as no merging is required here, just preserving winner's context.Shoutout to @danielleontiev for debugging this issue with me
The text was updated successfully, but these errors were encountered: