-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Added refetch in case cached data has gone stale. #1461
Added refetch in case cached data has gone stale. #1461
Conversation
We intentionally didn't include automatic refetching on stale data because it's likely to cause an infinite loop, and often the right solution is to change your app code. Stale data often comes up when you have two queries on the same page asking for conflicting data, and if they are querying rapidly changing lists you will sometimes end up with a ping-pong list refresh. I could be in favor if this was an opt-out option, or perhaps it had some infinite loop detection like only refreshing 2 times before throwing an error or something. |
@stubailo indeed I have seen this infinite loop possibility. To provide you with some context, I've recently publish apollo-cache-invalidation, a library to facilitate cache invalidation of fields. Thing is, the only way I found to make that idea work was purging specific data from the DataProxy directly. This eventually ends up with current ObservableQueries hitting stale data. |
Yeah I looked at that, it looks really cool actually! So do you think there is a way to limit refreshing to only once somehow, and still keep the invalidation working? |
There are easy and hard ways, I guess. Best way would probably be to allow for the imperative store API to actually hold information on recently invalidated fields, and erase that information once refetches are dispatched. But maybe I could write up an opt-in option. Something like |
Ah, so basically, when you invalidate you set a flag, something like "justInvalidated" and then when the query refetches, it sets that back to false. Hmm, what would the |
@lucasconstantino I think infinite loops are pretty unlikely here, unless the server state keeps changing very rapidly. I would be okay with having something like this be an optional argument to the query, maybe called |
b785b68
to
c0ea3df
Compare
Ok, here is a new code proposal: I've added an option called shouldRefetchIfStale, which is a callback to be called when stale data is encountered. The callback defaults to a returning false callback, which should keep things as they are and introduce no breaking change, making it opt-in. The callback will be provided with some context that should make it easy for the user to decide if the stale data should be refetched or not. This opens the possibility for more grained control over stale data refetching, while removing the need to inform each and every query that it should behave this way, thus facilitating adoption. For one to decide to simply turn-on the functionality on all queries, it must simply provide a callback returning always Not that elegant, but that's what I've reached so far. Any thoughts? P.s.: @helfer infinite loops, as you said, are really unlikely to happen. Funny thing is that Edit: I could make shouldRefetchIfStale option receive both a callback or a boolean... I guess that would be fine, but I'm not always comfortable with an argument receiving multiple types. |
@lucasconstantino I think having it as a function is a good first step. Just to make sure we're 100% on the same page, how would you use said function to avoid a loop? I.e. can you provide a complete example here? I'm also curious to know how that test triggers an infinite loop. Maybe there's something I haven't considered. |
@helfer just to point it out, I've only today read issue #821, and thus I now understand the problem I'm dealing with here was thoroughly discussed previously, and for a long time. Automatically refetching stale data was something even proposed as a possible improvement, so I see this is no new matter for you. I've pictured only one situation where infinite loops might happen when refetching stale data: when more than one ObservableQuery are listening to a query with a given field returning a given type which resolves the resource id to a new value every time it is called, and with different fields returned. Both would return different id's, meaning the second one's result would invalidate the first one's data, making the first perform a refetch, which would eventually invalidate the second's data, starting the loop again. This is only an assumption, though, and I haven't tested if this is really an occurring scenario. The mentioned test is doing exactly that, and I've tried running all tests with the refetch turn on by default; that test is the only one that fails, even though I did not dig further to see if an infinite loop was what cause the failure.
const refetches = {};
const shouldRefetchIfStale = ({ queryId }) => {
const now = new Date()
// Find's out if any refetch for the current queryId happened less than 10 seconds ago.
if (refetches[queryId] && refetches[queryId].some(time => time > now - 10000)) {
return false
}
refetches[queryId] = refetches[queryId] || []
refetches[queryId].push(now)
return true
} In my situation, using apollo-cache-invalidation, I would probably add a helper to only enable refetching of recently invalidated fields, if someone has the need. |
Any more thoughts one this matter? I can work on another approach if you guys find this one not suitable. |
Hey @lucasconstantino, sorry I didn't have time to review this PR! Can you add a test case or post an example of how to use it in this thread? I think it's a useful functionality to add, but I want to make sure that it's really usable for people and that we have reasonable defaults. ❤️ |
@lucasconstantino any movement on this? I would really like to have a way to invalidate parts of the apollo store and your solution seems like a reasonable way to do it :) |
Sorry folks, had no time lately for this. This is indeed in my priority list, so I think I'll have looked into it by the end of the week. |
b727154
to
1a87991
Compare
@lucasconstantino how can I help out on this? This functionality is a great one for the new effort around Apollos network layer. cc @evanshauser for a use-case for |
@jbaxleyiii I'm really trying to put my hands on code on this one. Last week I rebased this against the current master, but I'm having some unrelated tests failure (even on the master branch). Have you looked at the code? That would be my first code contribution to the project, and even though @helfer said to go forward with it I would like some feedback on if you guys think this is the best approach - and if you see any TypeScript issues with the added code, for I'm not really used to typed code. |
95e4100
to
e19ddc3
Compare
f90315b
to
5905786
Compare
5905786
to
1319206
Compare
@jbaxleyiii @helfer I've just added some tests. Would anyone be able to do a code review on that? |
@lucasconstantino I can! I'll work to wrap it into the 1.9 release as we are getting ready to ship 1.8 |
@lucasconstantino this change looks great! I'm going to sit on it for just a little bit longer as I believe we are really close to shipping Apollo-link. If we are able to hit that target next week, I will move this code into a link as it is such a perfect use case for it! It is 💯 a need, either way in a short time it will be possible to evict some cache thanks to your help! |
Generated by 🚫 dangerJS |
hmm, danger seems to be broken on test detection here. I'll take a look |
for some reason it pulled from an older version of danger? It should actually be okay 👍 |
This issue has been automatically marked as stale becuase it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions to Apollo Client! |
@lucasconstantino I do think that is should end up being moved to a link! I will chat with @evanshauser and we will get it all moved over! Thank you for the hard work! Sorry we weren't able to merge it in as initially expected. |
@jbaxleyiii on which project do you think this functionality will be moved to? I could have a look there and maybe port the code on the free time ;) |
Did this get implemented? |
At the moment im using a custom query function to alter the fetchPolicy based on a TTL in order to force network-only request if it has expired. https://gist.github.com/riccoski/224bfaa911fb8854bb19e0a609748e34 Unfortunately you have to manually create you CacheRef IDs as it's a lot of work trying to mimic how the client does it |
This is a proposal and has tests failing
Discussing about cache invalidation I've rised a questioning on why there happens no refetch on ObservableQueries once data has gone stale.
This pull-requests presents with a small update to trigger a refetch when missing fields from cache are found. I will still provide stale data, but will provide new data once it is fetched.
There is one test failing, but I didn't want to go on with this without bringing the proposal forward to the Apollo team.