apollographql / apollo-cache-persist

🎏 Simple persistence for all Apollo Cache implementations
MIT License
1.39k stars 118 forks source link

Cache-persist not persisting optimisticResponse data #410

Closed jfrancos closed 3 years ago

jfrancos commented 3 years ago

I'm doing some exploring with apollo-cache-persist and apollo-link-queue. My mutations set an optimisticResponse, which, aside from apollo-cache-persist not persisting data when queueLink is closed, does seem to do what I expect:

  1. It updates related variables elsewhere whose state comes from useQuery
  2. They show up in the cache tab of Apollo Client Devtools

I think that (but not 100% on) setting an optimisticResponse doesn't necessarily directly affect the cache even though it updates variables created with useQuery But

  1. Assuming that is correct, I don't completely understand where this data is if it's not in the cache, or what affects whether or not it winds up in the cache, but nonetheless
  2. I assume that data that shows up under the cache tab of Apollo Client Devtools is actually in the cache, and not in this possible other place mentioned in the previous point.

With the cache-persist debug option set to true, I do see a message about persisted data arrive in the appropriate moment, but it mentions storing only 2 characters of data ({}).

Again, the above is what I observe when queueLink is closed. When queueLink is open, cache-persist does work the way I expect (because it's coming from the server response), and gives a message about storing a larger, more appropriate number of characters.

Based on what I'm observing, it seems like either:

  1. cache-persist somehow depends on mutations moving through the whole link chain or
  2. stuff showing up in the cache tab of Apollo Client Devtools actually shows more than just cache.

Neither of which would be expected.

What am I doing wrong here? How can I persist data that winds up in the cache only in virtue of optimisticResponse, rather than coming from a server response?

Thanks so much!

Justin

wtrocki commented 3 years ago

Cache consisting of multiple layers - with optimistic layers that made up projection but are not actually applied. When designed optimistic operations were made typically short lived as they lifespan is usually couple seconds.

Persisting those is going to introduce number of challenges:

See also https://github.com/aerogear/offix/issues/118

Saving optimistic data would require changes in Apollo Client itself (to restore them) later so this is more like feature request

jfrancos commented 3 years ago

Hi Wojtek,

Thanks for that info. It sounds like you're saying there would be difficulty in persisting the data in each layer of the cache, and then restoring the data back to the specific layers that they came from.

Would it be correct to say though, that if I could figure out how to move the data from the short-lived optimistic response layer to the longer-lived cache layer, that cache-persist would then automatically persist and restore that data? I don't need optimistic data to be restored as optimistic data, I'd rather just figure out how to have optimistic data automatically move into the longer-lived layer.

I've tried doing this using useMutation's update field, but this was confusing for me because with

update(cache, {data}) {
    ...
    console.log(cache.extract())
    //optimistic not set, should default to false
    ...
}

showing me the data that I'd just inserted via optimisticResponse, it looked as thought the data I was trying to put in the longer-lived layer of the cache was already in the longer-lived layer of the cache.

Can you give me any hints as far as

  1. How to move the optimisticResponse short-lived data into the longer-lived data (so that cache-persist then does its thing with that data)
  2. How to differentiate between the different layers of cache data so I can get more useful information into my console when trying to figure this out?
wtrocki commented 3 years ago

If we apply changes to cache that is not representing state of server, we are hitting cache invalidation issue (from famous "there are two things hard in software.."

I have feeling that you are looking probably for something like WORA library done by @morrys. WORA applies changes first and then Devs need to reject them if error. Cache persist would be just one component to enable it.

jfrancos commented 3 years ago

Thanks for the feedback. I've been trying to do offline-first with @apollo/client + persistCache, retryLink, queueLink. Based on your comments, I'll see if I get further with offix, and I'll check out wora as well.

wtrocki commented 3 years ago

WORA would be more suitable for the case explained.

wodCZ commented 3 years ago

I've attempted to achieve offline-first support based on Apollo (& link family) about a year ago. I've somewhat achieved that following this article. It explains one approach on how to achieve restorable offline mutation with optimistic response support (and update handlers) at the end of the article.

I'd like to warn you about that approach though - as the app is getting bigger, maintaining everything in sync is a mess and requires a lot of boilerplate. As of today, I'd experiment with Offix, watermelonDB or WORA.

jfrancos commented 3 years ago

Thanks @wodCZ, I was looking at that article as well. I don't really understand what the pros and cons are in functionality in wora vs offix (@wtrocki can you comment on this?) but my thoughts on the three you mention:

  1. Offix - looks like it packages together apollo plus that link family, and does some other cool stuff. Looks nice in terms of number of contributors and stars
  2. Wora - Again, not sure what I'm getting/losing with this vs Offix. There's only one contributor and hasn't been updated since May so I'm hesitant
  3. WatemelonDB - Not graphql-centric like the other options -- would this be in addition to vanilla apollo?

As per wtrocki, offix may not be the right tool for me, but instinct says to at least try it out and see if I get further than with apollo + link family

Edit: I realized Wora has in fact been updated recently, just not in master.

wodCZ commented 3 years ago

I've ended up with some kind of Model - View - Controller architecture. I maintain a store (https://github.com/s-KaiNet/react-simple-hook-store) which the View (react) uses to render data and update the local copy using "actions". Point being, thorough the app I only work with the store, which is reliable and only source of truth. I don't use Apollo in my components. Then, the model (store) knows how to sync the local state with server - this is where queries and mutations are called. Finally the "controller" (plain JS classes) manages the model - restoring the state on app startup, triggering sync when the internet connection is restored etc.

As I'm writing this I realise how this sounds like overkill. I've came to conclusion that apps with read-only support are just fine with simple react + apollo + cache-persist approach, but at the moment you need read-and-write offline functionality, any attempts to abuse apollo cache for such support is not worth the hassle. I was a bit relieved when I saw wtrocki confirm my thoughts saying this:

If the app needs to be fully offline using the existing Apollo API to deliver offline is challenging as it wasn't designed to cover this case

Having the model separated from the view seems like clear way to work with fully offline apps. At least, I feel more confident about the app ever since I've rewritten the app 3 months ago.

In any case, I can't call myself an expert 😁 also, at the time, I didn't find Offix nor Wora - maybe they'd satisfy my needs.

jspizziri commented 3 years ago

I’m going to close this issue given the wealth of detail provided in the comments. I’d also like to add for such use-cases something like react-query might be a solution to consider (if you’re not tied to the Apollo client and are using react).