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

implement deduplication options #11

Closed
wants to merge 3 commits into from

Conversation

jonas-app
Copy link

@jonas-app jonas-app commented Jul 11, 2018

Hey @helfer,

thanks for your awesome and simple queue link.
In our project we have the need to deduplicate operations.
Otherwise multiple operations of the same save mutation get batched into one call.

The implementation has no breaking changes, as it defaults to the same behavior as before.
If you would prefer to keep your queue link simple I'm happy to release this as a separat package.

There are two open questions:

Would you recommend another approach to cancel operations?
I currently just complete the observable.
Other options would be to return an error or just break the chain.
Or should there be an option to share the result like apollo-link-dedup does?

Is there a nicer way to test multiple operations?
I found no merge/concat methods and my executeMultiple functions seems a bit hacky.

@codecov-io
Copy link

codecov-io commented Jul 11, 2018

Codecov Report

Merging #11 into master will decrease coverage by 1.96%.
The diff coverage is 96.29%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #11      +/-   ##
==========================================
- Coverage     100%   98.03%   -1.97%     
==========================================
  Files           1        1              
  Lines          25       51      +26     
  Branches        3       11       +8     
==========================================
+ Hits           25       50      +25     
- Misses          0        1       +1
Impacted Files Coverage Δ
src/QueueLink.ts 98.03% <96.29%> (-1.97%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f0d77e4...1985858. Read the comment docs.

@helfer
Copy link
Owner

helfer commented Jul 11, 2018

Hey @jonas-arkulpa, this sounds like something that apollo-link-dedup should be able to do out of the box, so all you have to do is combine this link with the dedup link. You can put the dedup link in front of the queue link from your app's perspective (app -> dedup -> queue -> network).

The nice thing about links is that you can combine a number of simple links together to get more complex behavior. So instead of adding functionality to one link and making it more and more complicated, it's usually possible to just add another simple link to your chain.

Let me know if apollo-link-dedup doesn't work for you. I'd be curious to know why.

@jonas-app
Copy link
Author

jonas-app commented Jul 13, 2018

Hey @helfer,
thanks for your fast reply.
To use apollo-link-dedup or a separate link was also my first intention.
But this has some issues and had't worked out as expected.

If a duplicate in flight operation exists, dedup shares the observable of the exiting one with the new operation (like »keepPolicy: "first"«). Our main use case is to only send the newest one and cancel the older one or share the result of the new operation with the old one (like »keepPolicy: "last"«).

The second issue is how dedup defines a duplicate. It uses toKey by default and isn't customizable. This means that for a duplicate all the variables have to be equal, which for us is not the case.
For example we first save "Hello" and afterwards "Hello world" with the same mutation and want to deduplicate the operations.
Otherwise the two operations get batched and it depends on luck which state gets saved last on the server. (+ unnecessary traffic)

As an alternative I imagined a link that works like apollo-link-batch-http and only deduplicate batches. This would sit between the queue and the http link. Or a customized version apollo-link-dedup. In both cases I would have to keep track of all in flight operations in the new link again.
As the deduplication functionality is tightly coupled with the queue/offline functionality I decided to go this way.

Let me know if you prefer one of the alternative solutions or if you have another solution which would solve my issues.

* Defaults to comparing operation operationA.toKey() === operationB.toKey()
* https://www.apollographql.com/docs/link/overview.html
*/
isDuplicate: (a, b) => (a.operationName === 'save' &&
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a consumer I would find it useful to specify the keepPolicy per-query using a context option.

Also useful would be specifying a dedupeKey, defaulting to ${operationName}:${variables.id}. dedupeKey would be used to calculate which operations keepPolicy should be applied to, sort of like a named queue. I hope that makes sense!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it is a good idea to default to ${operationName}:${variables.id} as this wouldn't fit most usecases.

  • There isn't always an variable id.
  • Maybe it's called differently.
  • We for example use input types which would break this default es well.

The default should always work and if you have a more specific need you can overwrite the default.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, by default the dedupeKey should be undefined and operations shouldn't be deduped in the queue. What I meant to say is the recommended dedupeKey would be ${operationName}:${variables.id}.

The use case I have in mind is editing multiple text documents and saving the edits while offline. I want to only save the final revision, which fits with keepPolicy: 'last'. However I want to keep the last operation for all different saved documents, so you would calculate which to keep with a dedupeKey: 'saveDocument:${id}'. Then when the queue opens it has exactly one saveDocument operation per unique document saved.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Yeah I'm doing exactly the same thing in our project. I'm just arguing that this shouldn't be the default.

@pl12133
Copy link

pl12133 commented Aug 23, 2018

My use case sounds similar to what you have done here so thank you for this PR @jonas-arkulpa even if it doesn't end up fitting in to this package.

As the deduplication functionality is tightly coupled with the queue/offline functionality I decided to go this way.

I have also found that to be the case. I was not able to get apollo-link-dedup working out-of-the-box with my own custom queue link, because with keepPolicy: last the queue needs to have all previous operations cleared out of it.

@jonas-app
Copy link
Author

Thanks for the feedback @pl12133.
The per-query config makes a lot of sense.
In which direction would you go if we extract this functionality into another package?
Which of the mentioned alternatives sounds best for you, or do you have a better one?

@jonas-app
Copy link
Author

If needed this should be realised as a separat link.

@jonas-app jonas-app closed this Dec 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants