Last active
August 29, 2015 14:05
-
-
Save themasch/b72d910081517a4115ed to your computer and use it in GitHub Desktop.
node.js rate limite scheduler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function ms(input) { | |
if(typeof input === 'Number') { | |
return input | |
} | |
var number = parseInt(input, 10); | |
var unit = input.replace(number.toString(10), '').trim() | |
switch(unit) { | |
case 'd': | |
number *= 24 | |
case 'h': | |
number *= 60 | |
case 'm': | |
number *= 60 | |
case 's': | |
number *= 1000 | |
} | |
return number | |
} | |
function Scheduler(limits) { | |
this.smallestDelta = false | |
this.timerRunning = false | |
this.queueRunning = false | |
this.limits = limits.map(function(limitDef) { | |
var milis = ms(limitDef.time) | |
var limit = { | |
maxValue: limitDef.limit, | |
totalWindow: milis, | |
msPerTick: milis / limitDef.limit, | |
ticksLeft: limitDef.limit | |
} | |
limit.timer = setInterval(function() { | |
limit.ticksLeft = Math.min(limit.ticksLeft + 1, limit.maxValue) | |
}, limit.msPerTick) | |
if(!this.smallestDelta) { | |
this.smallestDelta = limit.msPerTick | |
} | |
else { | |
this.smallestDelta = Math.min(limit.msPerTick, this.smallestDelta) | |
} | |
// dont stay alive for this timer | |
limit.timer.unref() | |
return limit | |
}.bind(this)) | |
this.queue = [] | |
this.lastUpdate = Date.now() | |
} | |
Scheduler.prototype.push = function(fnc) { | |
this.queue.push(fnc) | |
this.resumeQueue() | |
} | |
Scheduler.prototype.resumeQueue = function() { | |
if(!this.queueRunning) { | |
console.log(Date.now(), 'resumeQueue') | |
setImmediate(this.execute.bind(this)) | |
this.queueRunning = true; | |
} | |
} | |
Scheduler.prototype.tickAvailable = function() { | |
return this.limits.every(function(limit) { | |
return limit.ticksLeft > 0 | |
}) | |
} | |
Scheduler.prototype.execute = function() { | |
var hasToken = this.tickAvailable() | |
if(this.queue.length > 0 && hasToken) { | |
var max = this.limits.reduce(function(val, limit) { | |
return Math.min(val, limit.ticksLeft) | |
}, this.queue.length) | |
for(var i=0;i<max;i++) { | |
var fnc = this.queue.shift() | |
fnc() | |
} | |
this.limits.forEach(function(limit) { | |
limit.ticksLeft -= max | |
}) | |
if(this.queue.length > 0) { | |
setImmediate(this.execute.bind(this)) | |
} | |
} | |
else if(!hasToken) { | |
if(!this.timerRunning) { | |
// we still got work to do. keep the timers alive! | |
this.limits.forEach(function(limit) { | |
limit.timer.ref() | |
}) | |
this.timerRunning = true | |
} | |
setTimeout(this.execute.bind(this), this.smallestDelta) | |
} | |
if(this.queue.length === 0) { | |
// shutdown queue. everything done! | |
this.timerRunning = this.queueRunning = false | |
this.limits.map(function(limit) { | |
limit.timer.unref() | |
}) | |
} | |
} | |
module.exports = Scheduler |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Scheduler = require('./Scheduler') | |
var sched = new Scheduler([ | |
{ limit: 10, time: '10s' }, | |
{ limit: 500, time: '10m' } | |
]) | |
for(var i=0;i<50;i++) { | |
sched.push(function() { | |
console.log(Date.now() - start, this.x, sched.limits.map(function(l) { return l.ticksLeft })) | |
}.bind({ x: i })) | |
} | |
var start = Date.now() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment