tgdn / react-starter-kit-subscription

Example of Apollo subscriptions using react-starter-kit
MIT License
1 stars 0 forks source link

Have you run into the question "How to limit messages updates rate for subscription?" #4

Open k1tzu opened 7 years ago

k1tzu commented 7 years ago

For example if there're a huge amount of new messages at once so the client's browser just hangs if it receives all updates one by one. Do you have any ideas how to limit it? For example accumulate new messages and send updates only once in a time. Or maybe make it revert - client asks "are there any new messages?" and server answers with the messages buffer... I'm working on a project for IoT where I have millions of devices that sends updates and I need to show "new messages" to the user's UI. So it's someway alike your messaging app. Do you have such question within your project?

tgdn commented 7 years ago

I ran into this issue, but it was mostly a rendering issue: the UI having to update all the time in inefficient ways would cause it to hang, rather than the amount of messages the client receives.

Have a look at implementing shouldComponentUpdate and higher level hierarchies within your code: having multiple very simple components instead of a few more complex components. And handle whether the component should rerender or not. Have a look at recompose and reselect

Otherwise, I haven't actually run into the issue of having to rate limit incoming messages from the websocket.

k1tzu commented 7 years ago

Thank you for the answer. Definitely shouldComponentUpdate is a must. By the way do you aware of that redis can’t horizontally scale it can only replicate. You can’t build a network of redis servers. For example you can’t subscribe on a server in Canada and get events on that server from France redis server forwarded? Have you thought about it? And another thing - you can’t filter out messages on redis and you will get all messages on your react servers no matter that you subscribed for particular users messages and don’t need the other flow am I right?

tgdn commented 7 years ago

For now I will only be using one Redis instance. And that will probably be enough for low to medium traffic.

If/When I start to run into issues and that I need high availability, multiple instances across the world etc I'll update the system. RabbitMQ is probably a better option for a messaging queue. Also, because I post the messages through the graphql api, the PubSub is only needed for the real time aspect of the overall system. I haven't tested it yet so I can't say for sure what is best, or whether one Redis instance will fall appart or not. But there is no need to optimize before actually seeing and testing that the current system doesn't work.

I filter them before delivering them to the users. Each user gets the messages/updates that he subscribed for. On the server, once a message is created, it goes through the subscription resolver and is sent to the user if he subscribed to it, otherwise no. I don't get what you are saying about Redis.

k1tzu commented 7 years ago

About the filter - for example you have several messaging servers. Sampleuser1 on react server 1 subscribes to the messages for him. Sampleuser2 on react server 2 subscribes to the messages for him. Those subscriptions go to Redis - right?

And then, on react server 3 a message for sampleuser1 gets via graphql. And that server sends publish message to redis - right? And without a filter that message will be delivered via Redis to both servers 1 and 2 as far as I understood the thing. So without a filter on redis everyone will get everything and all you can do is to filter message only on react server upon arrival. So if you have like thousands servers around the world and millions of messages and data and millions of users and their own subscriptions - it will be extremely expensive to work without filtering data flows as early as possible. That's what I mean.

tgdn commented 7 years ago

You have 3 compiled files in build/

Both server and socket can be in different physical servers. You can spawn as many as you want of each in different locations etc. But they must be connected to the same Redis database, or to a Redis instance which is replicated across regions. The client which is compiled could be hosted on a completely different server such as a CDN because it is client-side JavaScript.

In your example I assume you say there is one server.js which handles the /grapqhl api calls and two socket.js which handle the websocket connections. Each client is connected through websocket to a different instance of socket.js in different physical locations.

Whats happens is that whenever a message is posted from one of the clients (through /graphql and thus server.js) it is published to Redis. Then, the two websockets (socket.js) handle the subscription on this line. And if you uncomment the lines below and comment the line 32 you can filter which clients you should publish the message to. And so the filtering is done server-side and not client side. And the client only receives his updates, and so only messages sent to him. And does not have access to all the other possible updates.

Does that make sense?

Messages are published through Redis first, and are filtered and published to the websockets only if allowed.

The method withFilter should return a boolean indicating whether the graphql-subscription should be published to the websocket client. And so you can easily build a 1-1, 1-many, many-to-many chat or simply a real time dashboard which sends live updates to a specific user. That will depend on your implementation.

k1tzu commented 7 years ago

I think it's not the best option to separate most of services. Much much better is to grow horisontally just by adding adding new server. So in my example server.js with /graphql and socket.js live on the same server. But there're lots of servers connected through Cassandra-like db at the back and some messaging platform on the front. By the way I have a friend he's a head of a huge software development company. And he said that rabbitMQ by his experience was a worst option. I don't remember why but he said it's better to write your own messaging based on ZeroMQ than to use rabbitMQ.

tgdn commented 7 years ago

Well, growing horizontally is the goal here.

It's up to you whether you want to have everything on the same server or not. But my approach was for using Google App Engine for the server.js which does automatic horizontal scaling, spawning nodes as needed, and have a load balancer with multiple automatically scalable Compute Engine instances which handle the socket.

Well, ZeroMQ it is. But Redis for starters is probably a good call.