-
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
Infinite Loop (Interlocking) when two useQuery calls to the same nested object w/ different input #10992
Comments
You have cut the warning message short here - the full text of it is
When working with Apollo, you are working with a global cache - and that only really works if you give it data that can be identified uniquely. I get that this might be unintuitive at first, but it's a consequence of using a global cache instead of a cache that works on a per-hook-call-level. If you have any ideas on how to improve the warning, that would be very welcome :) |
Adding more explanation here: The problem here is at it’s core this conflict:
As this object has no unique identifier for The input here is not really necessary to trigger this behavior. Imagine this alternative scenario:
and another query
Now, fire those two queries off at two different times. You will get responses along the line
and
Now, if AC were to blindly merge those object, it would end up with
But do we know this is the case?
and if we were blindly auto-merging, we would get completely wrong data. That’s why Apollo Client is not automatically merging things, but if it cannot identify cache objects, instead replaces them - even if it means that data might be lost (which in this case causes it to be queried again over and over). And in that case, you get that warning, telling you that you need some form of unique identifier for AC to make sure it can safely merge. |
hi @phryneas, thanks for the quick response! I get your point, if the user fully understands that Another argument is, that by toggling fetchPolicy (change to And for our case, our special use case means that we don't have a stable ID field of the nestedObject that would make sense; and in order for our API clients to avoid an infinite loop, mandating them to request that unnecessary ID field is not very elegant. Unless there's a way to always send the ID field to the client without them requesting it? Also, unfortunately, our company uses a shared Apollo client setting that makes configuring a custom merge function difficult.
I guess at least we need to update the warning to mention the possibility of infinite loops ;-) |
At least this one I can help you with easily :) You can use Unfortunately, we have been lacking docs on that feature, but new docs are right now being added over in #10967 |
This is something we should probably highlight more in the docs: that |
This is a very good example, thanks! Now it makes sense why the Apollo client is not auto-merging objects without an identifier. |
Thanks! I will see if I can push through with these options. I will refer to this issue when discussing it with the relevant internal teams.
I haven't gone through the spec yet, but is there a way to mark a field so the client has to request it? If not, is there a plan to add such functionality? As for the warning message, do you mind if I send a simple pull request to add info regarding possible infinite loops, and maybe point to this issue? |
This circles back to the core problem: Apollo Client doesn't know your Schema. Your Schema could be Megabytes big, and you don't want to ship that to your users (or, in some cases might not want to expose it at all). So marking a field in the schema so the client has to request it would not work - because the client would never see that "mark". Likewise, the server cannot just send data that has not been requested by the client - the client would just discard it, as per spec it wouldn't be allowed to use it (and the server would already violate the spec by returning it).
Sure, go ahead! We can go back and forth on the exact wording in the PR :) |
We've run into similar issues when working on large codebases with many teams contributing features and queries on a single page, and echo the recommendation for a schema-aware linter to catch these missing ids. For objects that either don't have an easily identifiable Also, ideally (in a perfect world), Apollo Client would do some cycle counting to detect cache bash loops and short-circuit them by returning an error for some (or all) guilty queries. |
Hi, I've updated the codesandbox (new link https://codesandbox.io/s/festive-carlos-m7v5jd, I did not claim the old one so now my own modifications become a new fork) to show that infinite loop can still present when no input is given or not in nested objects, to reflect that the true cause is AC won't auto-merge objects without identifiers. query QueryRandomBeerName {
randomBeer {
# if we request ID, the cache will be auto merged and no infinite loop
# id
name
}
}
query QueryRandomBeerOrigin {
randomBeer {
# if we request ID, the cache will be auto merged and no infinite loop
# id
origin
}
} I will file a PR to update the warning message later ;-) |
Again, great explanation. Thanks! |
Mentions the possibility of infinite loops apollographql#10992
Mentions the possibility of infinite loops apollographql#10992
Mentions the possibility of infinite loops apollographql#10992
* Update warning message when discarding unmergeable cache Mentions the possibility of infinite loops #10992 * Make the cache lost warning msg more succinct and less scary Co-authored-by: Jerel Miller <[email protected]> --------- Co-authored-by: Jerel Miller <[email protected]> Co-authored-by: Lenz Weber-Tronic <[email protected]>
We encountered the issue @forresty highlighted above with Apollo Client recently although it seems like it'd been latent for a while. Currently, it seems like the workaround (or mandatory behavior perhaps) as noted is to request id (or rather the keyFields?) on every query even if it's not needed. I tried reading various docs and questions, but don't totally understand what's happening there and want to understand better. In our case, we don't seem to get a warning message in the client. It would seem impossible to merge a cache with no primary key, so it feels like the incoming result should just be discarded. Indeed, inspecting the cache after such a query I don't see it updated...but then why do the query hooks trigger each other if they aren't actually updating the cache? |
Issue Description
See
https://codesandbox.io/s/friendly-roentgen-4wk6q2update: see https://codesandbox.io/s/festive-carlos-m7v5jd for infinite loop without input
specifically, when two or more components try to fetch the same nested object but with different inputs, the Apollo client refuses to auto-merge the cache of the nested object, but instead overwrites
nestedObject
repeatedly, causing an infinite loopThe cache key
fieldWithInput
keep alternating based on the variables of twouseQuery
callsTo be fair, when the infinite loop happens, there is the following warning in the console:
However, it's totally unintuitive that two seemingly harmless
useQuery
calls can cause an infinite loop.Link to Reproduction
https://codesandbox.io/s/friendly-roentgen-4wk6q2
update: see https://codesandbox.io/s/festive-carlos-m7v5jd for infinite loop without input
Reproduction Steps
follow the link and see the interlocking infinite loop in action
The text was updated successfully, but these errors were encountered: