diff --git a/lib/cartodb/api/user_limits_api.js b/lib/cartodb/api/user_limits_api.js index 06ab4ccf..caf9f151 100644 --- a/lib/cartodb/api/user_limits_api.js +++ b/lib/cartodb/api/user_limits_api.js @@ -1,8 +1,5 @@ var step = require('step'); - -const RATE_LIMIT_REDIS_DB = 8; - /** * * @param metadataBackend @@ -14,11 +11,6 @@ function UserLimitsApi(metadataBackend, options) { this.metadataBackend = metadataBackend; this.options = options || {}; this.options.limits = this.options.limits || {}; - this.rateLimits = { - redisCommand: 'EVAL', - sha: null, - lua: getRateLimitLuaScript() - }; this.preprareRateLimit(); } @@ -89,139 +81,11 @@ UserLimitsApi.prototype.getTimeoutRenderLimit = function (username, apiKey, call }; UserLimitsApi.prototype.preprareRateLimit = function () { - var self = this; - if (this.options.limits.rateLimitsEnabled) { - this.metadataBackend.redisCmd( - RATE_LIMIT_REDIS_DB, - 'SCRIPT', - ['LOAD', getRateLimitLuaScript()], - function (err, sha) { - if (!err && sha) { - self.rateLimits.sha = sha; - self.rateLimits.redisCommand = 'EVALSHA'; - } - } - ); + this.metadataBackend.loadRateLimitsScript(); } }; UserLimitsApi.prototype.getRateLimit = function (user, endpointGroup, callback) { - var self = this; - - let redisParams = [ - this.rateLimits.redisCommand === 'EVAL' ? this.rateLimits.lua : this.rateLimits.sha, - 2, - getStoreKey(user, endpointGroup), // KEY[1] - getStatusKey(user, endpointGroup) // KEY[2] - ]; - - this.metadataBackend.redisCmd( - RATE_LIMIT_REDIS_DB, - this.rateLimits.redisCommand, - redisParams, - function (err, rateLimits) { - if (err) { - if (err.name === 'ReplyError' && err.message === 'NOSCRIPT No matching script. Please use EVAL.') { - self.rateLimits.redisCommand = 'EVAL'; - return self.getRateLimit(user, endpointGroup, callback); - } else { - callback(err); - } - } - - callback(null, getLowerRateLimit(rateLimits)); - } - ); + this.metadataBackend.getRateLimit(user, 'maps', endpointGroup, callback); }; - -/** - * Returns Redis key where the limits are saved by user and endpoint - * The value is a Redis hash: - * maxBurst (b): Integer (as string) - * countPerPeriod (c): Integer (as string) - * period (p): Integer (as string) - * @param {string} user - * @param {string} endpointGroup - */ -function getStoreKey(user, endpointGroup) { - return `limits:rate:store:${user}:maps:${endpointGroup}`; -} - -/** - * Returns Redis key where the current state of the limit by user and endpoint - * This key is managed by redis-cell (CL.THROTTLE command) - * @param {string} user - * @param {string} endpointGroup - */ -function getStatusKey(user, endpointGroup) { - return `limits:rate:status:${user}:maps:${endpointGroup}`; -} - -function getRateLimitLuaScript() { - return ` - local results = {} - local resultsCounter = 0 - - local limits = {} - local limitsArray = redis.call("LRANGE", KEYS[1], 0, -1) - - for i, v in ipairs(limitsArray) do - local rest = i % 3 - if rest ~= 0 then - limits[rest] = v - else - resultsCounter = resultsCounter + 1 - results[resultsCounter] = redis.call("CL.THROTTLE", KEYS[2], limits[1], limits[2], v) - end - end - - return results - `; -} - -/** - * Returns the inner rateLimit what is the strictest one - * @param {Array} rateLimits Each inner array has 5 integers indicating: - * isBloqued, limit, remaining, retry, reset - */ -function getLowerRateLimit(rateLimits) { - /*jshint maxcomplexity:10 */ - if (!Array.isArray(rateLimits) || !rateLimits.length) { - return; - } - - let minIndex; - let minRemainingValue; - for (let currentIndex = 0; currentIndex < rateLimits.length; currentIndex++) { - const rateLimit = rateLimits[currentIndex]; - if (!validateRatelimit(rateLimit)) { - continue; - } - - const [isBlocked, , remaining] = rateLimit; - - if (isBlocked === 1) { - minIndex = currentIndex; - break; - } - - if (minRemainingValue === undefined || remaining < minRemainingValue) { - minIndex = currentIndex; - minRemainingValue = remaining; - } - } - - if (rateLimits[minIndex]) { - return rateLimits[minIndex]; - } else { - return; - } -} - -function validateRatelimit(rateLimit) { - return rateLimit.length === 5; -} - -module.exports.getStoreKey = getStoreKey; -module.exports.getLowerRateLimit = getLowerRateLimit;