This has been asked several times before and in several variations but I wanted to ask again as a lot of the other topics are either fairly old or the information is spread across questions / topics.
I’m creating a SaaS system. There are 3 parts to the system:
The landing page
The dashboard
The customer facing part of the application
I am not overly concerned about the landing page, nor the dashboard (for the most part). There is one page in the dashboard that will be used a lot, but this is mainly waiting for reactive DB updates when orders are made on the customer facing part of the app.
My concern is around concurrent users on the customer facing part of the app. I’m not sure what to expect with performance. I understand that various factors can affect performance and certain practises can improve performance but I wanted some advice on some benchmark figures that other people have experienced with their Meteor applications?
I am of course not expecting the software to be an overnight success (nor am I expecting it to be a success anyway - I’m trying to be realistic). I currently plan to launch the site on a VPS machine(s) on something like DigitalOcean or Linode. I suspect I will pay for one of the cheaper $5-20 tiers initially and go from there.
I am undecided on whether to run the database on the same VPS, on a different VPS or on a managed MongoDB database. Managed MongoDB servers are pretty expensive from what I can see (unless someone can recommend a more reasonably priced one?)
Or, should I just spin up a single VPS for each customer and host the application and database on the same server for each person (presumably the low ~$5 tier would be fine for this?) However, that also then brings up the question of how to manage the temporary free tier that users will have access to. Alternatively, is it better to have separate servers for the application and one for the database? Or vice versa?
Is performance / concurrent user performance based on the application web server or the database server?
Any advice on any of the questions I have would be really appreciated. Sorry for the fairly long post. As you can see, I currently have a lot of questions and a lot of decisions to make before trying to launch.
I run a small SaaS product on a single $10 DO instance, including DB and landing pages. The nature of my service is that I have 200-500 sessions/hour depending on time of day. Quite a lot of sessions have long-term subscriptions (the app is left open 24/7 on kiosks or desktops) and lots of short term subscriptions when users open the mobile apps briefly.
I used to be on a $5 instance but I recently expanded to $10 because I was occasionally running out of disk space during deployment. RAM sits at around 500MB, CPU between 5-7%, but it’s not really a processing heavy system. I have all tenants on the same server and db, using mizzao:partitioner to separate each customer’s data at a low-level
Having the db on the same host gives excellent response times. I take 3hrly db backups from a remote device (not that I’ve ever needed them). It’s not a mission-critical system so I’m happy with that.
My advice (and advice I received here a few years ago) is to start cheap and simple. If you start to outgrow your hosting then that’s a nice problem to have and you’ll have paying customers to cover the extra cost. By then you’ll have more understanding of how your app performs under real load, and so what you’ll need.
If you want some benchmark figures, here's some graphs from my last week of data
Thanks @znewsham. I wasn’t aware my CPU is impressively low, but there are a few things I’ve done over the years to keep it down:
I’m always thinking about performance when writing subscriptions and methods. I only ever hit the database if I need to and try to minimise the number of times a method has to hit the db by combining all fetches into a single operation. E.g. if a method first has to check that the user has the correct role/permissions to complete the method, and then the method needs some user data, I do a single fetch on the user to get the role AND the required data, rather than two different fetches. MontiAPM’s detailed logs help with that.
I use my own package wildhart:merbox-cache which automatically fetches data from the mergebox if it can, to reduce db operations. This is inspired by msavin:usercache but works on all collections and has a couple of bug fixes.
I use my own job scheduler package which is highly optimised for performance and uses zero background cpu.
I also try to offload as much as I can to the client - e.g exporting data - just give the client the data it needs and let the client do the heavy lifting of processing it, converting it to CSV, etc. And also in the reverse, processing data client-side before calling a server method, so the server has to do a little as possible with it.
Good Mongo indexes are essential.
Using Mongo Oplog can halve your CPU, my own before/after graphs are here.
I have bundlephobia - I don’t add npm packages to fix trivial things I could do myself. As you can see from the above, where I do add npm or meteor packages, if I can find ways to optimise them then I do.
My experience is that it’s higher. 5-10% CPU on a single $10 server with 250 connections. I suspect the “long runningness” may be some part of it - a connection that isn’t consuming any data really has no CPU cost (though it often does have a memory cost).
It’s not easy to give advice without knowing a lot about the type of data (read/write volume and computation needs mostly). If your meteor server is basically acting as a passthrough from DB -> client and there are limited reads, then scaling will be relatively easy.
Choosing whether to spin up a container/DB for each customer (single tenant) or keeping them in one is usually more a security consideration - either one can work, and either one can be scaled. Each has it’s own drawbacks (poor load balancing for intermittent use customers vs ability to tell a customer “it’s slow cos you’re using it a lot”).
I personally would never run the DB on the same host as the app server - I prefer a separation of concerns (for performance and security) and I’d always run a full replicaset rather than a standalone (for resiliency). But depending on the type of service you’re selling, your requirements may be different.
@znewsham, regarding db on the same host - I understand your point about resiliency, but like you say that depends on the type of service you’re selling.
But regarding performance - provided neither the meteor process nor the db are processor hogs you can’t get more performant than db on the same host with zero latency. But yes, when one starts slowing down the other then they need to be separated.
Regarding security - My db is closed to the outside world. I don’t have to expose any ports on my host just for the db because the app can access it on localhost, so also more secure, no?
This works only as long as you don’t need to scale out with your app. As soon as you do, you’ll have to expose the db’s port anyway. But even that is not a security problem if you set up a firewall (e.g. iptables) that makes the port inaccessible again – except for the dedicated ip numbers of where your meteor instances (and, alternatively, mongodb replica set members) are.
What @znewsham may have meant with security is that if you have multiple services on the same machine and one of them has such a security flaw that allows for the execution of an arbitrary code by the attacker, then the data from the other service (e.g. a complete mongodb database) could be copied.
Galaxy is not a bad option, specially they give you monitoring, security is another thing to consider.
Digital ocean recently started offering “app platform” not really sure if it’s any good, but it’s advertised that nodejs + mongodb can be hosted in single app for around $12 or so.
Paradoxically, the more and the better such services pop up, the more is Meteor’s existence in jeopardy, meaning that Meteor’s increasing popularity can also seal its own fate – not technologically, but in economic terms. MDG abandoned Meteor for unknown reasons, and it’s not far fetched to assume that their business model didn’t pay off. It was based on Galaxy, and obviously, that’s the same with Tiny.
Apparently, the team was much larger with MDG, i.e. their monthly recurring costs must have also been much higher, but Tiny still has the very same problem: If they don’t get enough customers for Galaxy sustainably, they will go down. Also, there’s an investor behind Tiny, and they want to see a profit.
From this perspective, MUP / MontiAPM and all the app platforms are parts of a problem for Tiny, and, from a broader perspective, a problem for all whose product is based on Meteor.
@wildhart Thank you so much! I will definitely keep these in mind.
I think my main issue currently is that I am making too many calls to the database because I couldn’t figure out how to get all my data in one call.
I am using Meteor & React. I have a collection that can be a parent of itself. I couldn’t figure out how to get all connecting children and sub children etc from one call. It’s explained in more detail here.
I essentially currently render one HOC using withQuery and then an unlimited number of child HOC withQuery components within each child (if that makes sense?)
I guess I will do my best to optimise performance prior to launch and then work from there.
@znewsham - My initial thought was to separate the database, which I will likely end up doing. Maybe not at initial launch, but soon after I suspect.
@peterfkruger - Thank you for helping (again)! Is MUP the best option for deployment? For some reason, I had been under the impression that it was not supported anymore.
@deank - Galaxy seems to have good tools but the pricing is high. I also recently saw the DO App Platform but I was slightly confused but it’s pricing. It seemed that you signup for the app platform, but also then pay fees on top of that for hosting?
That’s why Tiny is improving Galaxy. They have to. That’s how they win. Meteor is open-source so they know there’s going to be many solutions for self-hosting, etc. They have to make Galaxy the easiest, most feature-rich, most economical, solution. Galaxy could use a lot of work on the economical side of things.
I can’t tell you if it’s the best option. I don’t use it at the moment, but I will probably give it a try, since it is looking good and has just about the features that I need.
In plain language, Galaxy needs to be a lot cheaper; and then probably a lot more people would not bother anymore with custom deployments, they would much rather go straight to Galaxy and stay there.
I believe it was a sound idea back then with Meteor’s previous owner to try to sell Galaxy’s service at a high price. But it already should have been realized since that the profit curve maximum is not nearly at that price.
Have you tried mongo aggregation with $graphLookup? I’ve never used it myself, but I use aggregation in other ways.
The MUP repo has been updated regularly, most recently 4 months ago. It has a growing list of plugins which have been added relatively recently. The author @zodern is active on these forums and is also the developer of MontiAPM.
Having said that, I would love to support Tiny if Galaxy was cheaper.
I’m starting to use Docker in a simple server. I think that way is better to future increase of performance. You can start with a simple Docker or a Swarm with only one node. And if you App comes to have a lot of connection, you will be prepared to expand your structure.
To host, I’m using UpCloud. Little more expensive that DO and others, but the hardware is so much better. You can use my referral link to receive a bonus of $25 . https://upcloud.com/signup/?promo=28S7CS
You can use MUP too, is an easy way to deploy in your own server. And you don’t need to know about containers or others infrastructure things.