-
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
Warn when clobbering non-normalized data in the cache. #6372
Conversation
> This is a revival of PR #5833, which has accumulated too many merge conflicts over the last few months to be worth rebasing. One consequence of #5603 is that replacing non-normalized data in the cache can result in loss of useful data, which is preferable to mistakenly merging unrelated objects, but not ideal. In almost every case, the right solution is to make sure the data can be normalized, or (if that isn't possible) to define a custom `merge` function for the field in question, within the parent type policy. It turns out we can give a very detailed warning about such situations in development, and that's what this commit does. For example: Cache data may be lost when replacing the d field of a Query object. To address this problem (which is not a bug in Apollo Client), either ensure all objects of type D have IDs, or define a custom merge function for the Query.d field, so InMemoryCache can safely merge these objects: existing: {"__typename":"D","e":4} incoming: {"__typename":"D","h":{"__typename":"H","i":7}} For more information about these options, please refer to the documentation: * Ensuring entity objects have IDs: https://go.apollo.dev/c/generating-unique-identifiers * Defining custom merge functions: https://go.apollo.dev/c/merging-non-normalized-objects Looking at the output for our test suite, every warning seems legitimate and worth fixing. I will resolve the warnings in subsequent commits.
src/cache/inmemory/entityStore.ts
Outdated
if (process.env.NODE_ENV !== "production") { | ||
warnAboutDataLoss(existingObject, incomingObject, property, getFieldValue); | ||
} |
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.
Since this call is restricted to development, production minifiers should be able to prune virtually all of the code introduced by this PR.
src/cache/inmemory/entityStore.ts
Outdated
const fieldName = fieldNameFromStoreName(storeFieldName); | ||
const typeDotName = `${parentType}.${fieldName}`; | ||
|
||
if (warnings.has(typeDotName)) return; | ||
warnings.add(typeDotName); |
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.
Since this is a fairly verbose warning, I wanted to make sure it's displayed at most once per Type.field
.
Since cache.modify calls cache.merge, and we don't want cache.modify ever to trigger "Cache data may be lost..." warnings, it's more convenient if the StoreWriter is responsible for calling warnAboutDataLoss, rather than always performing that check in cache.merge.
This should have been part of #6372 (just merged).
Ever since the big refactoring in #6221 made the client more aggressive about triggering network requests in response to incomplete cache data (when using a cache-first FetchPolicy), folks testing the betas/RCs have reported that feuding queries can end up triggering an endless loop of unhelpful network requests. This change does not solve the more general problem of queries competing with each other to update the same data in incompatible ways (see #6372 for mitigation strategies), but I believe this commit will effectively put a stop to most cases of endless network requests. See my lengthy implementation comment for more details, since this is a non-obvious solution to a very tricky problem. Fixes #6307. Fixes #6370. Fixes #6434. Fixes #6444.
…6448) Ever since the big refactoring in #6221 made the client more aggressive about triggering network requests in response to incomplete cache data (when using a cache-first FetchPolicy), folks testing the betas/RCs have reported that feuding queries can end up triggering an endless loop of unhelpful network requests. This change does not solve the more general problem of queries competing with each other to update the same data in incompatible ways (see #6372 for mitigation strategies), but I believe this commit will effectively put a stop to most cases of endless network requests. See my lengthy implementation comment for more details, since this is a non-obvious solution to a very tricky problem. Fixes #6307. Fixes #6370. Fixes #6434. Fixes #6444.
// If we're replacing every key of the existing object, then the | ||
// existing data would be overwritten even if the objects were | ||
// normalized, so warning would not be helpful here. | ||
if (Object.keys(existing).every( |
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.
Does this check make sense when existing is an Array? It seems (though it's very likely I'm missing something) that this implies that this warning is skipped whenever incoming is at least as long as existing, which seems wrong because as said below arrays always need a custom merge function?
@benjamn Is there a way to disable this warning for all array types? My API will always return merged results and I don't need a warning every time I add an item to an array. typePolicies is not usable because we're talking dozens of types and hundreds of fields. |
I'm running into a similar issue. I have an
It appears everything is properly normalized. Existing and incoming are both correct. It seems like no warning is needed in this case. |
@KenneyE You're right that no data about the However, since you know that this is fine/expected, you can silence the warning by providing a field policy like this: new InMemoryCache({
typePolicies: {
User: {
fields: {
registeredClasses: {
merge: false,
},
},
},
},
}) The |
@jefbarn There's not a good way to bulk-disable the warnings right now (except to use In your case, if the behavior is triggered by the type of the field being an array, you'd need to be able to determine the schema type from the |
Thanks @benjamn! I was hoping to avoid having to add that for every array field, but it's a fair trade off. Appreciate the response. |
This warning was introduced in #6372.
This warning was introduced in #6372.
This warning was introduced in #6372.
One consequence of #5603 is that replacing non-normalized data in the cache can result in loss of useful data, which is preferable to mistakenly merging unrelated objects, but not ideal.
In almost every case, the right solution is to make sure the data can be normalized, or (if that isn't possible) to define a custom
merge
function for the field in question, within the parent type policy.It turns out we can give a very detailed warning about such situations in development, and that's what this commit does. For example:
Looking at the output for our test suite, every warning seems legitimate and worth fixing. I will resolve the warnings in subsequent commits.