Performance Gotcha's in Meteor

We need a community resource for well known, and maybe not so well known performance bottlenecks that are specific to Meteor that may not be familiar to people outside of Meteor or even who just haven’t tried to scale.

I have an application that has roughly 50 users on at a time, with occasionally up to 160.

I’m running this application on a small server but it normally has no problems. That is until a recent update brought the entire thing down. There were no errors to speak of during this time, so the only explanation was performance problems.

It turned out that I was trying to store a very deep object (multiple arrays within many more arrays) in Mongo. I was going on the principle of I can store anything I want in there and only sanitize in my publications (using fields selectors).

In reality however, the rate at which I was trying to update this object brought my app to it’s knees. Repeatedly.

Filtering off that very deep object (which I only need for another purpose and server side only) before ever trying to put it in the database solved my problem.

Some of the other performance gotchas that I’m familiar with (some from bulletproof meteor)

  • Low observer reuse. Making your Mongo finds too fine grained can be helpful since it keeps your client from having too dang many records, it hurts in that it increases both ram and cpu usage as each client ends up creating another observer on your database.
  • Reactivity everywhere. Some things don’t benefit from reactivity. Other things reactivity would have to be too well coordinated. Reactivity also costs because each change causes every function that depended on the changed value to re-run.
  • Bulk writes. This is in the pipe to be cleaned up. Right now, Meteor triggers all affected observers on EVERY write, but also has to traverse every observer. It causes writes to run very slow and to make observers take forever if there are a lot of writes happening when they start. This could really prevent your “ready” from being called
  • Excessive publications. Is every client who subscribes to subscription A going to need subscription B? Or at the very least is subscription B a cheap add-on to subscription A? If so, COMBINE THEM. One Subscription to rule them all!
  • Side effects in subscriptions. It is very tempting to have publications go off and do other things when they are setting up (for example make calls to another application/api make a bunch of log entries, indicate in some database where that user has been). Don’t. Subscriptions are simply ways to coordinate data between server and client. Use methods where you want side effects.
  • Excessive use of this.unblock. Unless your methods are waiting a long time on a lot of async processes, this.unblock is not ideal. It has a tendency to pollute the event loop and can lead to at least some race conditions. Also, this.unblock used in bulk can allow a single connection to trigger a large number of operations on your server filling up the event loop and keeping other connections from getting through.

What are your biggest performance gotcha’s that you’ve had to work through?

7 Likes

Thanks, @lassombra. Great post. Quick question: What do you mean by “filtering off that very deep object”? I’m having the same problem and am eager to learn how you solved it.

Tanks again, db

There are a multitude of options. For mine, I had a single property on the parent object which had no purpose outside of being passed through some other functions to glean their data from, but had a depth in the area of 20 levels or so, and spread out 5-10 wide at each level. I won’t begin to talk about who thought this was a good idea (because it’s not).

I simply set that property to undefined before adding to the database. Techincally, deleting the property would have been the right thing to do, but I elected to just set to undefined and let Mongo sort it out as the storage overhead was minimal, and this was happening in a performance critical section of code.

@lassombra So just to be clear, you took:

var obj = {
  prop1: {
    prop2: {
      ...
         prop20: {
          }
     }
  }
}; 

and you changed it to… ? I’m still a bit confused.

Thanks again for your help and for your quick reply.

db

delete obj.prop2;

That’s all it takes.

Edit, I misread your original object, you would want to delete obj.prop1.prop2;