Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
AnyCable vs. GraphQL-Ruby, or why doesn't AnyCable support Custom stream callbacks? #40
Hi, awesome work here.
I'd like to understand why there is no support for "Custom stream callbacks".
I am trying to use it along side with "graphql" gem for subscriptions and it uses a stream_from block.
If you guide me here I contribute to add this feature.
First of all, thanks for the feedback!
The reason is that a
When using AnyCable all the broadcasting is done within AnyCable server which doesn't know anything about your application.
In general, running custom code for every transmission (especially such heavy code as GQL run, if I understand correctly) doesn't scale well. Just imagine that you have a thousand of clients subscribing to the same GQL subscription and you're triggering the update.
In theory, we can add
Looks feasible though.
Hey @palkan thank you for the quick and detailed response.
I will be using actioncable directly because I don't see myself with a lot of time to dig on this. Although I will keep subscribed and ready to switch when this gets done.
I believe the utility of this for graphql subscriptions would be huge.
I've been playing with anycable and graphql, @gastonmorixe you mean
stream_from 2nd argument is the callback, not the block
and as I've seen on graphql subscription implementation it only pass the
@palkan what great job on anycable
@semenovDL custom callbacks are used to run a code inside the actioncable server, useful if you have rails loaded and using a ruby server, in the case of anycable, as the process runs separately, what code will run with custom callbacks? This is what I understood reading the code.
@palkan may correct me if I got it wrong
We've discussed it with @palkan and there is one caveat: where to store callbacks itself? The code that will be executed. That brings us to second feature not yet supported by AnyCable: channel instance variables.
This is a problem because in heavily loaded setups there can be several RPC servers (Rails apps handling client connection, subscription and disconnection) and queries from AnyCable servers are distributed between them in a round-robin fashion.
This is not a problem in ActionCable as AC server is the process that both hold a connection to the particular client and execute Ruby code – in AnyCable these two responsibilities are decoupled.
One possible solution is to enhance API between AnyCable and gRPC server to include special callback request from the AnyCable to RPC, asking to execute callback and including all required data about a client (and serialized channel instance variables).
So the process of broadcasting of the message that requires custom callback will look like this:
There will be some limitations:
Another option is to discard from round-robin between RPCs but it will complicate deploys, scaling, and setup.
On every GraphQL subscription, two ActionCable subscriptions are created.
When application triggers GraphQL subscription:
Then into first of these cable subscriptions quite a little info broadcasted: serialized object (its globalid).
Next, ActionCable receives this message from Redis and runs a custom callback in which:
It looks something like this:
If you have 1000 subscribed clients you will get 1000 of GraphQL query reevaluations in your ActionCable processes.
One possible solution for GraphQL is to write custom subscription implementation with ideas from GraphQL Pro's Pusher implementation: http://graphql-ruby.org/subscriptions/pusher_implementation.html
Idea is to store info about subscriptions in Redis or somewhere and to re-execute GraphQL queries in the process that triggers subscriptions and broadcast only results (and AnyCable will work fine here, but thousands of reevaluations still will be there).
@Envek Thanks for this write-up! (awesome charts, btw
Just want to add that even if we add support for custom callbacks and instance variables, we would still have this problem:
But it would transform into a little bit more scary one: If you have 1000 subscribed clients you will get 1000 AnyCable RPC calls and 1000 of GraphQL query reevaluations.
Probably, we could combine similar subscriptions to decrease the number of evaluations.
We have some ideas on how to do that with some restrictions: for example, if you do not use contexts in your subscriptions (context-free queries), than it should be easy to create a stream per unique subscriptions (schema + params) and re-use it for multiple clients
Sure. No problem. Best, Gaston…
Sent from my iPhone
On Aug 9, 2018, at 5:45 PM, Vladimir Dementyev ***@***.***> wrote: @gastonmorixe I've updated the title to better reflect what we're talking here about; hope you don't mind) — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
Persisted queries feature looks like something relevant http://graphql-ruby.org/operation_store/overview.html.
So, GraphQL Pro already has the functionality to de-duplicate queries. Thus we can use subscription's
Played with the idea to store subscription information in Redis (as GraphQL Pro's Pusher implementation does).
Released proof of concept as a
It works with AnyCable locally
Everyone interested, please try it, play with it and say your feedback (it is not production-ready yet).