Will Shaver
Will Shaver

Reputation: 13071

JSON string directly from node mongodb

I've been doing a series of load tests on a simple server to try and determine what is negatively impacting the load on my much more complicated node/express/mongodb app. One of the things that consistently comes up is string manipulation require for converting an in-memory object to JSON in the express response.

The amount of data that I'm pulling from mongodb via node and sending over the wire is ~200/300 KB uncompressed. (Gzip will turn this into 28k which is much better.)

Is there a way to have the native nodejs mongodb driver stringify the results for me? Right now for each request with the standard .toArray() we're doing the following:

  1. Query the database, finding the results and transferring them to the node native driver
  2. Native driver then turns them into an in-memory javascript object
  3. My code then passes that in-memory object to express
  4. Express then converts it to a string for node's http response.send using JSON.stringify() (I read the source Luke.)

I'm looking to get the stringify work done at a c++/native layer so that it doesn't add processing time to my event loop. Any suggestions?

Edit 1:

It IS a proven bottleneck.

There may easily be other things that can be optimized, but here's what the load tests are showing.

We're hitting the same web sever with 500 requests over a few seconds. With this code:

app.get("/api/blocks", function(req, res, next){
    db.collection('items').find().limit(20).toArray(function(err, items){
        if(err){
            return next(err);
        }
        return res.send(200, items);
    });
});

overall mean: 323ms, 820ms for 95th%

If instead I swap out the json data:

var cached = "[{... "; //giant json blob that is a copy+paste of the response in above code.
app.get("/api/blocks", function(req, res, next){
    db.collection('items').find().limit(20).toArray(function(err, items){
        if(err){
            return next(err);
        }
        return res.send(200, cached);
    });
});

mean is 164 ms, 580 for 95th%

Now you might say, "Gosh Will a mean of 323ms is great, what's your problem?" My problem is that this is an example in which stringify is causes a doubling of the response time.

From my testing I can also tell you these useful things:

Update 2:

Using a profiling tool: https://github.com/baryshev/look This is while hitting my production code on the same database intensive process over and over. The request includes a mongodb aggregate and sends back ~380KB data (uncompressed).

premature optimization my foot!

That function is very small and includes the var body = JSON.stringify(obj, replacer, spaces); line.

Upvotes: 1

Views: 2354

Answers (1)

djechlin
djechlin

Reputation: 60748

It sounds like you should just stream directly from Mongo to Express.

Per this question that asks exactly this:

cursor.stream().pipe(JSONStream.stringify()).pipe(res);

Upvotes: 2

Related Questions