AnyCable vs. GraphQL-Ruby, or why doesn't AnyCable support Custom stream callbacks? #161
Replies: 27 comments
-
Hi! 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. |
Beta Was this translation helpful? Give feedback.
-
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. Thank you |
Beta Was this translation helpful? Give feedback.
-
hey guys.. 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 😉 |
Beta Was this translation helpful? Give feedback.
-
digging into anycable code, I just found:
so yeah... |
Beta Was this translation helpful? Give feedback.
-
Hey, @palkan. I need this functionality for our project and would like to implement it. Maybe you can share your vision of how this task should be implemented, what to look at first? For example, at |
Beta Was this translation helpful? Give feedback.
-
@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 |
Beta Was this translation helpful? Give feedback.
-
@fernandes Yea, that's the point why we need callbacks. |
Beta Was this translation helpful? Give feedback.
-
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. |
Beta Was this translation helpful? Give feedback.
-
ActionCable subscriptions available in open-source in graphql gem by @rmosolgo works quite interesting: 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). |
Beta Was this translation helpful? Give feedback.
-
@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 |
Beta Was this translation helpful? Give feedback.
-
@gastonmorixe I've updated the title to better reflect what we're talking here about; hope you don't mind) |
Beta Was this translation helpful? Give feedback.
-
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.
|
Beta Was this translation helpful? Give feedback.
-
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 |
Beta Was this translation helpful? Give feedback.
-
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). |
Beta Was this translation helpful? Give feedback.
-
Closing since we've been using https://github.com/Envek/graphql-anycable in production for more than a year. Thanks @Envek! |
Beta Was this translation helpful? Give feedback.
-
@palkan Maybe slightly of-topic but did not know where to ask it elsewhere. Since you have been using it for a while in production, I would like to ask if you are running it together with litecable or not. Cause i am a bit confused when i look at the litecable github page claiming that it's primary use would be for test and development but then later it says it is compatible with anycable. So question is more is litecable production ready or should i just use it in dev/test only and use full actioncable in prod? We are looking at providing graphql subscription support for our graph (and we don't use rails) and found: https://github.com/anycable/graphql-anycable which say it works with litecable for none rails. So hope you could help me with my confusion :) |
Beta Was this translation helpful? Give feedback.
-
Hey @butsjoh! It should work with graphql-anycable, probably, with some additional setup. @Envek any thoughts? |
Beta Was this translation helpful? Give feedback.
-
Hi, thnx for the answer. After a second look at the litecable readme i noticed now that indeed that the part about dev/test is optional. Sorry for my misunderstanding but at least i got the confirmation :) thnx |
Beta Was this translation helpful? Give feedback.
-
@butsjoh I've not tried LiteCable with graphql-anycable yet, but it should work. |
Beta Was this translation helpful? Give feedback.
-
Hi! Are stream callbacks planned in one of the next versions? I also need this functionality. Thx |
Beta Was this translation helpful? Give feedback.
-
Hey @phlegx! Not really. Mostly because we don't see a demand for this feature. Could tell a bit more about your use case? (It would be better to start a new discussion) |
Beta Was this translation helpful? Give feedback.
-
Here my use case @palkan: An authenticated user owns several devices. The list view shows all this owned devices. An ActiveJob handles a device state change and broadcasts the state and the id of the device on the general device channel. The method |
Beta Was this translation helpful? Give feedback.
-
Usually, subscription scopes are used for that: YourAppSchema.subscriptions.trigger :event_name, {}, { data: "here" }, scope: device.user_id Internally, user identifier is got included into Redis channel name so all broadcasts become scoped to one user only. |
Beta Was this translation helpful? Give feedback.
-
@Envek thank you for the reply, but I dont use GraphQL-Ruby and I'm not familiar with that. |
Beta Was this translation helpful? Give feedback.
-
@phlegx You can implement scoping yourself. Instead of broadcasting update to a device, consider broadcasting updates to a user specific channel. I guess, you have something like |
Beta Was this translation helpful? Give feedback.
-
@palkan but |
Beta Was this translation helpful? Give feedback.
-
Correct |
Beta Was this translation helpful? Give feedback.
-
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.
Thank you
Beta Was this translation helpful? Give feedback.
All reactions