Commit 7c63e442 authored by Alan Szlosek's avatar Alan Szlosek

tweak timeseries percentile alert

parent aefa62a4
Pipeline #11158508 failed with stage
in 1 minute and 13 seconds
/*
1. Our sample metric is called "sample"
2. Every second, calculate the average of the metric values over the past 10 seconds
3. Alert when the average is over 50
4. Populate the metric with random integers every 500ms
*/
var Harbinger = require('../../index');
var log = require('../../lib/log')('examples/timeseries/percentile-alert', true);
var moment = require('moment');
var redis = require('redis');
function getRandomInteger(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
};
var redisClient = redis.createClient(6379, 'localhost');
// Instantiate a timeseries dataset called "sample" that preserves 60 seconds worth of data
var metric = new Harbinger.Timeseries(redisClient, 'percentile-alert', 60);
var averageInterval;
var countInterval;
var done = function() {
clearInterval(averageInterval);
clearInterval(countInterval);
redisClient.quit();
};
// Calculate the average of the metric every second, and print the average if it reaches our threshold
var calculationInterval = setInterval(
function() {
// Exclude the current second to avoid partial calculations
var startSeconds = moment().subtract(11, 'seconds').unix();
var endSeconds = startSeconds + 10;
metric.getTimeStatistics(
startSeconds,
endSeconds,
function(err, stats, secondsSpanned) {
if (err) {
log(err);
done();
return;
}
log('Dataset spans ' + secondsSpanned + ' seconds. Stats:', stats);
// Make sure we have 5 seconds worth of data
if (secondsSpanned > 5) {
log('Average above 51', avg);
done();
}
}
);
},
1000
);
// These are just sample values so we can force the percentile to the value we want,
// in order to trigger the alert
var times = [
10, 100, 110, 90, 200, 201, 300, 150, 205, 225, 500,
10, 100, 110, 90, 200, 201, 300, 150, 205, 225, 500
];
// Fill the metric with values every 500ms
var addInterval = setInterval(
function() {
var time = times.shift();
metric.time(moment().unix(), time);
},
500
);
var stats = require('statistics');
var log = require('./log')('timeseries', false);
/*
20170824 - Dreaming up new interface and semantics. StatsD count and timer terminology can be confusing.
Timeseries is intended to store and run calculations on timeseries data, similar to what Graphite or InFluxDb can do.
Data types:
Count/Increment
Key/value pair where the value is incremented or decremented. Think "website page hit counter"
Duration/Time
Key->[ [timestamp, value], ...] mapping. Useful if you want to track how long an operation takes. Maybe fetchUser() takes 100ms today, but only took 10ms a month ago. Durations help you see that.
*/
/*
METRIC TYPES
For all metric types, the key prefix includes the second the values are being added
For all metric types, the key prefix includes the unix timestamp for which values are being added
Count
- Implemented using hashes
......@@ -51,21 +66,21 @@ type: count, timer
// NEW METHODS
// TODO: need to set a scheduler to clean up count buckets somehow
// Casual scan over keys for type and use hrem
Timeseries.prototype.count = function(secondsBucket, count, callback) {
Timeseries.prototype.count = function(unixTimestamp, count, callback) {
var self = this;
var key = this._getCountKey();
log('hincrby', key, secondsBucket, count);
log('hincrby', key, unixTimestamp, count);
self.redisClient.hincrby(
key,
secondsBucket,
unixTimestamp,
count,
callback
);
};
Timeseries.prototype.time = function(secondsBucket, value, callback) {
Timeseries.prototype.time = function(unixTimestamp, value, callback) {
var self = this;
var key = this._getTimeKey(secondsBucket);
var key = this._getTimeKey(unixTimestamp);
log('lpush', key, value);
this.redisClient.lpush(
key,
......@@ -85,8 +100,8 @@ Timeseries.prototype.time = function(secondsBucket, value, callback) {
Timeseries.prototype._getCountKey = function() {
return this.namespace + ':count';
};
Timeseries.prototype._getTimeKey = function(secondsBucket) {
return this.namespace + ':time:' + secondsBucket;
Timeseries.prototype._getTimeKey = function(unixTimestamp) {
return this.namespace + ':time:' + unixTimestamp;
};
......@@ -101,6 +116,7 @@ Timeseries.prototype.getCounts = function(startSeconds, endSeconds, callback) {
args.push( i );
}
// From the hash, get values for all the unixTimestamps we care about
self.redisClient.hmget(args, function(err, value) {
if (err) {
callback(err);
......@@ -170,3 +186,12 @@ Timeseries.prototype.getTimes = function(startSeconds, endSeconds, callback) {
next();
};
Timeseries.prototype.getTimeStatistics = function(startSeconds, endSeconds, callback) {
this.getTimes(startSeconds, endSeconds, function(err, times) {
if (err) {
callback(err);
return;
}
callback(null, times.reduce(stats) );
});
};
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment