Commit fb140d57 authored by Alan W Szlosek Jr's avatar Alan W Szlosek Jr

make throttling logic more precise

parent 09ac685c
var cache = {};
var messageCounts = {};
/*
- for each messageId, keep track of:
- when throttline expires
- number of times we've seen messageId
- when message arrives
- deliver it if the messageId is was not seen recently
- for instance: if the messageId is not in cache, or the waitUntil time has passed
- set a timeout
- trigger the callback if our count is 2 or more (don't count the first occurrence which was already delivered)
*/
module.exports = function(messageId, message, throttleSeconds, callback) {
var cacheEntry;
var deliver = true;
var now = Date.now();
var occurrences;
// messageId allows us to rate-limit a major message to email at a
// different rate than we limit it to chat or some other alerting mechanism
// When did we last see this message?
if (messageId in cache) {
cacheEntry = cache[messageId];
cacheEntry.occurrences++;
// Should we re-deliver the message?
deliver = now >= cacheEntry.waitUntil;
if (messageId in messageCounts) {
messageCounts[messageId]++;
} else {
// Add to cache
cacheEntry = {
// This will be set below when we deliver the message
waitUntil: 0,
occurrences: 1
};
cache[messageId] = cacheEntry;
}
// Do we only care about the latest message?
cacheEntry.message = message;
if (deliver) {
occurrences = cacheEntry.occurrences;
cacheEntry.occurrences = 0;
cacheEntry.waitUntil = now + (throttleSeconds * 1000);
// Transform the message as appropriate for the channel
// Send the alert through the channel
callback(message, occurrences);
messageCounts[messageId] = 0;
setTimeout(
function() {
if (messageCounts[messageId] > 0) {
callback(message, messageCounts[messageId]);
delete messageCounts[messageId];
}
},
throttleSeconds * 1000
);
callback(message, 1);
}
};
/*
Use redis to scale out the rollup of repeat log messages over multiple event-pipelines
NOTE: This module needs a re-think and re-write. DONT USE IT
- use Redis INCR - if the value after increment is 1, then this is the first time we've seen the message. the value of the message is the occurrences. set a TTL.
Use redis to scale out the rollup of repeat log messages over multiple event-pipelines
*/
/*
I think throttle or rollup is confusing. We want to ensure that only one batch happens every N seconds,
so don't we need to use timeouts and check redis? Otherwise we may rollup repeat messages wihin the window,
but never get them.
Scenario:
- message comes in, redis returns 1 for incr so we deliver it
- 100 other messages come in immediately, redis returns 101 so we don't deliver
- after 10 seconds no more messages come in, and we drop the 100 throttled messages on the floor
- we need a timeout that will deliver the other 100 messages after N seconds
But that means we need to manually specify the timeout that handles the above case. And if we don't
ensure only one, and we scale this out to many processes, we could have duplication and races
*/
module.exports = function(redisClient) {
var timeoutHandles = {};
// You must specify your own message ID to support throttling the same message.id to different callbacks without collisions
return function(messageId, message, throttleSeconds, callback) {
var self = this;
......@@ -17,6 +44,18 @@ module.exports = function(redisClient) {
return;
}
if(messageId in timeoutHandles) {
clearTimeout(timeoutHandles[messageId]);
}
timeoutHandles[messageId] = setTimeout(
function() {
callback(message);
// Can we use strategy from the fast LRU instead of delete?
delete timeoutHandles[messageId];
},
throttleSeconds
);
// Deliver message if the count for this key is 1,
// ie. the first time we've seen it within timeframe
if (occurrences == 1) {
......
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