5 ms·
I asked Max in private, but i'll ask again here for visibility. How do you handle smart invalidation? Or more specifically, how do I trust that you're handling
by andrewingram 5y ago
I asked Max in private, but i'll ask again here for visibility.
How do you handle smart invalidation? Or more specifically, how do I trust that you're handling smart invalidation _correctly_. Looking at the site, it indicates that calling a mutation like `editUser(id: 5)` would presumably invalidate the User type record with ID 5.
But how do you actually do this reliably? There's nothing in the spec that would indicate the argument ID maps to a record of a certain type with an id field of the same value. Max indicated that you make assumptions based on the return type of the mutation, e.g. editUser has a return type of User, therefore you can infer the relationship. This might be _generally_ true, but it's not 100% reliably true. Additionally, my mutations _never_ just return the naked entity type like this, there's always a wrapping payload type (philosophically, the mutation payload should contain points to all the parts of the graph that _may_ have changed as a result of the operation). Editing a User doesn't _just_ edit a User, the effects on the graph can propagate far and wide. Another point here is that my mutations are rarely just CRUD operations, but more CQRS in nature, they're built to support a specific system capability rather than allowing generic write operations.
The problem of smart invalidation seems to have the exact same shape as smart store invalidation/updates after a mutation in the client. Even after all these years, Relay only does fairly superficial automatic updates, you nearly always have to use custom updaters (or client-side directives in some cases) to get the client-side store back in sync after anything but the most trivial mutations.
- mxstbr 5y agoGreat questions Andy! We do support wrapping payload types of any kind because we invalidate _all_ objects returned from a mutation. For example, if you run a mutation like editUser { user { id posts { id } } } we will invalidate any cached query result that contains that user and any cached query result that contains any of those posts! You are right that smart invalidation can never be 100% reliable, which is why we have the Purging API to manually purge records you know changed from your backend. I think most customers are going to use the manual Purging API, however we also have a bunch of customers with use cases for whom the smart invalidation suffices.
- jorams 5y agoI guess that means a mutation like `editUser { user { name } }` would not trigger smart invalidation, since there is no ID to work with?
- mxstbr 5y agoThat's correct, however we also allow defining custom "Key fields", which is how you can tell us which fields are unique and should be invalidated by (e.g. "User.email").
- nivertech 5y agoWould UUIDs or Relay-style global IDs help to avoid manual purging here?
- mxstbr 5y agoAny kind of unique ID works, whether UUIDs, global IDs, incremented IDs, etc.
- habibur 5y agoRather the API client calls the invalidation methods on the server to invalidate cache. The server doesn't do it all automatically. As far as I have understood.
- timsuchanek 5y agoThat sounds correct! These are ways of purging we support today: 1. Automatic through mutations (if we can detect) 2. A purging api (GraphQL) - you can e.g. `purgeUser(id: 5)` 3. The usual Max age + SWR based invalidation You can check out some more examples here: https://graphcdn.io/docs/cache-purging https://graphcdn.io/docs/cache-purging