Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ node_js:

notifications:
email:
- sam@tixelated.com
- luislhl@gmail.com
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
### 0.6.3
* Update example.js. Thanks @khalidsalomao

### 0.6.2
* New example and improvements in package.json. Thanks @khalidsalomao

### 0.6.1
* New smoothing factor to be used when lag is falling
* Isolate logic of currentLag in an overwritable function

### 0.5.1
* Set `onLag` default threshould to `maxLag()`. Thanks @flentini

Expand Down
47 changes: 30 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
[![Build Status](https://secure.travis-ci.org/STRML/node-toobusy.png)](http://travis-ci.org/STRML/node-toobusy)
[![Build Status](https://secure.travis-ci.org/luislhl/node-toobusy.png)](http://travis-ci.org/luislhl/node-toobusy)
[![npm version](https://badge.fury.io/js/node-toobusy.svg)](https://www.npmjs.com/package/node-toobusy)

# Is Your Node Process Too Busy?

`toobusy-js` is a fork of lloyd's [node-toobusy](http://github.com/lloyd/node-toobusy) that removes native dependencies
`node-toobusy` is a fork of lloyd's [node-toobusy](http://github.com/lloyd/node-toobusy) that removes native dependencies
in favor of using the `unref` introduced in [node 0.9.1](http://blog.nodejs.org/2012/08/28/node-v0-9-1-unstable/).

This package is a simpler install without native dependencies, but requires node >= 0.9.1.
Expand Down Expand Up @@ -31,14 +32,14 @@ and continue serving as many requests as possible.
## installation

```
npm install toobusy-js
npm install node-toobusy
```


## usage

```javascript
var toobusy = require('toobusy-js'),
var toobusy = require('node-toobusy'),
express = require('express');

var app = express();
Expand All @@ -60,25 +61,22 @@ app.get('/', function(req, res) {
});

var server = app.listen(3000);

process.on('SIGINT', function() {
server.close();
// calling .shutdown allows your process to exit normally
toobusy.shutdown();
process.exit();
});
```

## tunable parameters

The library exposes a few knobs:

`maxLag` - This number represents the maximum amount of time in milliseconds that the event queue is behind,
before we consider the process *too busy*.
`interval` - The check interval for measuring event loop lag, in ms.
**maxLag** - This number represents the maximum amount of time in milliseconds that the event queue is behind,
before we consider the process *too busy*.
**interval** - The check interval for measuring event loop lag, in ms.
**smoothingFactor** - When a new lag is measured, we smooth its value using the standard [exponential smoothing formula](https://en.wikipedia.org/wiki/Exponential_smoothing).
There are two factors available, the smoothingFactorOnRise, which is used when the new lag is higher than currentLag, and the smoothingFactorOnFall, which is used when the new lag is lower than currentLag.
It's a good idea to keep the factor on fall higher than on rise, to make the currentLag recover faster after spikes.
**lagFunction** - This is the function used to calculate currentLag. You can overwrite it if you need a different behavior. The parameters passed to it are: `lag`, `currentLag`, `smoothingFactorOnRise` and `smoothingFactorOnFall`.

```javascript
var toobusy = require('toobusy-js');
var toobusy = require('node-toobusy');

// Set maximum lag to an aggressive value.
toobusy.maxLag(10);
Expand All @@ -87,6 +85,21 @@ toobusy.maxLag(10);
// but may cause the check to be too sensitive.
toobusy.interval(250);

// Set smoothing factor on rise to a lower value. This will make it less sensible
// to spikes. Default is 1/3.
toobusy.smoothingFactorOnRise(1/4);

// Set smoothing factor on fall to a higher value. This will make it recover faster
// after spikes. Default is 2/3.
toobusy.smoothingFactorOnFall(3/4);

// You can overwrite this function to change the way currentLag is calculated.
// This is the default implementation.
toobusy.lagFunction = function(lag, cLag, sFactorRise, sFactorFall) {
var factor = lag > cLag ? sFactorRise : sFactorFall;
return factor * lag + (1 - factor) * cLag;
}

// Get current maxLag or interval setting by calling without parameters.
var currentMaxLag = toobusy.maxLag(), interval = toobusy.interval();

Expand All @@ -108,7 +121,7 @@ The default of 70 should get you started.

## Events

As of `0.5.0`, `toobusy-js` exposes an `onLag` method. Pass it a callback to be notified when
As of `0.5.0`, `node-toobusy` exposes an `onLag` method. Pass it a callback to be notified when
a slow event loop tick has been detected.

## references
Expand All @@ -122,4 +135,4 @@ this concept is not new. Here are references to others who apply the same techn

## license

[WTFPL](http://wtfpl.org)
[WTFPL](http://wtfpl.net)
37 changes: 37 additions & 0 deletions examples/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
var toobusy = require('node-toobusy');

// Set maximum lag to an aggressive value.
toobusy.maxLag(10);

// Set check interval to a faster value. This will catch more latency spikes
// but may cause the check to be too sensitive.
toobusy.interval(250);

// Set smoothing factor on rise to a lower value. This will make it less sensible
// to spikes. Default is 1/3.
toobusy.smoothingFactorOnRise(1/4);

// Set smoothing factor on fall to a higher value. This will make it recover faster
// after spikes. Default is 2/3.
toobusy.smoothingFactorOnFall(3/4);

// You can overwrite this function to change the way currentLag is calculated.
// This is the default implementation.
toobusy.lagFunction = function(lag, currentLag, smoothingUp, smoothingDown) {
var factor = lag > currentLag ? smoothingUp : smoothingDown;
return factor * lag + (1 - factor) * currentLag;
}

// Get current maxLag or interval setting by calling without parameters.
var currentMaxLag = toobusy.maxLag(), interval = toobusy.interval();

toobusy.onLag(function(currentLag) {
console.log("Event loop lag detected! Latency: " + currentLag + "ms");
});

// check if node is too busy
if (toobusy()) {
console.warn("TooBusy!");
}

console.log("Current adjusted event lag", toobusy.lag(), "ms");
27 changes: 21 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,38 @@
{
"name": "toobusy-js",
"name": "node-toobusy",
"description": "Don't fall over when your Node.JS server is too busy. Now without native dependencies!",
"homepage": "https://github.com/STRML/node-toobusy",
"version": "0.5.1",
"homepage": "https://github.com/luislhl/node-toobusy#readme",
"version": "0.6.3",
"dependencies": {},
"devDependencies": {
"mocha": "1.7.0",
"pre-commit": "^1.0.5",
"should": "1.2.1"
},
"maintainers": [
"Samuel Reed <sam@tixelated.com>"
"Samuel Reed <sam@tixelated.com>",
"Luis Helder <dev.luislhl@gmail.com>"
],
"license": "WTFPL",
"repository": "STRML/node-toobusy",
"repository": {
"type": "git",
"url": "https://github.com/luislhl/node-toobusy.git"
},
"engines": {
"node": ">=0.9.1"
},
"scripts": {
"prepublishOnly": "npm test",
"test": "mocha tests"
}
},
"keywords": [
"toobusy",
"node-toobusy",
"event",
"loop",
"lag",
"maxlag",
"busy"
],
"tonicExampleFilename": "examples/example.js"
}
64 changes: 45 additions & 19 deletions tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ describe('toobusy()', function() {
});

describe('lag events', function () {
//Sometimes the default 2s timeout is hit on this suite, raise to 10s.
this.timeout(10 * 1000);

it('should not emit lag events if the lag is less than the configured threshold',
testLagEvent(100, 50, false));
it('should emit lag events if the lag is greater than the configured threshold',
Expand Down Expand Up @@ -140,33 +143,31 @@ describe('smoothingFactor', function() {
this.timeout(10 * 1000);

beforeEach(function() {
toobusy.reset();
toobusy.maxLag(10);
toobusy.interval(250);
});
afterEach(function() {
toobusy.maxLag(70);
toobusy.interval(500);
});
it('should default to 1/3', function(done) {
(toobusy.smoothingFactor()).should.equal(1/3);
done();
});
it('should throw an exception for invalid values', function(done) {
(function() { toobusy.smoothingFactor(0); }).should.throw;
(function() { toobusy.smoothingFactor(2); }).should.throw;
(function() { toobusy.smoothingFactor(-1); }).should.throw;
(function() { toobusy.smoothingFactor(1); }).should.not.throw;
done();

setupSmoothingFactorTests({
function: toobusy.smoothingFactorOnRise,
suiteName: 'on rise',
default: 1/3
});
it('should be configurable', function(done) {
(toobusy.smoothingFactor(0.9)).should.equal(0.9);
(toobusy.smoothingFactor(0.1)).should.equal(0.1);
(toobusy.smoothingFactor()).should.equal(0.1);
done();

setupSmoothingFactorTests({
function: toobusy.smoothingFactorOnFall,
suiteName: 'on fall',
default: 1 - 1/3
});
it('should allow no dampening', function(done) {

it('should allow no dampening', function (done) {
var cycles_to_toobusy = 0;
toobusy.smoothingFactor(1); // no dampening
toobusy.smoothingFactorOnRise(1); // no dampening
toobusy.smoothingFactorOnFall(1); // no dampening

function load() {
if (toobusy()) {
Expand All @@ -180,9 +181,9 @@ describe('smoothingFactor', function() {

load();
});
it('should respect larger dampening factors', function(done) {
it('should respect larger dampening factors', function (done) {
var cycles_to_toobusy = 0;
toobusy.smoothingFactor(0.05);
toobusy.smoothingFactorOnRise(0.05);

function load() {
if (toobusy()) {
Expand All @@ -196,8 +197,33 @@ describe('smoothingFactor', function() {

load();
});

});

function setupSmoothingFactorTests(options) {
var smoothingFunc = options.function;

describe(options.suiteName, function() {
it('should default to ' + options.default, function(done) {
(smoothingFunc()).should.equal(options.default);
done();
});
it('should throw an exception for invalid values', function(done) {
(function() { smoothingFunc(0); }).should.throw;
(function() { smoothingFunc(2); }).should.throw;
(function() { smoothingFunc(-1); }).should.throw;
(function() { smoothingFunc(1); }).should.not.throw;
done();
});
it('should be configurable', function(done) {
(smoothingFunc(0.9)).should.equal(0.9);
(smoothingFunc(0.1)).should.equal(0.1);
(smoothingFunc()).should.equal(0.1);
done();
});
});
};

describe('started', function() {
it('should return false after shutdown', function(done) {
toobusy.shutdown();
Expand Down
Loading