mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
added new rate limit file based on redback. converetd auto compile to
use it.
This commit is contained in:
parent
aa0d26d5ab
commit
ff8320bce0
5 changed files with 121 additions and 45 deletions
|
@ -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) ->) ->
|
||||||
|
|
|
@ -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)
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue