-
Notifications
You must be signed in to change notification settings - Fork 529
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
Fix __typename prevent subscription deduplication #652
Fix __typename prevent subscription deduplication #652
Conversation
The anonymous function ends being "different" when run on separate processes, preventing identical subscription queries that use __typename from being deduplicated (when run from separate processes)
Hey @darrenclark thanks for taking a look at this! In Absinthe 1.5 what I want to do is focus on letting people generate their own doc ids. The current system has a couple of flaws, one of which you've found. My thought is that we'd probably eliminate "by default" deduplication. If you want to have things deduplicated then you need to provide some kind of identifier. Identifiers really have two parts:
A PR that works on these two things would be incredible. |
@benwilson512 Perfect, we can definitely look into this. It should open up some significant optimization opportunities for us. Any thoughts on the interface into this? Something similar to the document adapter interface (https://hexdocs.pm/absinthe/adapters.html)? With that in mind, should I close this PR? Given the plans for 1.5, I'm not sure it adds anything of value. |
@darrenclark yeah I think we should drop this particular PR in favor of the above approach. for the current user style id I was thinking it could be as simple as adding a key to the return value of the subscription config args, %{context: %{current_user: %{id: id}} ->
{:ok, topic: args.whatever, id: id}
end Or for a version that treated any logged in user the same config args, %{context: %{current_user: _}} ->
{:ok, topic: args.whatever, id: "authenticated"}
end Then the full subscription id would be |
I have a contrived example of what I was hoping to support: subscription PriceChange(productIds: [ID!]!) {
priceDidChange(productIds: $ids) {
product {
__typename
id
name
price
}
}
} producing a stream of JSON objects something like: {
"event": {
"__typename": "Product",
"id": "Product:3242",
"price": 10.5
}
}
{
"event": {
"__typename": "Product",
"id": "Product:12",
"price": 11.25
}
}
{
"event": {
"__typename": "Product",
"id": "Product:924",
"price": 5.05
}
} I was hoping that we'd be able to give all these documents the same ID: # only whitespace differs
subscription PriceChange(productIds: [ID!]!) {
priceDidChange(productIds: $productIds) { product { __typename id name price } }
}
# same fields, different order
subscription PriceChange(productIds: [ID!]!) {
priceDidChange(productIds: $productIds) { product { id __typename price name } }
}
# different operation name / variables
subscription PriceDidChangeSubscription(ids: [ID!]!) {
priceDidChange(productIds: $ids) { product { __typename id name price } }
}
# fragments
subscription PriceChange(productIds: [ID!]!) {
priceDidChange(productIds: $ids) { product { ...ProductInfo } }
}
fragment ProductInfo on Product { id __typename price name } It'd also be nice for us to be able to ignore the arguments for the subscription Any thoughts on:
|
Or rather, it would be nice to have the option to have full control over the IDs being generated, so that we could account for those situations. I can only really see us accounting for those situations if we see a lot of duplication due to minor changes like those. |
This gets a little tricky. Notably, these all have completely different blueprint representations, so the current functionality today treats these as all different. And if you have a serializer that respects the order of the fields as queried then they really ARE different. The results of one are not substitutable as the results of the other. However, Absinthe can sort of punt on these questions I suppose if we also allow a
Notably, |
Ah yes, good to know (I had no idea they mentioned it in the spec hah)
The final ID would end up being This would work well for our use case |
Yes, which we'll refer to as the |
If no |
Cool, sounds good! |
Closing as per #652 (comment) |
This fixes an issue where identical subscription GraphQL queries would not be deduplicated, i.e. would end up with different doc IDs here:
absinthe/lib/absinthe/phase/subscription/subscribe_self.ex
Lines 22 to 23 in d34d5ab
The anonymous function used when resolving
__typename
ends being "different" when run on separate processes, preventing identical subscription queries that use__typename
from being deduplicated (when run from separate processes)The root cause of the issue:
ends up producing:
I noticed this issue also happens with
Dataloader
/Dataloader.Ecto
because of these anonymous functions:https://github.com/absinthe-graphql/dataloader/blob/a61f550a28820d2befcd94411f7b03163f30db30/lib/dataloader/ecto.ex#L190-L191
(was able to workaround that on my end though, as I just provided non-anonymous functions for
query
andrun_batch
in my code)@benwilson512 How do you feel about:
:crypto.hash(:sha256, :erlang.term_to_binary(blueprint)) |> Base.encode16()
? (maybe to ignore anonymous functions, or simply use the query's string representation, not sure)and/or
I'd be happy to take on both of those tasks, just wanted to get your thoughts before I started on them