The Top 10 Meteor Performance Problems
When Meteor was first released in 2012, it represented a fairly ground-breaking approach to building web and mobile apps. All of a sudden, it became much simpler to create real-time applications using a single language on both the client side and server side.
Many, like us at Project Ricochet, experimented with Meteor and were impressed by how things just “magically” worked. Even in those early beta releases! Key features included database triggered live-page updates, data synchronization, live queries, latency compensation, hot code pushes, and more. Before long, Meteor developers (ours included) started turning their ideas into actual production apps.
Perhaps the true magic of Meteor is how it helps simplify a set of complex technologies behind the framework. This gave developers capabilities that previously were not possible — or required a large amount of resources.
Certainly, that newfound simplicity made it easier for developers to create amazing real-time applications. However if you weren’t careful, application performance could slip in fairly rapid fashion. Add a few concurrent users — and you could suddenly encounter all sorts of issues.
Set aside Meteor for a moment. Any web or mobile application that takes on a growing number of users can suffer performance issues. More users can readily cause bottlenecks to key resources like the CPU, network, memory, or the database. And, with more demand on resources, users begin to experience longer response times. Client and server machines can start maxing out on CPU and memory usage.
Well, Meteor applications are no different. Here at Project Ricochet, we’ve now written over 50,000 lines of Meteor code. So, we’ve certainly seen our share of performance-related issues.
Looking back, these are the issues we encountered the most:
1. Publishing Too Much Data
Once a database grows to a certain size and the number of concurrent users starts increasing, the server’s CPU, memory, and network usage skyrockets. Much of it has to do with the amount of data that’s being published and subscribed to. This results in slow performance on the server and long wait times for subscriptions to ready on the client.
2. Counting and Aggregating On the Client Side
We often need information about the collection as a whole, such as the number of records. Once again, when the database is small, performing counts and other aggregate functions on these datasets is no problem. But as our data grows, it doesn’t take much for these calculations to incur a significant performance penalty, particularly if you’re performing them on the client.
3. Publishing Real-Time All the Time
Meteor makes your app real-time by default. Behind the scenes, delivering this real-time capability is no easy undertaking. It places a high demand on the CPU, memory, network, and the database to work. This is especially wasteful if the client doesn’t require all of the data to be in real-time.
4. Too Many Observers
When a client subscribes to a publication, observers are created from a returned cursor or by calling a cursor's observeChanges method. These are set up to track any data changes that might occur on the queries involved. The more connected clients there are, the more subscriptions are established, increasing the number of observers. Unfortunately these observers require high CPU and memory usage, as well as network bandwidth between Meteor and MongoDB.
5. Frequent Subscriptions
Every subscription triggers the set up and flow of real-time data between the client and server. As more subscriptions become active, or as the data set of each subscription grows, more demands are placed on the entire system. To prevent the use of unnecessary subscriptions, we often bind subscriptions to a route, template, component, or computation so that they are automatically unsubscribed when not needed anymore.
However, this presents another problem. Frequent subscribes and unsubscribes can also tax the system, especially if the route, template, or component changes often or the computation is rerun frequently.
6. Subscribing to Too Much Data
We don’t always need all the data that a publication provides. Doing so leads to wasted client CPU and memory usage. The problem is even worse if it generates unnecessary reactivity updates. These issues aren’t typically seen in a local development environment, which usually has a small database and no other users on the system. However, once the database grows and concurrent activity increases from other active users, the client can slow down drastically.
7. Database Polling
In a non-local environment, Meteor, by default, will poll the database for changes on an ongoing basis. This can result in slower real-time updates. It also results in wasted resources on both the Meteor server and MongoDB if there were no database changes.
8. Slow Reads and Writes
As your database grows, queries can take longer to execute. More data means more information for MongoDB to process your query against. These issues can often catch you off guard, because they hardly ever happen in a local development database featuring a smaller amount of test data.
However, in Production this degradation can happen quickly. Especially with a significant and sudden increase in users. Database updates can experience the same performance impact too. In addition, too many users writing or updating to the database often leads to bottlenecks in MongoDB.
9. Slow Third-Party Services
Sometimes the fault for bad performance lies elsewhere. Often our apps rely on data and services from third-party providers. And, while doing so allows us to leverage their platforms, when they perform badly, or even worse, go down, so can our app.
10. Stuttering Animations
This last issue is a different type of performance problem than the rest. Mobile apps have come a long way. In many respects, they can provide better user experiences than their web app counterparts. With access to mobile-only hardware like GPS, accelerators, and fingerprint sensors, you could argue that mobile apps have raised the bar for what users expect from their technology interactions.
A clear example of that would be the animations that native apps often deploy to visually communicate feedback, information hierarchy, function changes, and prompts. In Meteor and Cordova, we can achieve similar animations using web technologies, like Javascript and CSS. However, because of the browser software they are rendered in, they often don’t have the same silky smooth movements. Instead, what users see are choppy, clunky movements that distract us from the true intentions of these communication cues. Users also interpret this (rightly or wrongly) as slow app performance.
How To Solve These Problems?
Of course, these aren’t the only performance problems you’ll likely see when creating a Meteor app. Editing the list down to 10 issues here was difficult. Meteor is a full-stack framework that handles everything from the browser to the database and everything in between.
As usage for your app grows, performance problems can surface at any point in the stack. But, based on our experience, we saw these 10 challenges most often. By tackling these issues and learning from others, we’ve been able to successfully apply useful strategies to mitigate, and often times, eliminate these issues altogether.
So ... what good is a list of challenges without the solutions? That’s what we thought!
Which is why we have also developed specific, detailed solutions to each of these issues for you. They are all contained in a FREE White Paper called “How to Address the Top 10 Meteor Performance Problems.” All 10 issues above are covered within the White Paper, and each features context on how to address the issue along with at least one code example.
This article and White Paper written by Marty Banting with the Project Ricochet Team