Introduction

I get paid to develop an application in Meteor so most of my days are pretty good. Occasionally I get tripped up by something in Meteor. There are enough posts about common mistakes, so I decided to post some of the less common mistakes I’ve come across. Some are harmless and make you laugh. Those deserve a few sentences. Some are not so funny and make you consider signing up for more yoga classes to deal with the frustration. This is one of those.

A former math professor of mine would tell us a seemingly random story or joke rather than a dry explanation in the hopes that we would remember it better. To his credit, I have never forgotten to add the Jacobian while solving an integral in multi-variable calculus, so please bare with my limited literary skills until the end. For the next not-so-common-mistake, I’ll write a joke.

Limitedly Mistaken

The Story

Your application is being used by more and more users, who are being annoyingly good at their job and generating lots of data. You anticipated this long ago and have your major publications paginated using the limit option. You decide that it would provide a smoother user experience if you switched that load more button to use an infinite scroll component. Since you’ve already transitioned to using React, it is a simple matter of wrapping your existing component with a third-party component and passing down a prop. You test it locally and see that it scrolls nicely and automatically subscribes to more records when you reach the bottom. You throttle the scroll event to prevent your users from hitting your servers to hard if they scroll quickly and pat yourself on the back for thinking about that. You deploy and smile, ready to head home for the day. That is until you see your server grind to a halt and your CPU usage hit 100%. You open your application and see the scrolly thing on the scroll-bar get smaller and smaller. You open the dev console and see that you are subscribing to several hundred records. You curse the infinite the scroll library, revert that change and try to figure out what went wrong.

The Code

Let’s look at the following code which is a simplification of the more complex implementation but without React or the scroll library. Can you spot the mistake? I’ll give you a hint, this code contains it so cursing the scroll library was uncalled for.


Session.setDefault('posts_limit', 0);

Template.postsList.helpers({
  limit() {
    return Session.get('posts_limit');
  },
  posts() {
    return Posts.find();
  },
});

Template.postsList.events({
  'click button'() {
    Session.set('posts_limit', Session.get('posts_limit') + 10);
  }
});

Template.postsList.onCreated(function() {
  this.autorun(() => {
    Meteor.subscribe('posts', Session.get('posts_limit'));
  });
});

It turns out that setting limit = 0 as an initial value is a really bad idea. If only this would work when withdrawing cash at an ATM. You ask for $0.00 and you receive all the cash in the ATM.

The Analysis

In retrospect, there are few things which could have been done differently.

1) JavaScript’s falsey values can be a subtle source of bugs. Since when is undefined semantically the same as zero? Perhaps they never should have been introduced. Now they are a fact of life and we need to be defensive.

2) Meteor’s code should not contain surprises like that. Given the reactive nature of Meteor, deploying a change like that causes every client to resubscribe. Give me nothing should really mean, ‘give me nothing.’

3) When testing manually, at least test with a proper data set and perhaps introduce some latency. I only had a few records in the development fixture and didn’t notice the full subscribe and then the normal subscribe as the limit was nearly immediately set to a non-zero value after the initial load.

4) Have some sort of automated integration tests which test such edge-cases. Doing these sorts of tests in Meteor are not trivial but possible.

The conclusion

In short, this

Meteor.subscribe('posts', {limit: 0})

is equal to

Meteor.subscribe('posts')

. Now I write,

if (limit > 0) {
  Meteor.subscribe('posts', {limit})

instead.

In an ideal world, no one would make mistakes, but in the real world we do. I share my mistakes freely, in the hopes that you won’t have to learn them the hard way.