Lessons from Node.js

Node.js is a server-side Javascript library for writing scalable network servers. It’s primary purpose is to wrap an asynchronous library around synchronous low-level calls. Code written to block on IO wastes time waiting for a response.

var response = synchCall(parameters) ;
useResponse(response);

The only way to get work done with this code is to have another thread ready to run. Apparently, Apache handles this by creating a new thread per connection, which can be very expensive. Each thread takes 2MB (though this can be reduced, right?) and context switching between threads is relatively expensive. So it doesn’t appear to scale very well, although, this is the worst possible implementation and can be improved with threadpools and/or green threads. An alternative it to switch to asynchronous calls.

asynchCall(parameters,
           function(response) { useResponse(response); }
          );

The callback is stored somewhere, ready to execute whenever the asynchCall returns. This requires that every blocking call be rewritten in this asynch style. To me, this looks like continuation-passing style (CPS), which is a transformation performed automatically by many Scheme compilers. The callback technique here has to be used carefully, i.e. not recursively, to avoid blowing the stack. With continuations you could use this style and not use a single excess stack frame. It would just quickly jump directly into each callback.

It would be interesting to see if a Scheme runtime can be written such that all IO continuations are implicitly asynchronous. I think you’ll need a parallel let to start multiple events; otherwise, it’s still just a single-threaded program. Maybe the signature of these IO functions would something like “read (kontinuation, koncurrent)”. The first continuation is registered to execute when the read finally gets a character from IO. The second continuation is executed immediately as a separate “green thread” of execution. Maybe I can integrate the C wrapper library written for Node.js into a Scheme interpreter like Scheme48. Then I can use scsh, the Scheme shell, to write scripts for scalable servers. I’ll have to think about the semantics of that parallel let thingie though.

Leave a comment