Skip to content

Commit

Permalink
Add new uptime claculator
Browse files Browse the repository at this point in the history
  • Loading branch information
fzaninotto committed Sep 21, 2012
1 parent 0a397ad commit 9eb3beb
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 0 deletions.
65 changes: 65 additions & 0 deletions lib/uptimeCalculator.js
@@ -0,0 +1,65 @@
var Ping = require('../models/ping');
var CheckEvent = require('../models/checkEvent');

var UptimeCalculator = function(check) {
this.check = check;
}

UptimeCalculator.prototype.getUptimePeriods = function(begin, end, callback) {
var self = this;
this.getPingBeforeTime(begin, function(err1, ping) {
if (err1) return callback(err1);
if (!ping) return callback(null, []);
CheckEvent.find()
.where('check').equals(self.check)
.where('timestamp').gte(begin).lte(end)
.sort({ timestamp: 1 })
.find(function(err2, checkEvents) {
if (err2) return callback(err2);
var periods = [];
var isUp = ping.isUp; // initial state
var currentPeriod = isUp ? [begin] : [];
checkEvents.forEach(function(checkEvent) {
switch (checkEvent.message) {
case 'up':
if (isUp) break; // check passes up while it was already up: ignore
// beginning of an uptime period
currentPeriod.push(checkEvent.timestamp.getTime());
isUp = true;
break;
case 'down':
if (!isUp) break; // check passes down while it was already down: ignore
// end of an uptime period
currentPeriod.push(checkEvent.timestamp.getTime());
periods.push(currentPeriod);
currentPeriod = [];
isUp = false;
break;
}
})
if (isUp) {
// check was up until the end
currentPeriod.push(end);
periods.push(currentPeriod);
};
callback(null, periods);
});
});
}

/**
* Get the last ping preceding a given timestamp
*
* This determines the state of a check at a given time.
*
* @api public
*/
UptimeCalculator.prototype.getPingBeforeTime = function(timestamp, callback) {
Ping.find()
.where('check').equals(this.check)
.where('timestamp').lte(timestamp)
.sort({ timestamp: -1 })
.findOne(callback);
}

module.exports = UptimeCalculator;
4 changes: 4 additions & 0 deletions makefile
@@ -0,0 +1,4 @@
test:
NODE_ENV=test mocha test/*/*.js

.PHONY: test
3 changes: 3 additions & 0 deletions package.json
Expand Up @@ -14,6 +14,9 @@
"async": "0.1.22",
"socket.io": "0.9.10"
},
"devDependencies": {
"should": "1.1.0"
},
"keywords": ["uptime", "monitoring", "api", "check"],
"repository": "https://github.com/fzaninotto/uptime",
"license": "MIT",
Expand Down
117 changes: 117 additions & 0 deletions test/lib/test_uptimeCalculator.js
@@ -0,0 +1,117 @@
var should = require('should');
var async = require('async');
var mongoose = require('../../bootstrap');
var UptimeCalculator = require('../../lib/uptimeCalculator');
var Check = require('../../models/check');
var CheckEvent = require('../../models/checkEvent');
var Ping = require('../../models/ping');

var check1, check2, now; // fixtures

describe('uptimeCalculator', function() {

before(function(done) {
async.parallel([
function(cb) { Ping.collection.drop(cb) },
function(cb) { Check.collection.drop(cb) },
function(cb) { CheckEvent.collection.drop(cb) },
], done);
});

before(function() {
now = Date.now();
});

before(function(done) {
check1 = new Check();
check1.save(function(err) {
if (err) throw (err);
async.series([
function(cb) { Ping.createForCheck(false, now - 3000, 100, check1, 'dummy1', '', cb); },
function(cb) { Ping.createForCheck(false, now - 2000, 100, check1, 'dummy2', '', cb); },
function(cb) { Ping.createForCheck(true, now - 1000, 100, check1, 'dummy3', '', cb); },
function(cb) { Ping.createForCheck(true, now, 100, check1, 'dummy4', '', cb); },
function(cb) { Ping.createForCheck(true, now + 1000, 100, check1, 'dummy5', '', cb); },
function(cb) { Ping.createForCheck(false, now + 2000, 100, check1, 'dummy6', '', cb); },
function(cb) { Ping.createForCheck(true, now + 3000, 100, check1, 'dummy7', '', cb); },
], done);
});
});

before(function(done) {
check2 = new Check();
check2.save(done);
});

describe('#getPingBeforeTime', function() {

it('should return nothing for new Checks', function(done) {
var calculator = new UptimeCalculator(check2);
calculator.getPingBeforeTime(now, function(err, ping) {
if (err) throw (err);
should.not.exist(ping);
done();
});
});

it('should return the latest ping', function(done) {
var calculator = new UptimeCalculator(check1);
calculator.getPingBeforeTime(now, function(err, ping) {
if (err) throw (err);
ping.monitorName.should.eql('dummy4');
done();
});
});

});

describe('#getUptimePeriods', function() {

it('should return empty array when there is no ping', function(done) {
var calculator = new UptimeCalculator(check2);
calculator.getUptimePeriods(Date.now(), Date.now() + 1000, function(err, periods) {
if (err) throw (err);
periods.should.eql([]);
done();
});
});

it('should return the correct period for not finished check', function(done) {
var calculator = new UptimeCalculator(check1);
calculator.getUptimePeriods(now + 3000, now + 6000, function(err, periods) {
if (err) throw (err);
periods.should.eql([ [now + 3000, now + 6000] ]);
done();
});
});

it('should return the correct period for not started check', function(done) {
var calculator = new UptimeCalculator(check1);
calculator.getUptimePeriods(now - 6000, now - 3000, function(err, periods) {
if (err) throw (err);
periods.should.eql([ ]);
done();
});
});

it('should return the correct period for crossing checks', function(done) {
var calculator = new UptimeCalculator(check1);
calculator.getUptimePeriods(now - 6000, now, function(err, periods) {
if (err) throw (err);
periods.should.eql([ [ now - 1000, now ]]);
done();
});
});

it('should return the correct periods', function(done) {
var calculator = new UptimeCalculator(check1);
calculator.getUptimePeriods(now - 3000, now + 3000, function(err, periods) {
if (err) throw (err);
periods.should.eql([ [now - 1000, now + 2000], [now + 3000, now + 3000] ]);
done();
});
});

});

});
2 changes: 2 additions & 0 deletions test/mocha.opts
@@ -0,0 +1,2 @@
--reporter spec
--slow 20ms

0 comments on commit 9eb3beb

Please sign in to comment.