flood

Generic performance testing tool.

About

For performance testing, it is generally necessary to configure many machines generating simultaneous load against an endpoint. With servers these days having many CPU cores, a good tool should utilize all cores.

Flood has simplified the process of aggregating test counters across many machines with many CPUs.

Installation

To install the package globally, run:

# npm install flood -g

This will add two commands to your system, flood and flood-watch.

Configuration

Machines that will actually generate the load by spawning worker threads will be running the flood command, passing a single argument with the filepath to a JSON configuration file. This file will look something like this:

{
  "clientPort": 5143,
  "workerModule": "dummy",
  "numWorkers": 0,
  "interval", 1000
}

The process will listen on clientPort for incoming connections. When a connection is received, it spawns a number of threads defined by numWorkers. If numWorkers is positive, that many threads are created. If it is zero, it creates one thread for each CPU (or CPU core). Numbers less-than zero will be added to the number of CPUs to determine the number of threads (e.g. setting -1 when 4 CPU cores are available will spawn 3 threads).

Each worker thread will load workerModule, which must export a start(config, counter) function. The first argument passed in to start is the config object as loaded from the config file, so you can add your own worker configuration directly alongside flood configuration. The second argument is a counter object.

Initially, each worker thread should take any necessary setup and then call counter.initialize(). This will reset the counter to zero and start an interval timer. Every interval milliseconds, the timer will fire and report back how many times the worker thread called counter.increment() or counter.errorIncrement(). The flood process will sum each worker thread's counters.

The flood process's counter sum is reported to the connected socket. For example, if interval is 1000 milliseconds, there are 4 worker threads, and each worker thread performs exactly 150 counter increments per second, then 600 would echo to the connected socket every second.

The flood process will only generate load while it has a socket connection.

The intention of the flood-watch process is to connect to all the flood processes and aggregate all their counters over a set time. A single argument with the JSON configuration file is passed to flood-watch, which looks something like this:

{
  "clientPort": 5143,
  "clients": [
    "127.0.0.1"
  ],
  "snapshots": 10,
}

It will initiate a socket connection to every client listed in clients on the port given by clientPort. Once a count is received from each client, they are summed and the result is printed to standard output. Once this occurs a number of times given by snapshots, the client socket connections are ended and the process ends.

Example Worker

A worker that simply counts how many times it can increment a counter may look like this:

function ticker(counter) {
  counter.increment();
  process.nextTick(function () {
    ticker(counter);
  });
}

exports.start = function (config, counter) {
  console.log('Initializing simple counter!');
  counter.initialize();
  ticker(counter);
};

More advanced workers simply need to do any initialization before calling counter.initialize() and then call counter.increment() after every iteration of work.