CodingNic

Process Management & Reliability

The Cluster Module

Process Management & Reliability 15 min read

The Cluster Module

Objectives

By the end of this lesson, you should be able to:

  • Explain what Node’s cluster module does, and why it exists
  • Fork multiple worker processes from a single primary process
  • Confirm requests are actually distributed across workers

💡 Why this matters: Node.js runs JavaScript on a single thread, one process uses one CPU core, no matter how many a machine actually has. The cluster module, built into Node itself, runs multiple independent processes, using every core available, and, as a side effect, means one worker crashing doesn’t take the others with it.

⚠️ A note on verification: every snippet and every output in this lesson was actually run.

Forking Workers

javascript
const cluster = require('cluster');
const http = require('http');

if (cluster.isPrimary) {
  console.log(`Primary ${process.pid} is running`);
  const numWorkers = 2;
  for (let i = 0; i < numWorkers; i++) cluster.fork();

  cluster.on('exit', (worker) => {
    console.log(`Worker ${worker.process.pid} died`);
  });
} else {
  http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ pid: process.pid }));
  }).listen(5000);
  console.log(`Worker ${process.pid} started`);
}

cluster.isPrimary is true in the original process, false inside each forked worker, the same file runs both roles, branching on which one it currently is. The primary’s job is only to fork workers and watch for them exiting, each worker runs the actual server, all of them listening on the same port, Node’s cluster module handles routing incoming connections to whichever worker picks them up.

Requests Distributed Across Workers

text
Primary 7 is running
Worker 14 started
Worker 15 started
Request 1 handled by pid 14
Request 2 handled by pid 15
Request 3 handled by pid 14
Request 4 handled by pid 15

Four requests to the exact same URL, handled by two different process ids, alternating, this is the cluster module’s built-in load distribution, an application using two workers can handle roughly twice the concurrent load of one, on a machine with at least two CPU cores.

What Happens When a Worker Dies

text
Worker 14 died
Worker 15 died

The cluster.on('exit', ...) handler fires whenever a worker’s process exits, for any reason, including the exact kind of uncaught exception from the last lesson. In a real deployment, this handler would call cluster.fork() again immediately, replacing the dead worker, the other worker keeps serving requests the entire time, the crash of one worker no longer means the whole application is down, exactly the gap Lesson 1 identified.

How Many Workers

A common starting point is one worker per CPU core, available as require('os').cpus().length, matching the number of workers to the hardware actually available, more workers than cores doesn’t add real parallelism, since there’s no additional CPU to run them on.

Try It

  1. Build the primary/worker file above, and confirm requests are distributed across at least two different worker process ids.
  2. Add a cluster.on('exit', ...) handler that calls cluster.fork() again, replacing a dead worker automatically, then kill one worker’s process directly and confirm a new one starts.
  3. Use require('os').cpus().length instead of a hardcoded worker count, and explain, in one or two sentences, why matching worker count to CPU count makes sense.
  4. Explain, in your own words, why the cluster module by itself doesn’t solve the graceful shutdown problem, workers still need to finish in-flight requests before actually exiting, covered next.

Recap

  • The cluster module runs multiple worker processes from one primary, using every CPU core a machine has, instead of just one.
  • Requests are distributed across live workers automatically, confirmed here by multiple, alternating process ids handling the same route.
  • A worker crashing no longer takes the whole application down, the primary can detect the exit and fork a replacement.

Next lesson: graceful shutdown, finishing in-flight requests cleanly instead of dropping them when a process needs to stop.