-
Notifications
You must be signed in to change notification settings - Fork 734
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
Do not cancel server fetch of watcher on dependent key update #1156
Do not cancel server fetch of watcher on dependent key update #1156
Conversation
@flobories: Thank you for submitting a pull request! Before we can merge it, you'll need to sign the Apollo Contributor License Agreement here: https://contribute.apollographql.com/ |
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.
Woof. Thanks for the detailed report and the PR. One minor comment.
hmm, not sure if this is correct solution. Wouldn't it be better to change signature to |
also this introduces bit uncertainty in |
thinking about both cases above I think better way is to figure out solution where canceling fetch won't call |
@RolandasRazma I don't know how we'd infer the correct fetch policy when cancelling from the cache though. What if it's Note that this also just calls Does that make more sense? |
the problem is that will have one success and one cancelation callback, and that might lead to very confusing results unless you know whole system |
Wait, I don't think I understand why that would happen. Can you explain: a) Why this would return one and one? |
a) Fetch B is canceled and return error as "canceled" (ref to b) |
OK, thank you very much for clarifying. @flobories What's your take on @RolandasRazma's concerns? |
I see you concern @RolandasRazma about having two different fetches in parallel, only one being cancelable because only one The main concern I'm trying to address is a server fetch being canceled by a cache update, when I'd prefer the server fetch to reach completion uninterrupted as I did not explicitly cancel it.
Does this make sense? |
your last scenario is what I was describing, where canceling from cache hides server fetch cancelation and "user of the library" never even knows that his request was canceled and restarted. Implementation as it stands in this PR will lead to bugs where: this requires weird internet condition, but this kind of race conditions impossible to find when you need to fix |
AHA, I think I found a big piece of my confusion, which is partly because I'm noodling with some stuff with a delegate-based URL session: The callback-based APIs will always puke up an error if something is cancelled, but the way I've got it set up in my delegate-based one I'm killing off completion blocks before the cancellation error hits the delegate. I feel like maybe we should be performing the fetch directly on the What do you guys think about that? |
(FYI, I'm converting this to a draft so I don't brain-dead merge it tomorrow morning before we finish this discussion) |
@RolandasRazma @flobories Thoughts? |
sorry I'm not that familiar with codebase to give credible opinion |
I like your solution @designatednerd! Store fetches and main fetches/refetches would be siloed! Two comments/questions:
Your solution would definitely solve my issue. |
@RolandasRazma You know this part of the codebase at least as well as I do, i've been focused on other stuff 🙃 You can't actually cancel a store load, so there's nothing really to track. If the entire watcher is cancelled while the store load is happening, then the completion callback should be nilled out in the cancellation and then we wouldn't call it when the store load finishes. I think you'd only have the cache key create a new fetch if you get that dependent keys changed notification after something got cleared out - then the cache would have nothing and it'd force a re-fetch. Like I've said repeatedly, this is extremely complex, to the point that I've been working on this lib for 10 months and still really haven't totally wrapped my head around every possibility here. @flobories the fact that you were able to even identify this problem indicates you're probably in an ok spot to take a stab at it with feedback from me and @RolandasRazma. So yes, POC is welcome! |
Will do, thanks for the feedback @designatednerd ! |
(actually tied up with other projects at work, will follow up tomorrow 🙃) |
@flobories Were you ever able to get another look at this? |
I have not 😬 but it’s been on my mind crushing me with guilt 🤣 |
Dude, no guilt! Just checking in to see if you were still interested in moving this forward. It's a gnarly problem to solve, so I very much appreciate that you are interested in solving it rather than me 😆 |
…_when_server_fetch
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.
@designatednerd, thanks for your patience! Here is my second attempt.
I believe it should not change the behavior much, only the case that I am having issues with.
As you suggested, this replaces the the main .returnCacheDataElseFetch
reload with a two step "manual" store load - which does not interrupt any running fetching
- followed by a .fetchIgnoringCacheData
if the store load fails - which it should if data is missing.
The only change in behavior I am wondering about is threading, since we are introducing one extra level of asynchronicity here in the double load + fetch, but I believe it should all stem from the same store
queue that handles store transactions / publishing key changes, so I believe it is correct?
What do you think?
I tested this branch in my app and it resolves my issue!
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.
Sorry I let this sit for a bit! Just a couple comments, otherwise I think this is looking good
Thanks for the review @designatednerd! Just pushed the updates you requested. |
Looks good on my end - @RolandasRazma since you caught the issue with the original, wanna double check that I'm not missing something here? |
} | ||
case .failure: | ||
// If the cache fetch is not successful, for instance if the data is missing, refresh from the server. | ||
self.fetch(cachePolicy: .fetchIgnoringCacheData) |
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.
is .fetchIgnoringCacheData
correct here? it was .returnCacheDataElseFetch
before.
Can we get into some fetch cycle here? fetch(cachePolicy: CachePolicy)
-> Fail -> fetch(cachePolicy: CachePolicy)
-> Fail -> ...
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.
Before it was returnCacheDataElseFetch
because we weren't explicitly trying to load from the cache. By the point this gets hit, we've already checked the cache so fetchIgnoringCacheData
acknowledges that.
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.
Also the fail
is for reading directly from the store, rather than for hitting the network, so that cycle should not be an issue.
OK I'm gonna go ahead and merge this - @flobories @RolandasRazma Thanks so much for all your help! |
Thanks @designatednerd for your help and patience! Feels great to reach the finish line here! |
See #1156 (comment) for details
This is an attempt at solving #1155
By only canceling fetches when they are replaced by a server fetch, we are making sure that cache updates do not cancel any inflight fetches.