-
Notifications
You must be signed in to change notification settings - Fork 605
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
Prototype of file rotation / resource proxying #1667
Conversation
I like the addition. As with many things I'd like to refine it here in fs2 before even considering it for cats-effect, especially given that as you say it appears mostly useful with Stream. Is the big I collected the questions I originally had in the channel. It would be good to spec the behaviour out precisely (not necessarily in a test to begin with ofc, comments only are fine)
Looks like a cool idea though :) |
Not sure. I was thinking that was a nice way to avoid dealing with cancellation occurring mid-swap. This has a nice symmetry with acquire being uncancelable -- swap is really an acquire + a release.
Eager
New acquire completes and then old release occurs. This simplifies of bunch of cases -- e.g., no need for special error handling when acquiring fails as part of swap.
Waits for new acquire, then waits for old release, then returns new R
On swap, and the already acquired resource remains the target of the proxy
After successful acquisition of new resource
Avoided this issue by making swap uncancellable. |
Thanks for all the answers :)
A single |
Oh good point. I tend to think of a resource as a single acquire. Any ideas on how to maintain cancellation in swap? Or alternatively, any use cases where uncancellable swap is problematic? |
Not right now, but this is why I wanted the spec (which you provided above), so I can play with implementing that.
well, my instinct is that |
It feels like this is similar to some work I've done previously; namely this: https://gist.github.com/Daenyth/28243952f1fcfac6e8ef838040e8638e |
I pushed |
I have been doing some thinking about this, and I have the two followup questions:
|
Hm, I don't recall any obvious issues that occur when two fibers call |
Actually you are right, apologies, I have made a mistake thinking about that |
First of all sorry for the endless questions, hopefully I'm not giving you too much of a hard time on this PR :)
If we do unify, it's still a bit unclear to me what the type should be, my first thought was to pass in a function representing the usage of the P.S: On a separate point, I'm making progress on the interruption aspect. I'm also tempted to write one version with the |
This core problem is why I used the |
We could replace |
That's effectively what I did, except using |
Notably, I was able to also guard incoming |
Yeah I was planning to do this as well. |
I think the api will be:
With the following semantics: |
How would you get access to the initial allocated resource? As long as we can get writeRotate working, I’m happy :)
… On Oct 22, 2019, at 12:46 PM, Fabio Labella ***@***.***> wrote:
I think the api will be:
trait ResourceProxy[F, A] {
def swap(next: Resource[F, A]): Resource[F, A]
}
object ResourceProxy {
def create[F[_]: Concurrent, A](initial: Resource[F, A]): Resource[F, ResourceProxy[F, A]]
}
With the following semantics: swap will wait until the previous consumer is done (which can be tracked with the type above), then will shutdown the current resource and acquire the new one.
I'm working on the implementation on this (I have it mostly done in my head :P). Let me know if the above makes sense
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or unsubscribe.
|
This turned out to be easy with some slight changes to the api. However, the idea of tracking the consumer is not possible unfortunately: having That means that unavoidably is up to the user to ensure no consumer is using On the bright side, I have made |
Looks great! Think we're ready for merge soon? One question on |
I think so yeah. The only question is the order of finalisation-acquisition, in an ideal world someone would come in with a really strong argument one way or another. Oh, and I guess more testing on the semantics of ResourceProxy in general wouldn't be out of place :P
This is where I left off yesterday, I'm actually not sure. I'm leaning towards |
I'm hoping to make some progress on this today, time allowing. The current API is kind of awkward given that Stream.resource(ResourceProxy.create[F, R]).flatMap { p =>
Stream.eval(p.swap(mkResource)).flatMap { r =>
// use r, call p.swap again if needed
}
} That is:
Thoughts on adding this as a combinator? Something like: Stream.resourceProxy(mkResource).flatMap { case (p, r) =>
// use r, call p.swap again if needed
}
// On Stream
def resourceProxy[F[_], R](mk: Resource[F, R]): Stream[F, (ResourceProxy[F, R], R)] =
Stream.resource(ResourceProxy.create[F, R]).flatMap { p =>
Stream.eval(p.swap(mk)).flatMap { r =>
Stream((p, r))
}
} Note this has nicer type inference. Also, I wonder if the "proxy" notion is intuitive? Maybe instead we should call it |
I agree that it's slightly clunky, but not 100% sure that adding the entire combinator to Stream is called for. def initial(r: Resource[F, R]): Resource[F, (ResourceProxy[F, R], R)] =
create.evalMap(...) and the boilerplate is reduced to I do agree that the proxy name is not super intuitive though EDIT: if you still think that |
Yep good idea. What do you think of |
I like |
OK I think this is ready for review. One question -- I think we should support a |
Maybe I will add a couple of tests around interruption but we can merge, no need to hold off longer
what's the use case? Early finalisation of something when you don't yet know when to |
Yeah, was imagining a use case where you want to close a resource but you don't have the info needed to create a new one until later. |
* Creates a new `Hotswap` initialized with the specified resource. | ||
* The `Hotswap` instance and the initial resource are returned. | ||
*/ | ||
def apply[F[_]: Concurrent, R](initial: Resource[F, R]): Resource[F, (Hotswap[F, R], R)] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how inconvenient do you think it would be to rename this to create
or make
, and create
to empty
? apply
is pretty much as convenient as the Stream combinator you wanted, so if you really want that convenience, ignore this comment. My doubt stems from the fact that apply
on tagless stuff is generally for summoning
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IDK, I like using apply
for the default constructor. We have precedence for it too with things like Deferred
and Socket
and FileHandle
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, leave it
* r2 released | | ||
* r3 released | ||
* }}} | ||
* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's pretty slick :)
@SystemFw I added the |
} | ||
|
||
override def clear: F[Unit] = | ||
swapFinalizer(().pure[F]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needs uncancelable
here :)
Let's merge after the one comment is resolved :) |
Interested to hear thoughts on this. The
ResourceProxy
type is general enough for cats-effect but probably only really useful when used withStream
. AFAIK, this is the first resource & memory safe way to do file rotation with fs2. I've implemented versions of this through the years dating back to 0.8 but they all at least leaked aRelease
node in the algebra if nothing else.Needs a lot of docs and tests and the signature of
writeRotate
isn't general enough probably. Should also makewriteRotate
behave likewriteAll
in that it respects theAPPEND
file option.