Closed kitten closed 4 years ago
Apart from a configuration on what happens when an Optimistic Mutation is run while offline / fails because the user is offline, we haven't decided yet what the natural behaviour should be. Optimally we shouldn't even have this configuration, but the behaviour can be one of the following:
OperationResult
(filtering offline errors basically) is issued until the mutation is actually sent successfullyOperationResult
even if it contains a NetworkError
or the user was offline (in this case the optimistic update wouldn't be cleared)Then there's also the problem of Optimistic Mutations themselves: if a mutation contains some fields that are and some fields that aren't optimistic, then what should happen?
Are both first options valid? Should both be possible somehow? What happens in other apps when they're offline and how do we prevent inconsistent states?
@kitten, I created the offline-first library which manages the offline workflow and persistence.
I used this library to extend Apollo and Relay with @wora/apollo-offline and react-relay-offline to manage queries and mutations offline.
Let me know if you are interested in integrating it to create the offlineExchange.
I would like to support @morrys as an alternative to the described approach. The challenge of building offline enabled application is that many approaches we can take here might be considered just one of the possible implementations that will work for some use cases but prevent others from being implemented.
The most flexible offline implementation base on the concept of executors.
For Wora that is: https://github.com/morrys/wora/tree/master/packages/offline-first that is being used under the hood of Apollo and Relay implementations.
A similar concept can be found in other libraries like AppSync or Offix library https://offix.dev/docs/offix-scheduler-introduction
Scheduler/Offline-First base on the idea that the client itself should not actually care about the offline-online state - it only needs to cache the server state and never offline state. Offline state can be kept in separate stores that form the blocking queue. When having this separate offline state client can quickly end up with problems of the typical distributed storage system (Typical stuff that Git resolves like conflicts/merge or rebase of the data etc.)
This will have numerous benefits:
Schedulers can enqueue offline operations based on certain criteria (as being offline). This means that URQL will only need to expose methods to operate on OptimisticMutations only. Example how it is possible to do that today in Apollo 2.0: https://github.com/morrys/wora/blob/master/packages/apollo-offline/src/ApolloClientOffline.ts#L137-L146
We can then later connect wora cache-persist as storage and get fully-featured offline support that will be flexible and satisfy various the community without poluting robust implementation of the URQL
The challenge of checking network error/state is that in some cases of unreliable networking we might end up with duplicates, there is need to have single blocking queue or at least some blocking configuration like in wora offline-first
to not cause strange situation for conflicts and data overrides.
We are currently checking whether we can reuse some storage adaptor code you already have here.
That being said this is kind of besides the point of this RFC (luckily) 😅 We already have a solid caching and persistence system. The persistence is already able to persist server-accurate state accordingly. Optimistic updates to the cache are separate and always have been. They’re quickly and easily invalidated automatically at the first server result (as they should be). To this point of caching, we don’t need a layer for scheduling;
so to sum this up, here’s a list of what we already have (although some of this isn’t documented as we’re waiting for additional features from this RFC)
So don’t worry about the basic persistence 😅 that’s off-topic here and basically done. We’re more interested in the hard part here of what we do in the face of network-errors.
Mutations can be partially optimistic, so part of the problem is that we’re defining what the user is likely to want to happen in the case of mutations failing, due to the client being offline. Apart from that our problems are mostly solved.
We’re mainly currently discussing what happens to the operation’s result when an optimistic mutation fails. One option is to deliver the optimistic result to the framework bindings immediately and rely on the user to only use optimistic results where it’s sensible.
The other option is to defer the result indefinitely for mutations during offline, or to even deliver the errored result itself. Which are not the favoured options right now 🙃
tl;dr: this is not about persistence of cache data and/or conflict resolution, but simply around the implementation of a full offline exchange 😇
but simply around the implementation of a full offline exchange 😇
Yes. That is why I suggested talking look on wora/offline-first as alternative I'm not sure about Wora but my initial offline implementation was using exchange and it had the same problem. We had been blocking requests but that prevented people from writing proper UI. Then we moved to return offline type of error from the exchange and trigger mutation again to get an optimistic response. This is very very hacky and not optimal - causes flickering etc.
We’re mainly currently discussing what happens to the operation’s result when an optimistic mutation fails. One option is to deliver the optimistic result to the framework bindings immediately and rely on the user to only use optimistic results where it’s sensible.
Forgive me this silly question. Not sure about internals here but when I tried that in Apollo it was adding optimistic results to the cache as result from server. Would users will be able to tell that they got an optimistic response? How this will be invalidated after page refresh?
I forgot to leave a follow-up comment here, but v3
is now shipping Offline Support (experimental for now) with all the strategies we've mentioned across comments and PRs. https://formidable.com/open-source/urql/docs/graphcache/offline/
Awesome!
Summary
The
offlineExchange
will build on Graphcache's persistence support (see #674) to bring full offline capabilities to the library.It will be a new exchange that wraps around the
cacheExchange
and looks identical in its API with extended functionality.This extended functionality supplements the persistence support by being able to queue operations when the user went offline, preventing errors from surfacing in the UI when going back online, and also reexecuting operations as necessary.
It shouldn't duplicate existing functionality, like the
retryExchange
, but may build on it.Requirements
networkError
s and return their results if the user is online and they're not exclusively optimistic mutationsretryExchange
being added in parallelfetchExchange
in the first place when offline by adding a grace periodThe storage adapter in Graphcache will need to be extended; this is our first draft: