-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Implement cache operator #209
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
Conversation
RxJava-pull-requests #50 FAILURE |
Returns an Observable that repeats the original Observable sequence to all subscribers. The source Observable is subscribed to at most once.
RxJava-pull-requests #51 SUCCESS |
My understanding is that Publish and Multicast don't remember or replay events that the source Observable generated before a given subscriber subscribed. |
I think you're right. How about Replay (#71)? Reading through your code submission now ... |
I really like how many unit tests there are ... very nice. I need to think through the implications of this compared with Replay/RefCount/Publish/Multicast etc. In other words, if Rx.Net doesn't have this operator for such a common case it seems that it should already be covered by one of the ones we have not yet implemented. |
Replay could be what this is, modulo Replay's connect() semantics (which I can't quite decipher from the documentation). |
That part is still somewhat confusing to me as well as I haven't yet spent the time to fully grok these operators and what it will take to implement. Perhaps though a What I'm trying to accomplish is first achieve the functionality provided by Rx and only then add new functionality if it's not already accounted for. Rx.Net has years of thought into the API design and functionality and there is value in common knowledge across implementations if we can maintain the same behavior. |
I can see why one would need a connect() method for Publish: one would want to be able to subscribe multiple observers to a Publish before having the Publish subscribe to its source. If the Publish subscribed to its source upon the first subscription it got, its second subscriber wouldn't get the initial set of notifications. The connect() method makes a lot less sense for Replay, as the second and later subscribers would get the initial set of notifications either way. The only things I can think of are:
|
I believe cache is a wrapper around Replay that calls the Replay's connect() upon the first subscription. |
Are you interested in exploring what an implementation of Replay would look like (not necessarily all of the overloads that we eventually want but whatever is necessary for this use case) and perhaps submitting that implementation to accomplish |
I think so. I would need to know what the behavior of a second call to connect() is supposed to be, both with and without the first call being unsubscribed. |
If you can play with Rx.Net or RxJS that would be the best way to determine the correct behavior. You can also take a look at the Rx.Net source: |
|
Is this going away because of #218 or does this still make sense? |
A variant of Replay that connects on first subscription could make sense, but might not meet the threshold for adding a new operator. In any case, it wouldn't use the commit in this request. |
I think I figured out when the ConnectableObservable would come into play with Replay in a cache scenario.
Now what do we do? We can't unsubscribe from source because subscriberB still expects to receive values.
Do we unsubscribe from source now? That's how the
In other words, the Thus the ConnectableObservable is a way to unsubscribe. I still think a |
I was thinking about adding a method to the Cache operator to notify it when it gets evicted and can rely on having no additional subscribers. That way, it could unsubscribe from its source when all of the existing subscribers unsubscribed. As gravy, it could even drop references to events that have been sent to all existing subscribers, but that's less important. |
Unsubscribing once all subscribers are unsubscribed is what the Here is an example timeline using
Using a refCount system that unsubscribes from the origin would result in re-executing the origin which is exactly what this operator is there to prevent. Same problem with dropping references to events that have been sent to all existing subscribers, C wouldn't receive anything in that case. Here is the same timeline with
Both of the use cases you suggest seem like they are exactly what Am I misunderstanding your meaning? |
A The use case is that the creator of the Cache (e.g. Hystrix) would place the Cache Observable into some sort of data structure (e.g. HystrixRequestCache) from which the Cache Observable can gain new subscribers. When the data structure expires or otherwise evicts the Cache Observable, it would use this new method to notify the Cache Observable that it will receive no more subscribers. At that point, the Cache Observable can unsubscribe from its source when its reference count reaches zero. This is only worth doing if subscribers are likely to unsubscribe before the sequence completes or if it is worth discarding early history while existing subscribers remain post-eviction. |
I think the key line you said is this:
Since Hystrix will always complete (onError or onCompleted) it doesn't really matter (nor is there a reliable hook to know when to cause eviction since even HystrixRequestContext is not a required thing). Thus, it seems that an object being dereferenced and then garbage collected is sufficient for the Hystrix case, since the Observable would have terminated and have no resources to clean up. Of note, it was my working on Hystrix to support RxJava that drove me to add How would your proposed changes modify the method signature of |
Pretty much. Also, the code maintaining the data structure would have to control subscriptions to the Cache so that it would be able to know when there are no more subscribers. I had decided this embellishment probably wasn't worth doing, but it was relevant to your question about sequences that never complete. |
I'm going to stick with I also want to better understand how combinations of things like Thanks for the feedback. |
Cache operator as discussed in ReactiveX#209 Similar to `replay()` except that this auto-subscribes to the source sequence. This comes with the same cautions as `toList` when dealing with infinite or very large sequences.
Cache operator as discussed in ReactiveX/RxJava#209 Similar to `replay()` except that this auto-subscribes to the source sequence. This comes with the same cautions as `toList` when dealing with infinite or very large sequences.
Returns an Observable that repeats the original Observable sequence to all subscribers.
The source Observable is subscribed to at most once.
When looking into integrating Hystrix and RxJava, the need for this operator became apparent. It appears to be of general use, so should be considered for RxJava core.