Skip to content

Commit

Permalink
Implemented FixedWindow throttling
Browse files Browse the repository at this point in the history
  • Loading branch information
analog-nico committed Sep 28, 2015
1 parent 1eaf310 commit 84a2a20
Show file tree
Hide file tree
Showing 7 changed files with 375 additions and 30 deletions.
44 changes: 31 additions & 13 deletions lib/server/core/presets/google-analytics.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

var _ = require('lodash');
var moment = require('moment');

var Manager = require('../manager.js');
var Rule = require('../rule.js');
Expand All @@ -27,7 +28,10 @@ var generalRuleQPD = null,
*/
module.exports = function (options) {

// TODO: Note: Daily quotas refresh at midnight PST.
// Note: Daily quotas refresh at midnight PST.
function getStartOfNextWindow() {
return moment.utc().startOf('day').add(1, 'day').add(8, 'hours').valueOf();
}

_.defaults(options, {
api: '',
Expand All @@ -52,8 +56,10 @@ module.exports = function (options) {
if (generalRuleQPD === null) {
generalRuleQPD = new Rule({
limit: options.dailyRequests,
window: 24*60*60*1000,
throttling: 'window-fixed',
throttling: {
type: 'window-fixed',
getStartOfNextWindow: getStartOfNextWindow
},
resource: 'requests'
});
}
Expand Down Expand Up @@ -89,8 +95,10 @@ module.exports = function (options) {
// 500 write requests per project per day – can be increased
manager.addRule({
limit: options.dailyWrites,
window: 24*60*60*1000,
throttling: 'window-fixed',
throttling: {
type: 'window-fixed',
getStartOfNextWindow: getStartOfNextWindow
},
resource: 'writeRequests'
});

Expand Down Expand Up @@ -123,8 +131,11 @@ module.exports = function (options) {
// 50 upload operations per property per day
manager.addRule({
limit: 50,
window: 24*60*60*1000,
throttling: 'window-fixed',
getStartOfNextWindow: getStartOfNextWindow,
throttling: {
type: 'window-fixed',
getStartOfNextWindow: getStartOfNextWindow
},
scope: ['userId', 'propertyId'],
resource: 'writeRequests'
});
Expand All @@ -141,8 +152,11 @@ module.exports = function (options) {
// 100 unsampled reports per day per property
manager.addRule({
limit: 100,
window: 24*60*60*1000,
throttling: 'window-fixed',
getStartOfNextWindow: getStartOfNextWindow,
throttling: {
type: 'window-fixed',
getStartOfNextWindow: getStartOfNextWindow
},
scope: ['userId', 'propertyId'],
resource: 'unsampledReports'
});
Expand All @@ -155,8 +169,10 @@ module.exports = function (options) {
// 50 requests per project per day
manager.addRule({
limit: 50,
window: 24*60*60*1000,
throttling: 'window-fixed',
throttling: {
type: 'window-fixed',
getStartOfNextWindow: getStartOfNextWindow
},
resource: 'writeRequests'
});

Expand All @@ -169,8 +185,10 @@ module.exports = function (options) {
// 10,000 requests per view (profile) per day
manager.addRule({
limit: 10000,
window: 24*60*60*1000,
throttling: 'window-fixed',
throttling: {
type: 'window-fixed',
getStartOfNextWindow: getStartOfNextWindow
},
scope: 'viewId',
resource: 'requests'
});
Expand Down
26 changes: 18 additions & 8 deletions lib/server/core/presets/google-plus.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
'use strict';

var Manager = require('../manager.js');
var _ = require('lodash');
var moment = require('moment');

var Manager = require('../manager.js');


/**
* Quota Preset for Google+
Expand All @@ -18,7 +21,10 @@ var _ = require('lodash');
*/
module.exports = function (options) {

// TODO: Note: Daily quotas refresh at midnight PST.
// Note: Daily quotas refresh at midnight PST.
function getStartOfNextWindow() {
return moment.utc().startOf('day').add(1, 'day').add(8, 'hours').valueOf();
}

var manager = new Manager({
backoff: 'timeout'
Expand All @@ -28,15 +34,17 @@ module.exports = function (options) {

manager.addRule({
limit: 20000000,
window: 24*60*60*1000,
throttling: 'window-fixed',
throttling: {
type: 'window-fixed',
getStartOfNextWindow: getStartOfNextWindow
},
resource: 'signInRequests'
});

manager.addRule({
limit: 5,
window: 1000,
throttling: 'window-fixed',
throttling: 'window-sliding',
queueing: 'fifo',
scope: 'userId',
resource: 'signInRequests'
Expand All @@ -46,15 +54,17 @@ module.exports = function (options) {

manager.addRule({
limit: 10000,
window: 24*60*60*1000,
throttling: 'window-fixed',
throttling: {
type: 'window-fixed',
getStartOfNextWindow: getStartOfNextWindow
},
resource: 'requests'
});

manager.addRule({
limit: 5,
window: 1000,
throttling: 'window-fixed',
throttling: 'window-sliding',
queueing: 'fifo',
scope: 'userId',
resource: 'requests'
Expand Down
2 changes: 1 addition & 1 deletion lib/server/core/presets/twitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module.exports = function (options) {
manager.addRule({
limit: options.authenticated ? authQuota : noAuthQuota,
window: 15*60*1000,
throttling: 'window-fixed', // TODO: Consider feeding back of X-Rate-Limit-Reset
throttling: 'window-sliding', // TODO: Consider feeding back of X-Rate-Limit-Reset
queueing: 'fifo',
scope: options.authenticated ? 'userId' : [],
resource: resource
Expand Down
16 changes: 12 additions & 4 deletions lib/server/core/presets/youtube.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
'use strict';

var Manager = require('../manager.js');
var _ = require('lodash');
var moment = require('moment');

var Manager = require('../manager.js');


/**
* Quota Preset for YouTube Analytics API
Expand All @@ -21,7 +24,10 @@ var _ = require('lodash');
*/
module.exports = function (options) {

// TODO: Note: Daily quotas refresh at midnight PST.
// Note: Daily quotas refresh at midnight PST.
function getStartOfNextWindow() {
return moment.utc().startOf('day').add(1, 'day').add(8, 'hours').valueOf();
}

_.defaults(options, {
requestsPerSecondPerUser: 12
Expand All @@ -33,8 +39,10 @@ module.exports = function (options) {

manager.addRule({
limit: 100000,
window: 24*60*60*1000,
throttling: 'window-fixed',
throttling: {
type: 'window-fixed',
getStartOfNextWindow: getStartOfNextWindow
},
resource: 'requests'
});

Expand Down
76 changes: 73 additions & 3 deletions lib/server/core/throttling/window-fixed.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,74 @@
// Strategies: cutoff, linear-remaining, twice-average-remaining
'use strict';

// TODO: Proper implementation
module.exports = require('./window-sliding.js');
// Possible strategies: cutoff, linear-remaining, twice-average-remaining
// Currently implemented strategy: cuttoff
// A more linear strategy can be produced by just dividing the limit by a certain factor and returning shorter intervals with getStartOfNextWindow.

var _ = require('lodash');


function FixedWindow(options, moreAvailableCb) {

if (_.isUndefined(options.limit)) {
throw new Error('Please pass the limit parameter to allow window-fixed throttling');
}

if (!_.isFunction(options.getStartOfNextWindow)) {
throw new Error('Please pass a function as the getStartOfNextWindow parameter to allow window-fixed throttling');
}

this._limit = options.limit;
this._used = _.isFinite(options.used) ? options.used : 0;
this._moreAvailableCb = moreAvailableCb;
this._getStartOfNextWindow = options.getStartOfNextWindow;

this._prepareNextWindow();

}

FixedWindow.prototype.isAvailable = function (resourceAmount, options) {
return this._used + resourceAmount <= this._limit;
};

FixedWindow.prototype.reserve = function (resourceAmount, options) {

this._used += resourceAmount;

var self = this;
return function (feedback) {

if (!_.isUndefined(feedback.limit)) {

if (!_.isFinite(feedback.limit) || feedback.limit <= 0) {
throw new Error('Please pass a positive number as the limit parameter');
}

var limitIncreased = feedback.limit > self._limit;

self._limit = feedback.limit;

if (limitIncreased && self._used < self._limit) {
self._moreAvailableCb();
}

}

};

};

FixedWindow.prototype._prepareNextWindow = function () {

var time = this._getStartOfNextWindow();

var self = this;
setTimeout(function () {
self._used = 0;
self._prepareNextWindow();
self._moreAvailableCb();
}, time - (new Date()).getTime()).unref();

};


module.exports = FixedWindow;
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"dependencies": {
"bluebird": "^2.10",
"double-ended-queue": "2.1.0-0",
"lodash": "^3.10"
"lodash": "^3.10",
"moment": "^2.10.6"
},
"engines": {
"node": ">=0.10.0"
Expand Down

0 comments on commit 84a2a20

Please sign in to comment.