Node.js at Zoosk!

Zoosk is now using Node.js to serve many of our pages and we plan to gradually transition from our current, typical LAMP stack to serving most customer facing content from Node in the near future!

You can read all about the various general benefits and detriments of using Node on many other web sites so I would like to get more specific about what I have found. As a developer working on moving our site to Node I have had some interesting experiences and a lot of fun finding and building new technologies for Zoosk.

Node has a vibrant third party module community but it can be tricky to figure out which module to use for a given piece of functionality you want. Nipster is a great tool as it gives you a rough idea of how popular the different modules are which may indicate how useful/powerful they are and also how much community support you are going to find when you run into issues. Recently I have also been looking at Node Modules which is really interesting because you can allow it to look at your Github profile and it will factor in people you follow, things you have starred, etc in giving you search results for new modules.

One thing that we looked at straight off the bat was templating languages. By default you are probably going to end up with Jade as a templating language which is great, but as much as possible we wanted to look at each third party technology where we had a choice and try to make an informed decision on what to use. We ended up going with Dust because of some of its powerful streaming abilities that let you do things like begin rendering out chunks of the page and then going back and filling in the rest when your api calls return.

Once I started writing code to run under Node I started really enjoying the power of the event driven model as a tool to deal with the asynchronous nature of web requests, both incoming and outgoing. There is something very satisfying to me about writing some concise and very readable code along the lines of:

apiService.checkUserAuth(authToken).then(
    function (response){/*success handler*/},
    function (error){/*error handler*/}
);

Incidentally, we are using Q for promises which really adds a lot to our codebase.

As opposed to PHP, which creates a new thread for each request and then waits on input/output, with Node we are a lot more resource efficient and one Node server can handle a lot more inbound requests than one PHP server in the way that we use them. However there are tradeoffs with this strategy and an interesting one is memory leaks.

While memory leaks are never a good thing, if some PHP code is leaking memory it’s generally not the end of the world as each instance is started, possibly leaks memory, and then is shut down and the memory is returned to the system. One of the hardest things I have had to do so far with Node is to try to track down a memory leak that we were having that was slowly but steadily consuming more memory on every request. After finding that memory leak I wish I had some magic advice that would help people track down future memory leaks but instead I just have a few tips and rules of thumb.

What finally allowed us to track down the bug was a bunch of extra logging everywhere that looked at process.memoryUsage().rss which is how much of the processes’ memory is in RAM. With this type of logging sprinkled liberally around the codebase we could see how much the memory usage was going up at various places in the code as well as how much per request so we could try to identify any possible pattern that had to do with the request type. Another tool that didn’t end up factoring into this leak but that I think is really important to know about is Chrome’s developer tools. Node-inspector is awesome and is basically the “Sources” tab from Chrome’s dev tools which lets you set breakpoints, step through code, and evaluate variables directly in its console. The other tool is Leak finder for javascript which allows you to take heap snapshots of your running Node process and then load them up in Chrome to inspect them directly or compare and contrast snapshots which can help you figure out what object you are leaking. In our case it ended up being a bug in an old version of the https client module that comes with Node that caused it to leak memory on every outbound request!

Another thing that I have come to appreciate in the Node world is the ease of using an inversion of control pattern while developing and hooking together your own and third party modules. With all these third party modules available and with the prospect of being able to reuse pieces of code in different places inside the company its good to make things very modular. I recently wrote a client to access one of our new, internal only, api service layers which we call Zia. While writing this client I needed an http client that dealt with promises which I didn’t want to write as another engineer who always produces extremely strong code had already ported Angular’s $http service to our Node setup and plugged in promises from Q. I was able to write my code so that all the components can be plugged into each other at run time so that if we someday want to switch to a different http provider or promise provider it would be very easy:

require(‘zia-client’)
    .withConfig(config.ziaConfig)
    .withHttpBackend(httpBackend)
    .withPromiseProvider(q)
    .addToRequest(app.request);
 

Internally the significant portion of zia-client looks like this:

ZiaServices.prototype.addToRequest = function(req) {
    var config = this.config,
        promiseProvider = this.promiseProvider_ || httpPromiseProvider,
        anHttpBackend = this.httpBackend_ || httpBackend;
 
    req.__defineGetter__(‘ziaHttp’, function() {
        return this._ziaHttp || (this._ziaHttp = ziaHttpFactory(config, promiseProvider(), anHttpBackend()));
    });
}

You can see that either a promise provider is passed in as in the example above with the .withHttpBackend() call or there is a default one that comes with my zia-client package in the case that the user does not want to provide one.

In short I really like Node.js and I am looking forward to seeing where it goes in the future!