Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Streaming / Piping JSON.stringify output in Node.js / Express

Tags:

node.js

I have a scenario where I need to return a very large object, converted to a JSON string, from my Node.js/Express RESTful API.

res.end(JSON.stringify(obj));

However, this does not appear to scale well. Specifically, it works great on my testing machine with 1-2 clients connecting, but I suspect that this operation may be killing the CPU & memory usage when many clients are requesting large JSON objects simultaneously.

I've poked around looking for an async JSON library, but the only one I found seems to have an issue (specifically, I get a [RangeError]). Not only that, but it returns the string in one big chunk (eg, the callback is called once with the entire string, meaning memory footprint is not decreased).

What I really want is a completely asynchronous piping/streaming version of the JSON.stringify function, such that it writes the data as it is packed directly into the stream... thus saving me both memory footprint, and also from consuming the CPU in a synchronous fashion.

like image 233
Zane Claes Avatar asked Nov 21 '12 23:11

Zane Claes


2 Answers

Ideally, you should stream your data as you have it and not buffer everything into one large object. If you cant't change this, then you need to break stringify into smaller units and allow main event loop to process other events using setImmediate. Example code (I'll assume main object has lots of top level properties and use them to split work):

function sendObject(obj, stream) {
    var keys = Object.keys(obj);
    function sendSubObj() {
       setImmediate(function(){
          var key = keys.shift();
          stream.write('"' + key + '":' + JSON.stringify(obj[key]));
          if (keys.length > 0) {
            stream.write(',');
            sendSubObj();
          } else {
            stream.write('}');
          }
       });
    })
    stream.write('{');
    sendSubObj();
} 
like image 105
Andrey Sidorov Avatar answered Nov 15 '22 21:11

Andrey Sidorov


It sounds like you want Dominic Tarr's JSONStream. Obviously, there is some assembly required to merge this with express.

However, if you are maxing out the CPU attempting to serialize (Stringify) an object, then splitting that work into chunks may not really solve the problem. Streaming may reduce the memory footprint, but won't reduce the total amount of "work" required.

like image 43
Gates VP Avatar answered Nov 15 '22 21:11

Gates VP