-
Notifications
You must be signed in to change notification settings - Fork 141
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
Caching stuff #208
base: master
Are you sure you want to change the base?
Caching stuff #208
Conversation
* @param {*} options.cacheKey | ||
* @returns {getter} | ||
*/ | ||
function addGetterOptions(getter, options) { |
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.
This API change to introduce the concept of "getter options" is a little awkward imo.
Currently, it will replace any previously set options which could lead to unexpected behavior given its name.
An alternative would be to call this setGetterOptions
to indicate it would replace, not patch, the options.
But I feel like the API is being conflated with implementation details of hidden __options
object. The alternative that I would suggest is two separate methods to configure these options.
cc @jordangarcia since this is API design considerations
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.
@jordangarcia Another thing to consider is that it's probably natural from an API standpoint that getters are configured where they are defined. It may be time to introduce a formal Getter()
constructor... :)
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.
I would like to see a Getter
constructor.
// same as `['items']`
var getter = Getter(['items']);
// or
var getter = Getter(['items'], {
useCache: false
});
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.
@jordangarcia Is there an advantage to using a constructor outside of style preference?
Rather than exposing crude controls to internal cache and caching behavior, it seems to me a better design would be to introduce a caching interface abstraction to allow the user to customize at will. This would avoid adding complexity to nuclear-js to permit additional caching algorithms (ie |
* Clear all cached values | ||
*/ | ||
clearCacheValues() { | ||
this.reactorState = this.reactorState.set('cache', Immutable.Map()) |
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.
what is the use case of this api method?
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.
added as an afterthought, thought it might be useful in some cases of switching views in a spa. will remove
@loganlinn A cache abstraction would be nice. Did you have something in mind? |
Before I make any changes, I want to make sure I point out the driving motivator of this change, which is that caching as it is currently implemented is broken. There is no cache cleanup, and as such memory will grow linearly over time. This is a real problem for single page apps and data intensive pages. IMO we need to fix or mitigate this out of the box for users. I'd like to address this in one of two ways:
In either case, adding a cache abstraction would be a logical next iteration to help power users fine tune their apps, but @loganlinn I think we need to address the base case first. If you have better ideas, or a good handle on what a caching interface would look like, please let me know. |
@scjackson I agree that the lack of cache invalidation / cleanup is a core problem that needs to be addressed. I believe any viable candidate that ships with NuclearJS must (1) prevent the infinitely linear growth of cached data over time and (2) maintain the caching performance benefits that already exist in NuclearJS. Here is what I propose, when a Getter is unobserved by invoking the returned unwatchFn, if no other observers are using that getter then we remove any cache entries for it. This provides a more natural cache GC when a Getter is no longer "hot". Thoughts? |
@jordangarcia I like that. Would you be ok with disabling caching for unobserved getters (ie things that are just |
Hm, good point. I am a little hesitant to change a lot of the out of the box caching behavior as it may have performance impacts on people using it. Imagine a computationally expensive I think having the option on a Getter to The more I think about it, the more I am leaning towards a hybrid approach, where we use intelligent unobserve logic to do cache cleanup and have a pretty high maxItems limit that serves as a cache ceiling for things like |
@jordangarcia So this would include:
|
So if we change the default caching behavior of The only use case I can think of is if you dont want the getter ever getting into cache during It seems like a getter has 3 caching options, 'always', 'never', and 'default'. I sort of dislike having two flags to represent 3 options. What are your thoughts on Getter(['items'], {
cache: 'always',
})
Getter(['items'], {
cache: 'default',
})
Getter(['items'], {
cache: 'never',
}) cc: @loganlinn |
@jordangarcia I thought you were advocating for maintaining the cache by default behavior of |
ah yeah I was unclear on that. If we implement an upper limit as well as On Tue, Jan 26, 2016 at 11:32 AM, Sam Jackson [email protected]
|
Updated with the following:
|
one other relevant change, I'm now clearing cache values on |
@@ -9,6 +11,19 @@ import { isKeyPath } from './key-path' | |||
*/ | |||
const identity = (x) => x | |||
|
|||
class GetterClass { |
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.
I think this would be better as a pseudo-constructor. IE I dont like the idea of changing the Getter type because now we have to handle both a Getter
instance and a plain array getter in every interface.
Instead Getter(['items'])
returning simply ['items']
makes a lot of sense to me for backwards compat and simplicity.
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.
so basically what i had before?
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.
yes, but called Getter
instead of addOptionsToGetter
. The latter name implied that you could mutate an existing Getter (potentially globally) versus a function to create a Getter.
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.
Ok -- addOptionsToGetter
was by ref. Which would modify it globally. We could return a cloned copy with options added instead. Thoughts?
return observerState.withMutations(map => { | ||
entriesToRemove.forEach(entry => removeObserverByEntry(map, entry)) | ||
// Update both observer and reactor state | ||
observerState = observerState.withMutations(oState => { |
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.
not a fan of these function params, can we not do observerState
because of no-shadow
?
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.
Could you clarify? Not sure what you mean by this?
Removed the |
@jordangarcia @mikeng13
Added a number of options/functionality around caching including:
getCacheValues
andclearCacheValues
toReactor
. This will allow users to monitor their own cache usage and clear it if need be.useCache
andmaxItemsToCache
as config options forReactor
. This will allow users to disable caching completely, or limit the number of items that are stored in cache. Items are evicted from cache on a standard LRU policy.addOptionsToGetter
, which accepts a getter and augments it with options. Currently supported options areuseCache
, which allows users to specify whether the getter value should be cached on evaluate, andcacheKey
, which allows users to specify their own cache key.