Recently by Jeff Kunkle
Rising from non-existence three short years ago, Node.js is already attracting the accolades and disdain enjoyed and endured by the Ruby and Rails community just a short time ago. It overtook Rails as the most popular Github repository last November (now superseded by Twitter's Bootstrap project) and was selected by InfoWorld for the Technology of the Year Award in 2012.
If you've never used Node.js before or have limited experience, it may not be obvious why people are so excited about it. This post will attempt to explain the basic theory central to Node's approach, arming you with a better frame of reference for the debates you'll undoubtedly encounter.
Please note that this post won't make much sense without a very basic familiarity with Node. Consider checking out the Node.js website and reading a few articles on Node first if that's the case.
I/O Latency
The core premise behind Node's approach is that I/O operations are slow compared to the computation done in your application. Consider the table below, taken from Ryan Dahl's 2008.11.08 presentation on Node.js. It shows various I/O operations on the left and the number of CPU cycles it takes to perform them. Don't focus on the actual numbers, but on the differences in magnitude for the operations.
| Operation | CPU cycles |
|---|---|
| L1 | 3 |
| L2 | 14 |
| RAM | 250 |
| Disk | 41,000,000 |
| Network | 240,000,000 |
From the table you can see that disk and network access times dwarf things like memory access or L1 and L2 cache access. The chart below makes the magnitude differences even more obvious. The L1, L2, and RAM access times are so much smaller than disk and network access that their bars don't even appear on the graph.

Waiting
If you can buy that I/O operations are often orders of magnitude slower than the computation you're performing in your app, then what is your app doing during the I/O operations? It's waiting! It's execution is literally blocked until the I/O operation completes.
Consider the following fictitious web request. The slim green bars represent the time your application devotes to processing and the gray bars represent the time spent waiting for I/O to complete. The example starts off with some logic to process the request.
- Parse the request and invoke the appropriate controller logic
- Initiate a database query or maybe a request to an external service
- Wait for I/O
- Process the query results and write some data to a log file
- Wait for I/O
- Perform some final formatting of the results and return them to the client

The vast majority of time spent during the request involved waiting for the I/O operations, and there were only two. Very little time is actually used to perform application-specific processing. Node.js was designed from the start to exploit this imbalance.
Scaling
Before we get into the approach Node.js takes to scaling, let's consider how our made-up web request would be scaled with other models.
Scaling with Threads
Using a thread based model, you'd scale the above example by creating multiple threads, one for each concurrent connection. The diagram below depicts a threaded model capable of handling four concurrent connections.

While this approach allows us to scale by adding more threads, each thread still spends most of its time waiting for I/O, not processing your application logic. Unfortunately, continuing to add threads introduces context switching overhead and uses considerable memory to maintain execution stacks.
Scaling with Processes
Another popular approach to scaling your application is to run multiple processes. As you can see from the diagram below, the theory behind scaling with multiple processes is basically the same as scaling with threads, although it does use more memory. Like the threading model, each process still spends most of its time waiting on I/O.

Scaling with Node.js
Since the code you write for Node.js executes in a single thread within a single process, it takes a different approach to scaling. It extracts the "I/O waiting" by using an internal thread pool or leveraging asynchronous I/O APIs of the host operating system to free your thread for processing other connections. Instead of your code blocking on I/O operations, it's freed to process other connections. When the I/O operation completes, your code is called back to handle the results.

With Node.js your code never blocks for I/O operations, eliminating those long gray bars of waiting time. This non-blocking mode of operation is what allows Node.js to handle large numbers of concurrent connections without overly-straining system resources.
Conclusion
The success of Node.js relies on the premise that time spent waiting for I/O far outweighs the time spent executing application logic. While that's true of many of todays' web/network-based applications, it doesn't always apply. If your application is more CPU-intensive with minimal I/O by comparison, then Node.js is probably not the right platform. As my father used to say, don't try to fit a square peg in a round hole.
If you've used Node.js lately you've likely run across Vows, the asynchronous behavior driven development test framework for Node. Vows does a really nice job of forcing you to separate your setup logic from your assertions, but that's not what this post is about.
Vows can generate test coverage reports with its --cover-plain, --cover-html, and --cover-json options. Unfortunately, creating code coverage metrics isn't as straightforward as simply including these options in your call to vows. You need to
- Pre-instrument your code using node-jscoverage.
- Require the instrumented versions of your code from your tests.
The first prerequisite is fairly easy to satisfy, although you need to remember to re-run node-jscoverage any time you change the source files you're interested in covering. Setting up a script using something like watchr to re-instrument your files if anything changes might be a good option. Anyway, simply executing the following (assuming you've installed node-jscoverage and added it to your path) from the root of your node project will create the instrumented versions of the files in your lib directory:
node-jscoverage lib lib-cov
The second prerequisite is not as straightforward to resolve as you might expect unless you want to generate the code coverage on every test run. Why? Because requiring the instrumented code from your test is not the same as requiring the un-instrumented versions. Consider
// test/myfile_test.js
var fut = require('../lib/myfile.js'); // regular version
var fut = require('../lib-cov/myfile.js'); // instrumented version
Since the statement requiring the file under test is in every test you write, having to change it when you want to run the instrumented or un-instrumented files is, in my opinion, not an option. So, on my current project we wrote the following javascript to to serve as a replacement to requiring your files under test.
// test/coverage.js
var covererageOn = process.argv.some(function(arg) {
return /^--cover/.test(arg);
});
if (covererageOn) {
console.log('Code coverage on');
exports.require = function(path) {
var instrumentedPath = path.replace('/lib', '/lib-cov');
try {
require.resolve(instrumentedPath);
return require(instrumentedPath);
} catch (e) {
console.log('Coverage on, but no instrumented file found at '
+ instrumentedPath);
return require(path);
}
}
} else {
console.log('Code coverage off');
exports.require = require;
}
In essence, the script checks the process arguments to see if one of the vows --cover-* arguments were passed and changes its require method to substitute /lib-cov for any /lib references. If none of the coverage arguments are detected it simply requires the file as usual. Since your files under test normally use relative references to other files in your lib directory, they get covered too. Check out the example below to see how you would use the script in a test.
// test/mytest.js
var vows = require('vows');
var assert = require('assert');
var coverage = require('coverage');
var fileUnderTest = coverage.require('../lib/myfile.js');
vows.describe('your test').addBatch({
// do some testing
}).export(module);
On my current project we've been using Node.js for an app that does a lot of packet capture and processing. In the past we used node-pcap for packet capture but were looking for an easier way to simply parse raw pcap files. It turns out the pcap file format is pretty simple as was writing a node module to parse it.
The module, called pcap-parser, can be used to parse any pcap file or readable pcap stream, such as the piped output of tcpdump. As packets are parsed, pcap-parser emits relevant events to which your node program can listen. It's a really simple way to process a theoretically infinite stream of pcap data. The code below shows a basic example of it in action. Please check out the project page for more details.
var pcapp = require('pcap-parser');
var parser = new pcapp.Parser('/path/to/file.pcap');
// var parser = new pcapp.Parser(process.stdin);
parser.on('packet', function(packet) {
console.log(packet.header);
console.log(packet.data);
});
parser.parse(); // to kick things off
System Metrics is a new Rails 3 Engine providing a clean web interface to the performance metrics instrumented with ActiveSupport::Notifications. It will collect and display the notifications baked into Rails and any additional custom ones you add using ActiveSupport::Notification#instrument.
System Metrics is not intended to be a replacement for performance monitoring solutions such as New Relic. However, it is especially handy for quickly identifying performance problems in a development environment. It's also a great alternative for private networks disconnected from the Internet.
You can find more information about System Metrics on the System Metrics site. Please kick the tires and let us know what you think.

People often have a hard time determining if they're done with a software development task. It's a great idea to sit down with your team and together decide what you're going to consider as criteria for declaring a task done. If you haven't done that yet, here's one question you can ask yourself to help decide if you're done.
Is there any reason I'll need to think about this code again in the near future?
If you didn't quite implement all the unit tests you think you should have, the answer is yes and you're not done. If you cut a corner or two to squeeze it in before the end of the iteration, the answer is yes and you're not done. If you're uncomfortable with the implementation and would like to refactor it, the answer is yes and you're not done. If there are a few details on the UI you left out in the interest of time, the answer is yes and you're not done.
Too many times I've seen people close out a task only to revisit it the next week to polish up some loose ends, write a few more tests, refactor a confusing piece, etc. Don't do it.
Software development is hard enough without having to keep mental notes of all the needed tweaks and adjustments to things you've already done. Your goal should be to knock out a task so totally and completely that you can get it out of your brain and focus on the next one. Doing so will do wonders for your sense of accomplishment.

