Skip to content
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

useMutation doesn't roll back optimistic cache.evict #7321

Closed
lorensr opened this issue Nov 13, 2020 · 9 comments · Fixed by #8829
Closed

useMutation doesn't roll back optimistic cache.evict #7321

lorensr opened this issue Nov 13, 2020 · 9 comments · Fixed by #8829
Assignees
Labels
🚧 in-triage Issue currently being triaged 🌹 has-reproduction

Comments

@lorensr
Copy link
Contributor

lorensr commented Nov 13, 2020

Successful rollback

When I call this mutation, the following happens:

  1. update is run with optimisticResponse, and an item is removed from the list
  2. error is received from the server
  3. the changes made in update are rolled back: the item is added back to the list
  const [removeReview] = useMutation(REMOVE_REVIEW_MUTATION, {
    update: (cache) => {
      const { reviews } = cache.readQuery({ query: REVIEWS_QUERY })
      cache.writeQuery({
        query: REVIEWS_QUERY,
        data: { reviews: reviews.filter((review) => review.id !== id) },
      })
    },
  })

...

    removeReview({
      variables: { id },
      optimisticResponse: {
        removeReview: true,
      },
    }).catch((e) => {
      if (find(e.graphQLErrors, { message: 'unauthorized' })) {
        alert('👮‍♀️✋ You can only delete your own reviews!')
      }
    })

Failing rollback

When the mutation update uses cache.evict instead of cache.writeQuery, step 3 does not occur.

const [removeReview] = useMutation(REMOVE_REVIEW_MUTATION, {
  update: (cache) => cache.evict({ id: cache.identify(review) })
})

https://github.com/GraphQLGuide/guide/blob/repro-rollback-fail/src/components/Review.js#L132-L141

Versions

System:
OS: macOS 10.15.7
Binaries:
Node: 10.22.0 - ~/.nvm/versions/node/v10.22.0/bin/node
Yarn: 1.22.4 - ~/gh/guide/node_modules/.bin/yarn
npm: 6.14.6 - ~/.nvm/versions/node/v10.22.0/bin/npm
Browsers:
Chrome: 86.0.4240.193
Safari: 14.0
npmPackages:
@apollo/client: 3.1.3 => 3.2.5
apollo: ^2.30.2 => 2.30.2
apollo-cache-persist: 0.1.1 => 0.1.1
apollo-link-rest: 0.8.0-beta.0 => 0.8.0-beta.0
npmGlobalPackages:
apollo: 2.30.2

@benjamn
Copy link
Member

benjamn commented Nov 13, 2020

@lorensr This is an intentional consequence of #5773, though I understand that behavior can be surprising.

However, when that functionality was implemented, cache.evict did not take Cache.EvictOptions as it does now. I'm open to adding an option to control the shallowness/depth of the eviction, now that it's easier to add named options. Would that help?

@lorensr
Copy link
Contributor Author

lorensr commented Nov 14, 2020

Yeah, if there were an option like cache.evict({ shallow: true }) that rolls back in step 3, that would be great ☺️

Not sure how to name it to make it obvious what to use it for. { rollbackable: true }? 😄 Dunno if there are other scenarios you'd want shallow for.

@benjamn
Copy link
Member

benjamn commented Nov 23, 2020

@lorensr What if we reused optimistic here?

// Evict from all layers of the cache (the current behavior)
cache.evict({ id, fields: {...} });
// Evict from the top layer only (can be rolled back)
cache.evict({ id, fields: {...}, optimistic: true });
// Evict from the bottom layer only (ignores optimistic layers)
cache.evict({ id, fields: {...}, optimistic: false });

@lorensr
Copy link
Contributor Author

lorensr commented Nov 23, 2020

Looks good! Using an enum for the 3 options with BOTH as default option might be a little clearer, but less neat. I like boolean. Also wonder what the use cases are for optimistic: false

@3nvi
Copy link

3nvi commented Apr 5, 2021

Any updates on this?

@brainkim brainkim self-assigned this Apr 5, 2021
@brainkim brainkim added 🌹 has-reproduction 🚧 in-triage Issue currently being triaged labels Apr 5, 2021
@gmathieu
Copy link

gmathieu commented Jul 1, 2021

Just ran into this today using v3.3.20. An optimistic flag would be really helpful!

@Risbot
Copy link

Risbot commented Sep 22, 2021

Something news about this optimistic variant?

benjamn added a commit that referenced this issue Sep 23, 2021
After thinking further about the discussion in #7321, I believe the
inability to roll back optimistic evictions is simply a bug that we can
fix, rather than something the developer should have to worry about
(e.g. by passing `optimistic: true`, as I previously proposed in
#7321 (comment)).

Normally, `cache.optimisticData` points to the topmost currently active
optimistic `Layer`, and `cache.data` points to the root/non-optimistic
layer. While running an optimistic `update` function, we temporarily set
`cache.data` to be `=== cache.optimisticData`, in an attempt to confine
the optimistic update to one optimistic layer. Eviction was (until now)
an exception to this confinement, because optimistic `Layer` objects
still have a chain of `layer.parent.parent...` objects that eventually
terminates at the `Root` layer. Before now, eviction was blindly
following that entire chain, ignoring the current value of `cache.data`.

This commit uses `cache.data` as the stopping point for eviction, which
has the effect of confining optimistic evictions to the one layer that
they should be updating, hopefully solving #7321 without requiring any
intervention by the developer.
@benjamn
Copy link
Member

benjamn commented Sep 23, 2021

@lorensr @brainkim @Risbot @gmathieu @3nvi I did some more thinking, and I believe I've found a way to solve the original problem without passing any additional options: #8829

This is good news, because the same update function typically runs for both the optimistic update and the final non-optimistic update, so it would be tricky to handle the hypothetical optimistic: true option in the non-optimistic case, even if it seemed to solve the problem in the optimistic case.

benjamn added a commit that referenced this issue Sep 23, 2021
After thinking further about the discussion in #7321, I believe the
inability to roll back optimistic evictions is simply a bug that we can
fix, rather than something the developer should have to worry about
(e.g. by passing `optimistic: true`, as I previously proposed in
#7321 (comment)).

Normally, `cache.optimisticData` points to the topmost currently active
optimistic `Layer`, and `cache.data` points to the root/non-optimistic
layer. While running an optimistic `update` function, we temporarily set
`cache.data` to be `=== cache.optimisticData`, in an attempt to confine
the optimistic update to one optimistic layer. Eviction was (until now)
an exception to this confinement, because optimistic `Layer` objects
still have a chain of `layer.parent.parent...` objects that eventually
terminates at the `Root` layer. Before now, eviction was blindly
following that entire chain, ignoring the current value of `cache.data`.

This commit uses `cache.data` as the stopping point for eviction, which
has the effect of confining optimistic evictions to the one layer that
they should be updating, hopefully solving #7321 without requiring any
intervention by the developer.
@pei-wang
Copy link

pei-wang commented Nov 3, 2022

Hi @lorensr,
did u test the this with latest Apollo client? I tested on the code you provided (https://github.com/GraphQLGuide/guide/blob/repro-rollback-fail/src/components/Review.js#L132-L141), it breaks the optimistic deletion.

I am using cache.evict({ id: cache.identify(xxxx) }) to implement the deletion functionality. but the optimistic UI is broken after I upgraded to latest version.
I think it is caused by https://github.com/apollographql/apollo-client/pull/8829/files.

if (this instanceof Layer && this !== limit) {
        evicted = this.parent.evict(options, limit) || evicted;
 }

@benjamn could u help to enlighten me how to make the optimistic update(deletion) work?

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 14, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
🚧 in-triage Issue currently being triaged 🌹 has-reproduction
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants