Understanding Exceptions and Domains in Node.js

Exceptions in Node.js and their effect

If you have developed applications using node.js then you must have encountered the problem of application getting crashed whenever an error occurs.

     User.find({'username':'Suroor Wijdan'}, function(err, user){
         if(err) throw err;
         console.log(user);
     });

The above code once run if throws an error then this will lead to the process to exit. And as Node.js runs on a single process, it results in bringing down the whole application.

Exception Handling prior to Domains

Before Domains came into existence, the one and only way to catch expceptions was to listen for uncaughtException like below:

    process.on('uncaughtException', function(err){
        console.log(err);
    });

But this is generally considered bad, as it silently swallows the exception leaving behind an open connection to client waiting for the response. This inturn leads to resource leak but this is what most people were doing before domains.

One other alternative would be to, let the application crash and then bring it up again using something like forever. But this doesn't solve the overall purpose of handling the expections.

What can we do with Domains?

Domains were added in the Node v.0.8 and are still marked as Unstable on the official documention page.

Domains provide us a way to act on error events for all the event emitters and callbacks that are bound to a specific domain. All the errors propogate to that domain without losing the context of the error. Lets see a simple code snippet:

     var http = require('http');
     var domain = require('domain').create();

     domain.on('error', function(err){
         console.log(err); //What else we can do here?
     });

     domain.run(function(){
         http.createServer(function(req, res){
            throw new Error('Server encountered a bizzare error!');
             req.end('ok')
         }).listen(9091);
     });

In the above code snippet, we have created a domain and started an http server listening to requests in the scope of that domain. We have also placed an error handler on the domain to listen for error events. Once we try to access the server, the error thrown from the server propagtes to the bound domain and executes the error handler.

The above example is a very bad idea of using domains, this makes it somewhat same as the uncaughtException thing. So whats the correct way of using domains? The example given on the documentation page of the Domain provides us with a very nice example in the context of clusters. I am gonna make it more simpler with this example below:

    var http = require('http');
    var domain = require('domain');

    http.createServer(function(req, res){
        var serverDomain = domain.create();

        serverDomain.on('error', function(err){
            console.log('Domain:', err.domain);
            res.writeHead(500);
            res.end(err.message);            
        });
        domain.add(req);
        domain.add(res);        
        serverDomain.run(function(){
            requestHandler(req, res);
        });
    }).listen(9091);

In the above example, we have created and bound a domain on each of the req and res. As req and res event emitter objects were created before the domain existed, we need to bind them to the domain explicitly.

requestHandler is a general function for handling requests and is not of concern in this example. The only thing we need to see is that if any of the event emitters or callbacks associated with the domain throw an error, it will be handled by the domain and the connection to the client will be closed leaving no open connections.

Domain middleware for Express

If you are using Express framework for your application then, you can use the following middleware to add a domain for each of your request and response.

    var domain = require('domain');
    app.use(function(req, res, next){
        var reqDomain = domain.create();
        reqDomain.add(req);
        reqDomain.add(res);
        reqDomain.on('error', next);
        reqDomain.run(next);
    });

Domains also act on errors thrown asynchronously. Also considering the above example, we can use different Domains to be bound to different sub-sets of the application logic so we can handle the errors thrown differently.

There is a lot more to domains, Domains enable us to intercept callbacks. We will look into it in another post.

Let me know of any sugesstions for this post.

comments powered by Disqus