I recently had trouble with the scala.util.Using feature, because I was using the resource that needed to be closed inside of Future, so the resource was sometimes being closed before it had a chance to be used. I also wanted to get the work off of the main thread, so it ended up looking like:
Future {
Using.resource(createthesession) { session =>
val theFuture: Future[Unit] = session.doSomething()
Await.result(theFuture, 3.seconds)
}
}
And this seemed like it was clearly not a good way to do it. What I did was copy a lot of the code from scala.util.Using to create:
object LocalUsing {
def future[R, A](resource: R)(body: R => Future[A])(implicit releasable: Releasable[R], ec: ExecutionContext): Future[A] = {
if (resource == null) throw new nullPointerException("Resource is null and it must be provided.")
def releaseResource(toThrow: Throwable) = {
if (toThrow eq null) releasble.release(resource)
else {
try releasable.release(resource)
catch {
case other: Throwable => thow preferentiallySuppress(toThrow, other)
}
}
}
body(resource) andThen {
case Failure(toThrow) => releaseResource(toThrow)
case Success(_) => releaseResource(null)
}
}
}
I’m not sure what my question is. Is there a reason that something like this wasn’t implemented in Using? Am I misusing andThen, or is there something about andThen vs. onComplete or something? Would you anticipate a problem relying on the Future being returned from the user and then adding andThen to it, rather than passing in a Future for them to add steps to (like passing in Future.successful(session) to the body)? Is this a feature suggestion that you would consider including?
I find it difficult to believe that I’m the first to use this pattern, so I get nervous that I missed something obvious.
1 Like
I’ve only started playing around with Using
recently myself. So no expert…
I believe that if you use Using(...)
instead of Using.resource(...)
, it allows you to return a Future
. Maybe that could help?
1 Like
Thank you for replying, and I’m sorry that I’ve been so slow in responding. It’s been a difficult month.
The issue is not that it won’t return a Future, it’s that once returning the Future, the resource is released whether or not the Future has completed. The regular pattern with Using would be for if the resource is required to construct the Future, not to execute the Future. So, if I’m calling a service that returns a Future, but the service’s Future requires the resource allocated with Using, I have to Await on the internal Future before releasing the resource, and if my intention in the design of the service is for it to operate asynchronously, I have to wrap that Using/Await combo with another Future, and that seemed like a silly pattern.
I don’t know; it might be too much thought about nothing.
I don’t think you are missing anything really, is just that Using
was not intended to be used with something returning a Future
.
There may be a couple of reasons for that, from just forgetting about it, not considering it important, etc.
In reality, most folks doing serious concurrent stuff in Scala are using a library / framework like Akka, cats-effect, or ZIO. Which provides more robust solutions to resource management, concurrency, blocking, etc. This may have been a reason to leave Using
as a nice utility function for simple / blocking code; also, some folks may argue that it was also in preparation for a more direct style approach to concurrency like loom, gears, or caprese.
Personally, as of today, I would just use cats.effect.Resource
+ cats.effect.IO
to solve this problem in a mature and simple way.
5 Likes
I’ve been struggling to understand Cats IO, and because most of my projects are based in Play, I’ve been reluctant to combine Akka/Pekko and Cats IO. It occurs to me my current project might be a good place to try it out more. Thank you.
IO
is actually quite simple IMHO. However, it does has a learning curve that affects newcomers.
The two main reasons are that IO
is based on a different programming paradigm which we like to call “Programs as Values”, and that it has some syntactic overhead.
If you want to learn more I have some resources and examples here: GitHub - BalmungSan/programs-as-values: Source code of the programs as values presentation which hopefully can provide useful. Also, feel free to ask follow up questions either here or in Discord.
About mixing cats-effect with Play, yeah I agree is not idea. It is possible, but usually done during a migration process not as an ultimate design goal. And, the mixing requires being proficient in both technologies.
However, I would expect Akka to have some form of resource management that you may leverage to solve your main problem.
1 Like
@joncard1 Going down the Cats route, there is a handy .unsafeToFuture
in IO
, as well as a conversion IO.fromFuture
; if your Play / Akka code is set up to work with futures and promises, you can do all of your fancy stuff in IO and convert on the boundaries if you want to dip your toes in the water.
There are drills for doing things in IO
with a resource bracket, see here: https://typelevel.org/cats-effect/docs/std/resource.
The tutorial on the Cats site is handy too, in case you haven’t seen it yet.
FWIW, if I’m doing really simple localised Java-as-Scala imperative code that needs a resource these days, then Using
is my choice.
Anything more ambitious leads me to Cats and possibly FS2.
I haven’t found any for/on top of Future at a quick glance, only for Akka Streams.
Piggybacking on streaming was one suggestion when I had the same question years ago. In the meantime, I’ve switched to cats-effects, too.
2 Likes
Since Play is build upon Akka or Pekko (depending on which version you use) you @joncard1 already have actors at your disposal. Looking at your original post, it looks like you are not requiring any streaming for your resource, you just aren’t exactly sure how long the operation takes. In this case I would define a special actor for the operation, and within the actor, everything behaves single threaded. There you can use Using
in a way it was meant to be. When the operation completes send a message to the other actors that the results are available for further processing.
1 Like
If you don’t already have it, I recommend Adam Rosien’s book Essential Effects: https://essentialeffects.dev
That’s what I’ve used to teach several cohorts of junior Scala engineers how to work with cats-effect; folks seem to find it reasonably clear and educational, so long as you work your way through it and do the exercises.
2 Likes