parse-community / parse-server

Parse Server for Node.js / Express
https://parseplatform.org
Apache License 2.0
20.83k stars 4.77k forks source link

How to scale horizontally a Parse Server? #4564

Closed Samigos closed 5 years ago

Samigos commented 6 years ago

My current setup is: 1 Parse Server - 2.6.3 1 MongoDB Server - 3.4.9

Both running on Ubuntu VMs.

In a few weeks, we are expecting our user base to grow significantly! We are currently using 2 VMs on Azure... 1 as a Parse Server and the other as a MongoDB Server.

Could someone guide me through the steps to add 1 VM, for each of the above types? (+1 Parse Server and +1 MongoDB Server (it's something I've never done before!))

I believe I'm going to need a load balancer... am I right? How would it play along with the Parse Server?

tolgaatam commented 6 years ago

How would it play along with the Parse Server?

Parse Server is designed to be stateless. It means that parse web servers do not store critical information locally and can scale horizontally without any further in-server configuration. Even some consecutive requests from the same user can he handled by different servers without any headache.

I believe I'm going to need a load balancer... am I right?

Yes. Load Balancer is a frontier server with a static IP. It receives initial requests from the clients and directs them to your internal parse servers by using a specific algorithm. It ensures that your server with better response time (less busy for that moment) becomes responsible from the request.

For this, you can get the load balancer service from Azure, you do not need to set it up by yourself (better you do not try to :) ) Incoming 80 (http) and 443 (secure http) ports of your load balancer should be open (probably this is the default config) and outgoing 80 should be open (your load balancer and parse servers do not need to talk with secure http). Your parse servers should already be able to receive requests from http. You may further want to restrict the inbound traffic to your parse servers so that they only accept requests from your load balancer's address. All load balancer configuration, you can do them from Azure's graphical dashboards.

After this point, just set up as many parse servers as you want the same way. You will tell your load balancer about your new server and it will start to work with the new server as well.

For database, you do not need to do anything for now, it seems. You can create replica sets (few database servers hold the same data and you can read from any) and even can do sharding (data is spreaded over to different servers) later on to scale your horizontal superbly, but beware that your database architecture will stay central as all your web servers will use the same database and you do not have to scale your database every time you scale your web servers. Generally, a crappy machine with a good SSD will not require scaling before you have more than few web servers.

Last advice is, do not maintaing the database yourself 🗡 Backups, preserving the data, metrics and SCALING will cause a lot of trouble later. Consider moving to MongoDB Atlas or mLab. I know that Atlas can deploy your servers to Azure data centers and charge you a little more than the actual server cost and this little more is worth the pain and time it saves. I myself deployed my servers to Amazon and chose Atlas to deploy the database to the same Amazon region. Latency between the web and database servers are very good and my database is maintained by them so I just sit back and relax. I hear that mLab is also very good, especially with the customer relations.

Samigos commented 6 years ago

@tolgaatam First of all, I want to thank you for the response! You really helped me put some things in order! I have some questions though, if you don’t mind...

First, does a load balancer have a limit on the number of servers it can handle, or is it unlimited?

Second, right now I’m using Parse’s method to connect to my Parse Server. Now, that my app will have to connect to a load balancer instead of the Parse Server, do I need to do something different, or I keep the old:

let productionServerConfiguration = ParseClientConfiguration {
      $0.applicationId = "applicationId"
      $0.clientKey = "clientKey"
      $0.server = "https://domain:port/parse"
}
tolgaatam commented 6 years ago

@Samigos I feel delighted to see that I was able to help you with my answer.

First, there should be no limit on the number of servers you can host under your load balancer. At least, if there is such limit due to the network traffic of the load balancer, it is very difficult to reach that boundary. So i suggest you do not mind about this right now.

Secondly, your application id and client key are carried through the body of your requests and they will be forwarded as they are by your load balancer to your web servers; so you do not need to change them. On the other hand, you may want to change your server address to something like this:

"https://loadbalanceripaddress:port/parse"

Also, if you use a server domain address, you can direct the domain to your load balancer ip and you do not need to configure anything in the client side. This way your server address config could be like:

"https://serverdomainaddress:port/parse"

I use a domain for my server so that if I need to switch my servers to a different provider and my ip address changes, I can get off with a domain redirection only :)

milesrichardson commented 6 years ago

Note that although parse-server is stateless, scaling horizontally may result in drift between caches. This could cause problems if you're updating roles, for example, and each server is maintaining its own role cache.

If you're scaling horizontally, I would recommend using the redis cache adaptor (as opposed to default in-memory adaptor) and keeping the cache in one place.

flovilmart commented 6 years ago

Using the redis cache is a good idea, and you can also use it for scaling live query.

stale[bot] commented 6 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

ozbillwang commented 5 years ago

@milesrichardson @tolgaatam

Could you please confirm if I have to enable redis cache if scale horizontally a Parse Server?

How can I check if my application is updating roles or not. Any other user cases, which need enable redis? scaling live query?

tolgaatam commented 5 years ago

@ozbillwang

When you do not use redis cache, your parse server caches items locally. this method is known to be causing memory leaks and it is not much efficient. For any serious deployment, I just recommend using a redis cache.

For my case, the greatest use case was session caching. Almost all requests require authorization. Checking session and user information requires 2 round trips to the database normally. However the cache layer in Parse Server caches session along with the related user information. This helps to reduce the 15~20ms session check time to ~1ms once cached.

Given you have 10 instances of parse server and use local caches, this session caching is only %10 successful, as requests from the same user do not have to end up in the same instance. A central cache makes sure that once a session is cached, it always results in a hit (until cache expires) as all the instances refer to the same cache.

Another point is the cache consistency. User roles is an example of this. Once you change a user role, that instance would cache the change on its own and the other instances would be still remembering the old user role assignment. Until the caches expire, that user would still use their old roles which is risky.

So, I would say that, local cache is not suitable for any production environment (due to memory leak issue) and also it is not suitable for scaled environments at all.

ozbillwang commented 5 years ago

Thanks, @tolgaatam

Since I run parse-server via its docker container, how to link it with redis?

$ docker run --name my-mongo -d mongo
$ docker run --name my-parse-server --link my-mongo:mongo -d parseplatform/parse-server:3.4.4 --appId APPLICATION_ID --masterKey MASTER_KEY --databaseURI mongodb://mongo/test
ozbillwang commented 4 years ago

@tobernguyen @milesrichardson

One more question about this topic. Should we enable stickiness on load balancer? If needed, how long would be recommend?

Quota from AWS ALB statement:

Enabling target-level stickiness requires that any weighted routing rules associated with this target group have group-level stickiness enabled first. If the target group is not part of a weighted routing rule, there are no prerequisites for enabling target-level stickiness.