2014-02-12 05:23:40 -05:00
|
|
|
Settings = require('settings-sharelatex')
|
2017-05-04 10:22:54 -04:00
|
|
|
RedisWrapper = require("../../infrastructure/RedisWrapper")
|
|
|
|
rclient = RedisWrapper.client("clsi_recently_compiled")
|
2014-02-12 05:23:40 -05:00
|
|
|
Project = require("../../models/Project").Project
|
|
|
|
ProjectRootDocManager = require "../Project/ProjectRootDocManager"
|
2014-11-28 09:26:21 -05:00
|
|
|
UserGetter = require "../User/UserGetter"
|
2014-02-12 05:23:40 -05:00
|
|
|
ClsiManager = require "./ClsiManager"
|
2017-04-03 11:18:30 -04:00
|
|
|
Metrics = require('metrics-sharelatex')
|
2014-02-12 05:23:40 -05:00
|
|
|
logger = require("logger-sharelatex")
|
2014-02-28 12:59:54 -05:00
|
|
|
rateLimiter = require("../../infrastructure/RateLimiter")
|
2014-02-12 05:23:40 -05:00
|
|
|
|
|
|
|
module.exports = CompileManager =
|
2016-05-18 05:09:22 -04:00
|
|
|
|
|
|
|
|
2014-11-28 09:26:21 -05:00
|
|
|
compile: (project_id, user_id, options = {}, _callback = (error) ->) ->
|
2014-02-12 05:23:40 -05:00
|
|
|
timer = new Metrics.Timer("editor.compile")
|
|
|
|
callback = (args...) ->
|
|
|
|
timer.done()
|
|
|
|
_callback(args...)
|
|
|
|
|
2017-10-09 11:31:01 -04:00
|
|
|
@_checkIfAutoCompileLimitHasBeenHit options.isAutoCompile, "everyone", (err, canCompile)->
|
2014-02-12 05:23:40 -05:00
|
|
|
if !canCompile
|
2014-05-19 10:28:35 -04:00
|
|
|
return callback null, "autocompile-backoff", []
|
2014-02-12 05:23:40 -05:00
|
|
|
logger.log project_id: project_id, user_id: user_id, "compiling project"
|
|
|
|
CompileManager._checkIfRecentlyCompiled project_id, user_id, (error, recentlyCompiled) ->
|
|
|
|
return callback(error) if error?
|
|
|
|
if recentlyCompiled
|
2016-03-21 09:54:45 -04:00
|
|
|
logger.warn {project_id, user_id}, "project was recently compiled so not continuing"
|
|
|
|
return callback null, "too-recently-compiled", []
|
2014-06-01 13:26:33 -04:00
|
|
|
|
2014-02-12 05:23:40 -05:00
|
|
|
CompileManager._ensureRootDocumentIsSet project_id, (error) ->
|
|
|
|
return callback(error) if error?
|
2017-07-28 10:01:05 -04:00
|
|
|
CompileManager.getProjectCompileLimits project_id, (error, limits) ->
|
2014-02-12 05:23:40 -05:00
|
|
|
return callback(error) if error?
|
2017-07-28 10:01:05 -04:00
|
|
|
for key, value of limits
|
|
|
|
options[key] = value
|
2017-10-09 11:31:01 -04:00
|
|
|
# Put a lower limit on autocompiles for free users, based on compileGroup
|
|
|
|
CompileManager._checkCompileGroupAutoCompileLimit options.isAutoCompile, limits.compileGroup, (err, canCompile)->
|
2017-10-03 11:16:21 -04:00
|
|
|
if !canCompile
|
|
|
|
return callback null, "autocompile-backoff", []
|
|
|
|
# only pass user_id down to clsi if this is a per-user compile
|
|
|
|
compileAsUser = if Settings.disablePerUserCompiles then undefined else user_id
|
|
|
|
ClsiManager.sendRequest project_id, compileAsUser, options, (error, status, outputFiles, clsiServerId, validationProblems) ->
|
|
|
|
return callback(error) if error?
|
|
|
|
logger.log files: outputFiles, "output files"
|
|
|
|
callback(null, status, outputFiles, clsiServerId, limits, validationProblems)
|
2017-07-28 10:01:05 -04:00
|
|
|
|
2016-07-14 09:48:46 -04:00
|
|
|
|
|
|
|
stopCompile: (project_id, user_id, callback = (error) ->) ->
|
|
|
|
CompileManager.getProjectCompileLimits project_id, (error, limits) ->
|
|
|
|
return callback(error) if error?
|
|
|
|
ClsiManager.stopCompile project_id, user_id, limits, callback
|
|
|
|
|
2016-05-31 11:20:24 -04:00
|
|
|
deleteAuxFiles: (project_id, user_id, callback = (error) ->) ->
|
2014-12-01 07:19:01 -05:00
|
|
|
CompileManager.getProjectCompileLimits project_id, (error, limits) ->
|
|
|
|
return callback(error) if error?
|
2016-05-31 11:20:24 -04:00
|
|
|
ClsiManager.deleteAuxFiles project_id, user_id, limits, callback
|
2014-02-12 05:23:40 -05:00
|
|
|
|
2014-11-28 09:26:21 -05:00
|
|
|
getProjectCompileLimits: (project_id, callback = (error, limits) ->) ->
|
|
|
|
Project.findById project_id, {owner_ref: 1}, (error, project) ->
|
|
|
|
return callback(error) if error?
|
|
|
|
UserGetter.getUser project.owner_ref, {"features":1}, (err, owner)->
|
|
|
|
return callback(error) if error?
|
|
|
|
callback null, {
|
|
|
|
timeout: owner.features?.compileTimeout || Settings.defaultFeatures.compileTimeout
|
|
|
|
compileGroup: owner.features?.compileGroup || Settings.defaultFeatures.compileGroup
|
|
|
|
}
|
2014-05-19 11:10:41 -04:00
|
|
|
|
2014-02-12 05:23:40 -05:00
|
|
|
COMPILE_DELAY: 1 # seconds
|
|
|
|
_checkIfRecentlyCompiled: (project_id, user_id, callback = (error, recentlyCompiled) ->) ->
|
|
|
|
key = "compile:#{project_id}:#{user_id}"
|
|
|
|
rclient.set key, true, "EX", @COMPILE_DELAY, "NX", (error, ok) ->
|
|
|
|
return callback(error) if error?
|
|
|
|
if ok == "OK"
|
|
|
|
return callback null, false
|
|
|
|
else
|
|
|
|
return callback null, true
|
|
|
|
|
2017-10-09 11:31:01 -04:00
|
|
|
_checkCompileGroupAutoCompileLimit: (isAutoCompile, compileGroup, callback = (err, canCompile)->)->
|
|
|
|
if compileGroup is "default"
|
|
|
|
CompileManager._checkIfAutoCompileLimitHasBeenHit isAutoCompile, compileGroup, callback
|
|
|
|
else
|
|
|
|
Metrics.inc "auto-compile-#{compileGroup}"
|
|
|
|
return callback(null, true) # always allow priority group users to compile
|
2017-10-03 11:16:21 -04:00
|
|
|
|
2017-10-09 11:31:01 -04:00
|
|
|
_checkIfAutoCompileLimitHasBeenHit: (isAutoCompile, compileGroup, callback = (err, canCompile)->)->
|
2017-10-03 11:16:21 -04:00
|
|
|
if !isAutoCompile
|
|
|
|
return callback(null, true)
|
2017-10-09 11:31:01 -04:00
|
|
|
Metrics.inc "auto-compile-#{compileGroup}"
|
2017-10-03 11:16:21 -04:00
|
|
|
opts =
|
|
|
|
endpointName:"auto_compile"
|
|
|
|
timeInterval:20
|
2017-10-09 11:31:01 -04:00
|
|
|
subjectName:compileGroup
|
|
|
|
throttle: Settings?.rateLimit?.autoCompile?[compileGroup] || 25
|
2017-10-03 11:16:21 -04:00
|
|
|
rateLimiter.addCount opts, (err, canCompile)->
|
|
|
|
if err?
|
|
|
|
canCompile = false
|
|
|
|
if !canCompile
|
2017-10-09 11:31:01 -04:00
|
|
|
Metrics.inc "auto-compile-#{compileGroup}-limited"
|
2014-02-12 05:23:40 -05:00
|
|
|
callback err, canCompile
|
|
|
|
|
|
|
|
_ensureRootDocumentIsSet: (project_id, callback = (error) ->) ->
|
|
|
|
Project.findById project_id, 'rootDoc_id', (error, project)=>
|
|
|
|
return callback(error) if error?
|
|
|
|
if !project?
|
|
|
|
return callback new Error("project not found")
|
|
|
|
|
|
|
|
if project.rootDoc_id?
|
|
|
|
callback()
|
|
|
|
else
|
|
|
|
ProjectRootDocManager.setRootDocAutomatically project_id, callback
|
|
|
|
|
2016-05-31 11:20:24 -04:00
|
|
|
wordCount: (project_id, user_id, file, callback = (error) ->) ->
|
2015-09-10 11:41:48 -04:00
|
|
|
CompileManager.getProjectCompileLimits project_id, (error, limits) ->
|
|
|
|
return callback(error) if error?
|
2016-05-31 11:20:24 -04:00
|
|
|
ClsiManager.wordCount project_id, user_id, file, limits, callback
|