cats-effect
cats-effect copied to clipboard
Idea: Managed resource map
trait Manager[F[_], K, V] {
def +=(pair: (K, Resource[F, V])): F[Unit]
def -=(key: K): F[Unit]
def apply(key: K): F[Option[V]]
def join(key: K): F[V]
}
object Manager {
def apply[F[_]: Sync, K, V]: Resource[F, Manager[F, K, V]] = ???
}
The idea here is that it's often useful to have a concurrent map-like data structure which manages Resource lifecycles for you, indexed by some key (so you can retrieve the results). These resources can be submitted (via +=) and will be evaluated in background, with their value stored in the map once complete. Removing the key from the map would close the resource scope. Similarly, closing the resource scope on the Manager would close all constituent Resources and cancel any outstanding computations.
and will be evaluated in background, with their value stored in the map once complete.
should this allow cancellation? What happens if the evaluation fails?
and will be evaluated in background, with their value stored in the map once complete.
should this allow cancellation? What happens if the evaluation fails?
It would only be cancelable by -= or closing the Manager, I think. If it fails, I would probably fail apply and join's effects. That is, if I understand join correctly (apply + getOrElse raise?)
Should failing remove the entry from the map, or should that be pushed back to the user? I could see it trying to acquire the Resource again.
I think this might be a case where we want to designate V as being thread-safe, since multiple fibers can trivially access the same resource, or should that be forbidden?
should this allow cancellation?
Yes
What happens if the evaluation fails?
Right now, the interface doesn't have a good channel for that. The key/value pair would simply not be inserted into the map. Users could add their own side-channels by tossing an onError on the resource before insertion, or we could do something for them to allow for the reporting. Certainly I think that, if the user joins, it should be reported back to them. In fact, perhaps join should return an Outcome.
I think this might be a case where we want to designate V as being thread-safe, since multiple fibers can trivially access the same resource, or should that be forbidden?
I would definitely assume V has to be thread-safe. This kind of structure doesn't make much sense otherwise, imo.
This sounds a lot like keypool - https://github.com/ChristopherDavenport/keypool
I think you could implement keypool with this, but this is definitely a different thing. There are no bounds here, and the idea is specifically to submit Resources, rather than effects, because the point of such a construct would be to manage the still-open lifecycles of those resources until removed from the map.
I'll work on this if nobody minds, I've had need for something similar and it'd be nice to have a standard implementation.
@wemrysi I totally missed your comment. Take it. :-)
Just so I can find this in the future when I inevitably look for it… I often refer to this type of construct as a "job manager".
I've been using something like this at work managing a bunch of external processes that create a DB proxy connection (think GCP Cloud sql proxy), can be launched at will, need to coexist, and have a life cycle.
It was very useful, but a bit annoying to build correctly.
Yeah I've basically always needed this type of thing at every company I've ever worked at which used Cats Effect or any of its predecessors. It's a super commonly needed bit of machinery.