diff --git a/.gitignore b/.gitignore index 09a5066..17724e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ /node_modules *~ /examples/node_modules -/build -npm-debug.log +npm-debug.log \ No newline at end of file diff --git a/ChangeLog b/ChangeLog index c9d5f33..3e98f3d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +0.3.0 - + * full JS implementation, removed c code & bindings + * example bugfixes + 0.2.3 - * node.js 0.10.0 support - uprade to latest bindings and include in .travis.yml diff --git a/binding.gyp b/binding.gyp deleted file mode 100644 index 83e219d..0000000 --- a/binding.gyp +++ /dev/null @@ -1,12 +0,0 @@ -{ - 'targets': [ - { - 'target_name': 'toobusy', - 'include_dirs': [ - ], - 'sources': [ - 'toobusy.cc', - ] - } - ] -} diff --git a/examples/package.json b/examples/package.json index d917b0a..bfa9b7f 100644 --- a/examples/package.json +++ b/examples/package.json @@ -4,6 +4,6 @@ "description": "dependencies required by the toobusy examples", "private": true, "dependencies": { - "express": "2" + "express": "3" } } diff --git a/examples/standalone.js b/examples/standalone.js index 79f518f..12aab86 100644 --- a/examples/standalone.js +++ b/examples/standalone.js @@ -1,6 +1,6 @@ // first, we want to be able to get cpu usage stats in terms of percentage var loaded = false; -var toobusy = require('../'); +var toobusy = require('..'); var work = 524288; @@ -8,7 +8,7 @@ function worky() { var howBusy = toobusy(); if (howBusy) { work /= 4; - console.log("I can't work! I'm too busy:", howBusy + "ms behind"); + console.log("I can't work! I'm too busy:", toobusy.lag() + "ms behind"); } work *= 2; for (var i = 0; i < work;) i++; diff --git a/index.js b/index.js index e8e8a92..aa840c2 100644 --- a/index.js +++ b/index.js @@ -1,5 +1 @@ -var bindings = require('bindings')('toobusy.node') -module.exports = bindings.toobusy; -module.exports.shutdown = bindings.shutdown; -module.exports.maxLag = bindings.maxLag; -module.exports.lag = bindings.lag; +module.exports = require('./toobusy'); \ No newline at end of file diff --git a/package.json b/package.json index e72dd0a..de72baa 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,8 @@ "name": "toobusy", "description": "Don't fall over when your Node.JS server is too busy.", "homepage": "https://github.com/lloyd/node-toobusy", - "version": "0.2.2", + "version": "0.3.0", "dependencies": { - "bindings": "1.1.0" }, "devDependencies": { "should": "1.2.1", @@ -32,7 +31,7 @@ ], "main": "./index.js", "engines": { - "node": ">=0.8.0" + "node": ">=0.9.1" }, "scripts": { "test": "mocha tests" diff --git a/toobusy.cc b/toobusy.cc deleted file mode 100644 index 3a575c1..0000000 --- a/toobusy.cc +++ /dev/null @@ -1,99 +0,0 @@ -#include -#include -#include -#include -#if defined(_WIN32) - #include -#else - #include -#endif - -using namespace v8; - -static const unsigned int POLL_PERIOD_MS = 500; -static unsigned int HIGH_WATER_MARK_MS = 70; -// A dampening factor. When determining average calls per second or -// current lag, we weigh the current value against the previous value 2:1 -// to smooth spikes. -static const unsigned int AVG_DECAY_FACTOR = 3; - -//static uv_idle_t s_idler; -static uv_timer_t s_timer; -static uint32_t s_currentLag; -static uint64_t s_lastMark; - -Handle TooBusy(const Arguments& args) { - // No HandleScope required, because this function allocates no - // v8 classes that reside on the heap. - bool block = false; - if (s_currentLag > HIGH_WATER_MARK_MS) { - // probabilistically block requests proportional to how - // far behind we are. - double pctToBlock = ((s_currentLag - HIGH_WATER_MARK_MS) / - (double) HIGH_WATER_MARK_MS) * 100.0; - double r = (rand() / (double) RAND_MAX) * 100.0; - if (r < pctToBlock) block = true; - } - return block ? True() : False(); -} - -Handle ShutDown(const Arguments& args) { - // No HandleScope required, because this function allocates no - // v8 classes that reside on the heap. - - uv_timer_stop(&s_timer); - return Undefined(); -} - -Handle Lag(const Arguments& args) { - HandleScope scope; - return scope.Close(Integer::New(s_currentLag)); -} - -Handle HighWaterMark(const Arguments& args) { - HandleScope scope; - - if (args.Length() >= 1) { - if (!args[0]->IsNumber()) { - return v8::ThrowException( - v8::Exception::Error( - v8::String::New("expected numeric first argument"))); - } - int hwm = args[0]->Int32Value(); - if (hwm < 10) { - return v8::ThrowException( - v8::Exception::Error( - v8::String::New("maximum lag should be greater than 10ms"))); - } - HIGH_WATER_MARK_MS = hwm; - } - - return scope.Close(Number::New(HIGH_WATER_MARK_MS)); -} - -static void every_second(uv_timer_t* handle, int status) -{ - uint64_t now = uv_hrtime(); - - if (s_lastMark > 0) { - // keep track of (dampened) average lag. - uint32_t lag = (uint32_t) ((now - s_lastMark) / 1000000); - lag = (lag < POLL_PERIOD_MS) ? 0 : lag - POLL_PERIOD_MS; - s_currentLag = (lag + (s_currentLag * (AVG_DECAY_FACTOR-1))) / - AVG_DECAY_FACTOR; - } - s_lastMark = now; -}; - -extern "C" void init(Handle target) { - HandleScope scope; - - target->Set(String::New("toobusy"), FunctionTemplate::New(TooBusy)->GetFunction()); - target->Set(String::New("shutdown"), FunctionTemplate::New(ShutDown)->GetFunction()); - target->Set(String::New("lag"), FunctionTemplate::New(Lag)->GetFunction()); - target->Set(String::New("maxLag"), FunctionTemplate::New(HighWaterMark)->GetFunction()); - uv_timer_init(uv_default_loop(), &s_timer); - uv_timer_start(&s_timer, every_second, POLL_PERIOD_MS, POLL_PERIOD_MS); -}; - -NODE_MODULE(toobusy, init); diff --git a/toobusy.js b/toobusy.js new file mode 100644 index 0000000..5803e67 --- /dev/null +++ b/toobusy.js @@ -0,0 +1,54 @@ +var STANDARD_HIGHWATER = 70; +var STANDARD_INTERVAL = 500; + +// A dampening factor. When determining average calls per second or +// current lag, we weigh the current value against the previous value 2:1 +// to smooth spikes. +var AVG_DECAY_FACTOR = 3; + +var lastTime = new Date().valueOf(), now, lag, highWater = STANDARD_HIGHWATER, interval = STANDARD_INTERVAL, currentLag = 0; + +var checkInterval = setInterval(function(){ + now = new Date().valueOf(); + lag = now - lastTime; + lag = (lag < interval) ? 0 : lag - interval; + currentLag = (lag + (currentLag * (AVG_DECAY_FACTOR - 1))) / AVG_DECAY_FACTOR; + lastTime = now; +}, interval); + +// Don't keep process open just for this timer. +checkInterval.unref(); + +var toobusy = function(){ + // If current lag is < 2x the highwater mark, we don't always call it 'too busy'. E.g. with a 50ms lag + // and a 40ms highWater (1.25x highWater), 25% of the time we will block. With 80ms lag and a 40ms highWater, + // we will always block. + var pctToBlock = (currentLag - highWater) / highWater; + var rand = Math.random(); + return rand < pctToBlock; +}; + +toobusy.lag = function(){ + return parseInt(currentLag, 10); +}; + +toobusy.maxLag = function(newLag){ + if(!newLag) return highWater; + + // If an arg was passed, try to set highWater. + if(Object.prototype.toString.call(newLag) !== "[object Number]"){ + throw "Expected numeric first argument."; + } + newLag = parseInt(newLag, 10); + if(newLag < 10){ + throw "Maximum lag should be greater than 10ms."; + } + highWater = newLag; + return highWater; +}; + +toobusy.shutdown = function(){ + clearInterval(checkInterval); +}; + +module.exports = toobusy; \ No newline at end of file