This is a guest post from Arunoda Susiripala, the man behind MeteorHacks and many open-source Meteor packages. In this article, he’ll explain how to improve your app’s performance using Kadira, his newly-launched Meteor performance monitoring solution tool.

We tend to improve app performance only when we are reaching launch day, or when users start to complain about slow speeds. And sometimes we discover these issues too late, leading to loss of potential customers and users.

It would be great if we could write high-quality, optimized code from day one. But that can be difficult to do, and may also lead to over-optimization and wasted resources.

So in this tutorial, I will instead show you a continuous approach you can use to improve the performance of your Meteor app while as building it. This way, you’ll be able to launch your app and release features with confidence.

Throughout this process, we will get some help from Kadira. Let’s get started!

Continuous Performance Optimization

Here’s the process we will be using to optimize our Meteor app. This will play nice with your existing development workflow, especially with concepts such as the lean methodology and continuous delivery:

  1. Develop a feature of your app.
  2. Deploy it and ask real users to use it.
  3. Observe how your app performs (with help from Kadira).
  4. Apply improvements if needed.
  5. Apply fixes and start again.

The second step mentions using real users to test your app. Real users could be your early adopters, or existing beta testers on your team. It could even be your mom and dad!

Doing this will enable you to see how your app behaves in real-world conditions. You’ll be able to optimize what actually matters to your users, so you won’t waste time and resources fixing something your users don’t really care about.

Start Monitoring With Kadira

First, you need to connect your application to Kadira. Doing this will show you exactly what is happening inside your application in real time. We’ll then use the information Kadira shows to optimize our app, focusing only on what matters most.

Connecting to Kadira is very simple: create an app and follow the steps – it will take no more than 5 minutes.

Special Deal: Get One Month Free

Here’s a special deal just for Discover Meteor readers: sign up or log in using this URL and you’ll get 1 free month on the “Startup” plan:

https://ui.kadira.io/account/coupons/YeGXPMsnggREYwbS7-startup

And yes, this even works if you already have a Kadira account!

Let’s Optimize

Once users have used the app for a few hours, it’s a good time to check for optimizations. I’ll show you some simple ways to locate and fix performance issues.

Although Meteor is a complex framework, there are two main entry points for all the interactions:

  • Methods
  • Publications and Subscriptions (PubSub)

We will focus on each of these separately.

Optimizing Methods

Here, we simply figure out the methods with higher throughput and try to reduce their response time.

Throughput means the number of method calls per minute, and response time indicates how much time the server takes to process.

Go to the Methods Detailed View and sort methods by throughput. Start optimizing from the first method (the one utilized by most users). Then optimize all methods with higher throughput compared with others.

Methods sorted by throughput

To start optimizing, click on the first method and look for the Response Time Graph. Normally, if your method’s response time is below 500ms, that’s good.

Additionally, you can look at a sample trace of a method and see what exactly happened there, which will help you identify where the server spends time and what do to address this.

Method trace

We can use a few different things to reduce response time:

  • Add necessary indexes.
  • Optimize HTTP Calls, email sending & third party NPM modules.
  • Do MongoDB aggregations.
  • Reduce wait time.

Refer to this Kadira Academy article for a step by step guide to applying the above fixes.

Optimizing PubSub

In PubSub, we’ll look at a few metrics to see what we can improve:

  • SubRate - number of subscriptions in a given minute.
  • Response Time - time taken to process publication until it sends the initial data set.
  • Network Latency - time taken to send data to the client.
  • Observer Reuse - ratio of observer reuseness. I’ll talk more about this later.

Reducing Response Time

First, go to the PubSub Detailed View and sort methods by SubRate. Click on Publications and look at the response time. We want to try to reduce the response time starting from the first publication. You can follow the same process we used to reduce method response time.

Publications sorted by SubRate

Note that inside methods you can call this.unblock() and prevent other methods and subscriptions being waiting on that method. But unfortunately, there is no such API for publications.

Reduce Network Latency

Network latency shows the time it takes to send the subscription data to the client. This is a metric derived from the data you are sending to the client.

Select the client’s desired bandwidth from the top right and look for the network latency value. If the network latency value is less than 500ms, you’re probably good to go. On the other hand, if it’s more than 500ms, here are a few steps you can follow to reduce it:

  • Use field filters.
  • Use paginations and similar techniques.
  • Do client-side counts on the server.
  • Restructure your app.

Refer to this Kadira Academy article for more information.

Optimize For Oplog

You must use the MongoDB oplog integration to improve the performance of your app. It’s a must for a production app. But not all queries are supported with a simple oplog. If it is unsupported, it will fall back to poll-and-diff logic for detecting MongoDB changes, which is inefficient.

When analyzing a PubSub trace in Kadira, it will show you whether your observers are using oplog or not. If not, it will tell you the reason they’re not using oplog, and how to fix it if possible.

Optimize for oplog with Kadira

Reduce Observer Reuse

When you create a subscription, all your cursors will be converted into observers inside the publication. The observers will fetch the initial data from the MongoDB. They also look for database changes and send those to the clients.

Observers take time to fetch data from the server and will spend some CPU time when observing for changes.

Fortunately, if you create identical cursors, Meteor can reuse them. See following example:

Meteor.publish('postsByCategory', function(category) {
  return Posts.find({category: category});
});

There are four clients subscribing to above publication.

1. Meteor.subscribe('postsByCategory', 'meteor');
2. Meteor.subscribe('postsByCategory', 'meteor');
3. Meteor.subscribe('postsByCategory', 'nodejs');
4. Meteor.subscribe('postsByCategory', 'meteor');

1st client creates a cursor with {category: "meteor"} and Meteor creates a observer for that cursor. After that 2nd client creates a subscription with the same category. Meteor does not create a new observer but simply re-use the existing one.

In the above example, 2nd 4th subscriptions are reusing an existing observer. So observer reuse ratio is 50%.

Observer Reuse Ratio = reusedObservers/totalObsevers * 100
                     = 2/4 * 100
                     = 50%

In Kadira, sort your publications with SubRate. Find the observer reuse ratio of all the publications with a higher SubRate.

Observer Reuse Ratio

Follow this guide to writing queries, which will have higher observer ratios.

Reduce SubRate With SubsManager

Normally it is impossible to reduce the SubRate, since it depends on the number of users you have. How users use your app is not something you can control.

However, you can reduce unwanted subscription requests. For example, when you change a route (Iron Router), all previous subscriptions will become unsubscribed. This causes unwanted subscription calls.

If we can cache subscriptions while we change routes, we can easily solve this problem. This is not an issue with Iron Router itself, but is a feature when you subscribe inside a Deps.autorun computation.

Subscription Caching is the solution to this problem. You can easily implement it with the SubsManager package.

This is how SubsManager can be used inside Iron Router to cache subscriptions.

var subs = new SubsManager({
  // will be cached only 20 recently used subscriptions
  cacheLimit: 20,
  // any subscription will be expired after 5 minutes of inactivity
  expireIn: 5
});

Router.map(function() {
  this.route('home', {
    path: '/',
    waitOn: function() {
      return subs.subscribe('postList');
    }
  });

  this.route('singlePost', {
    path: '/post/:id',
    waitOn: function() {
      return subs.subscribe('singlePost', this.params.id);
    }
  });
})

We don’t need to do this for all the subscriptions. Let me show you how to find out such publications with Kadira.

First, go to the PubSub Detailed View and sort the publications by Shortest Lifespan. Choose publications with short lifespans and high throughput. You need to cache subscriptions for the chosen publications:

Publications with Shortest Lifespan

Performance Optimization Doesn’t Have To Be Hard

Throughout this continuous process, we tried to optimize what matters most to your users. By the time you launch your product, you’ll have fixed the most glaring performance issues in your app.

So give Kadira a try, and let me know if you manage to make your Meteor app more performant!