added new rate limit file based on redback. converetd auto compile to

use it.
This commit is contained in:
Henry Oswald 2014-02-28 17:59:54 +00:00
parent aa0d26d5ab
commit ff8320bce0
5 changed files with 121 additions and 45 deletions

View file

@ -8,8 +8,7 @@ ProjectRootDocManager = require "../Project/ProjectRootDocManager"
ClsiManager = require "./ClsiManager" ClsiManager = require "./ClsiManager"
Metrics = require('../../infrastructure/Metrics') Metrics = require('../../infrastructure/Metrics')
logger = require("logger-sharelatex") logger = require("logger-sharelatex")
RateLimiter = require("ratelimiter") rateLimiter = require("../../infrastructure/RateLimiter")
module.exports = CompileManager = module.exports = CompileManager =
compile: (project_id, user_id, opt = {}, _callback = (error) ->) -> compile: (project_id, user_id, opt = {}, _callback = (error) ->) ->
@ -56,17 +55,15 @@ module.exports = CompileManager =
_checkIfAutoCompileLimitHasBeenHit: (isAutoCompile, callback = (err, canCompile)->)-> _checkIfAutoCompileLimitHasBeenHit: (isAutoCompile, callback = (err, canCompile)->)->
if !isAutoCompile if !isAutoCompile
return callback(null, true) return callback(null, true)
key = "auto_compile_rate_limit" opts =
ten_seconds = (10 * 1000) endpointName:"auto_compile"
limit = new RateLimiter(db:rclient, id:key, max:7, duration:ten_seconds) timeInterval:10
limit.get (err, limit)-> subjectName:"everyone"
Metrics.inc("compile.autocompile.rateLimitCheck") throttle: 5
if limit.remaining > 0 and !err? rateLimiter.addCount opts, (err, canCompile)->
canCompile = true if err?
else
canCompile = false canCompile = false
Metrics.inc("compile.autocompile.rateLimitHit") logger.log canCompile:canCompile, opts:opts, "checking if auto compile limit has been hit"
logger.log canCompile:canCompile, limit:limit, "checking if auto compile limit has been hit"
callback err, canCompile callback err, canCompile
_ensureRootDocumentIsSet: (project_id, callback = (error) ->) -> _ensureRootDocumentIsSet: (project_id, callback = (error) ->) ->

View file

@ -0,0 +1,9 @@
redback = require("redback").createClient()
module.exports =
addCount: (opts, callback = (opts, shouldProcess)->)->
ratelimit = redback.createRateLimit(opts.endpointName)
ratelimit.addCount opts.subjectName, opts.timeInterval, (err, callCount)->
shouldProcess = callCount < opts.throttle
callback(err, shouldProcess)

View file

@ -23,21 +23,21 @@
"lynx": "0.0.11", "lynx": "0.0.11",
"session.socket.io": "0.1.4", "session.socket.io": "0.1.4",
"socket.io": "0.9.15", "socket.io": "0.9.15",
"mimelib": "~0.2.8", "mimelib": "0.2.8",
"bufferedstream": "~1.4.1", "bufferedstream": "1.4.1",
"mixpanel": "0.0.18", "mixpanel": "0.0.18",
"settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#master", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#master",
"logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master", "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master",
"soa-req-id": "git+https://github.com/sharelatex/soa-req-id.git#master", "soa-req-id": "git+https://github.com/sharelatex/soa-req-id.git#master",
"fairy": "0.0.2", "fairy": "0.0.2",
"node-uuid": "~1.4.0", "node-uuid": "1.4.0",
"mongojs": "0.9.8", "mongojs": "0.9.8",
"node-ses": "0.0.3", "node-ses": "0.0.3",
"bcrypt": "0.7.5", "bcrypt": "0.7.5",
"archiver": "~0.5.1", "archiver": "0.5.1",
"ratelimiter": "~1.0.0", "nodetime": "0.8.15",
"nodetime": "~0.8.15", "mocha": "1.17.1",
"mocha": "~1.17.1" "redback": "0.3.7"
}, },
"devDependencies": { "devDependencies": {
"chai": "", "chai": "",
@ -45,16 +45,16 @@
"sandboxed-module": "0.2.0", "sandboxed-module": "0.2.0",
"timekeeper": "", "timekeeper": "",
"sinon": "", "sinon": "",
"grunt-concurrent": "~0.4.3", "grunt-concurrent": "0.4.3",
"grunt-contrib-clean": "~0.5.0", "grunt-contrib-clean": "0.5.0",
"grunt-contrib-coffee": "~0.10.0", "grunt-contrib-coffee": "0.10.0",
"grunt-nodemon": "~0.2.0", "grunt-nodemon": "0.2.0",
"grunt-contrib-less": "~0.9.0", "grunt-contrib-less": "0.9.0",
"grunt-mocha-test": "~0.9.0", "grunt-mocha-test": "0.9.0",
"grunt-available-tasks": "~0.4.1", "grunt-available-tasks": "0.4.1",
"grunt-contrib-requirejs": "~0.4.1", "grunt-contrib-requirejs": "0.4.1",
"grunt-execute": "~0.1.5", "grunt-execute": "0.1.5",
"bunyan": "~0.22.1", "bunyan": "0.22.1",
"grunt-bunyan": "~0.5.0" "grunt-bunyan": "0.5.0"
} }
} }

View file

@ -10,11 +10,8 @@ describe "CompileManager", ->
beforeEach -> beforeEach ->
@rateLimitGetStub = sinon.stub() @rateLimitGetStub = sinon.stub()
rateLimitGetStub = @rateLimitGetStub rateLimitGetStub = @rateLimitGetStub
@ratelimiter = class RateLimiter @ratelimiter =
constructor: -> addCount: sinon.stub()
return {
get: rateLimitGetStub
}
@CompileManager = SandboxedModule.require modulePath, requires: @CompileManager = SandboxedModule.require modulePath, requires:
"settings-sharelatex": @settings = "settings-sharelatex": @settings =
redis: web: {host: "localhost", port: 42} redis: web: {host: "localhost", port: 42}
@ -25,7 +22,7 @@ describe "CompileManager", ->
"../../models/Project": Project: @Project = {} "../../models/Project": Project: @Project = {}
"./ClsiManager": @ClsiManager = {} "./ClsiManager": @ClsiManager = {}
"../../managers/LatexManager": @LatexManager = {} "../../managers/LatexManager": @LatexManager = {}
"ratelimiter":@ratelimiter "../../infrastructure/RateLimiter": @ratelimiter
"../../infrastructure/Metrics": @Metrics = "../../infrastructure/Metrics": @Metrics =
Timer: class Timer Timer: class Timer
done: sinon.stub() done: sinon.stub()
@ -53,7 +50,6 @@ describe "CompileManager", ->
.calledWith(@project_id, @user_id) .calledWith(@project_id, @user_id)
.should.equal true .should.equal true
it "should flush the project to the database", -> it "should flush the project to the database", ->
@DocumentUpdaterHandler.flushProjectToMongo @DocumentUpdaterHandler.flushProjectToMongo
.calledWith(@project_id) .calledWith(@project_id)
@ -203,29 +199,30 @@ describe "CompileManager", ->
describe "_checkIfAutoCompileLimitHasBeenHit", -> describe "_checkIfAutoCompileLimitHasBeenHit", ->
it "should be able to compile if it is not an autocompile", (done)-> it "should be able to compile if it is not an autocompile", (done)->
limit = {remaining:-1} @ratelimiter.addCount.callsArgWith(1, null, true)
@rateLimitGetStub.callsArgWith(0, null, limit)
@CompileManager._checkIfAutoCompileLimitHasBeenHit false, (err, canCompile)=> @CompileManager._checkIfAutoCompileLimitHasBeenHit false, (err, canCompile)=>
canCompile.should.equal true canCompile.should.equal true
done() done()
it "should be able to compile if rate limit has remianing", (done)-> it "should be able to compile if rate limit has remianing", (done)->
limit = {remaining:3} @ratelimiter.addCount.callsArgWith(1, null, true)
@rateLimitGetStub.callsArgWith(0, null, limit)
@CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=> @CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=>
args = @ratelimiter.addCount.args[0][0]
args.throttle.should.equal 5
args.subjectName.should.equal "everyone"
args.timeInterval.should.equal 10
args.endpointName.should.equal "auto_compile"
canCompile.should.equal true canCompile.should.equal true
done() done()
it "should be not able to compile if rate limit has no remianing", (done)-> it "should be not able to compile if rate limit has no remianing", (done)->
limit = {remaining:0} @ratelimiter.addCount.callsArgWith(1, null, false)
@rateLimitGetStub.callsArgWith(0, null, limit)
@CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=> @CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=>
canCompile.should.equal false canCompile.should.equal false
done() done()
it "should return false if there is an error in the rate limit", (done)-> it "should return false if there is an error in the rate limit", (done)->
limit = {remaining:4} @ratelimiter.addCount.callsArgWith(1, "error")
@rateLimitGetStub.callsArgWith(0, "Err", limit)
@CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=> @CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=>
canCompile.should.equal false canCompile.should.equal false
done() done()

View file

@ -0,0 +1,73 @@
assert = require("chai").assert
sinon = require('sinon')
chai = require('chai')
should = chai.should()
expect = chai.expect
modulePath = "../../../../app/js/infrastructure/RateLimiter.js"
SandboxedModule = require('sandboxed-module')
describe "FileStoreHandler", ->
beforeEach ->
@settings = apis:{filestore:{url:"http//filestore.sharelatex.test"}}
@redbackInstance =
addCount: sinon.stub()
@redback =
createRateLimit: sinon.stub().returns(@redbackInstance)
@limiter = SandboxedModule.require modulePath, requires:
"settings-sharelatex":@settings
"logger-sharelatex" : @logger = {log:sinon.stub(), err:sinon.stub()}
"redback": createClient: => @redback
@endpointName = "compiles"
@subject = "some project id"
@timeInterval = 20
@throttleLimit = 5
@details =
endpointName: @endpointName
subjectName: @subject
throttle: @throttleLimit
timeInterval: @timeInterval
describe "addCount", ->
beforeEach ->
@redbackInstance.addCount.callsArgWith(2, null, 10)
it "should use correct namespace", (done)->
@limiter.addCount @details, =>
@redback.createRateLimit.calledWith(@endpointName).should.equal true
done()
it "should only call it once", (done)->
@limiter.addCount @details, =>
@redbackInstance.addCount.callCount.should.equal 1
done()
it "should use the subjectName", (done)->
@limiter.addCount @details, =>
@redbackInstance.addCount.calledWith(@details.subjectName, @details.timeInterval).should.equal true
done()
it "should return true if the count is less than throttle", (done)->
@details.throttle = 100
@limiter.addCount @details, (err, canProcess)=>
canProcess.should.equal true
done()
it "should return true if the count is less than throttle", (done)->
@details.throttle = 1
@limiter.addCount @details, (err, canProcess)=>
canProcess.should.equal false
done()
it "should return false if the limit is matched", (done)->
@details.throttle = 10
@limiter.addCount @details, (err, canProcess)=>
canProcess.should.equal false
done()