CompileManager.coffee 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. Settings = require('settings-sharelatex')
  2. RedisWrapper = require("../../infrastructure/RedisWrapper")
  3. rclient = RedisWrapper.client("clsi_recently_compiled")
  4. ProjectGetter = require('../Project/ProjectGetter')
  5. ProjectRootDocManager = require "../Project/ProjectRootDocManager"
  6. UserGetter = require "../User/UserGetter"
  7. ClsiManager = require "./ClsiManager"
  8. Metrics = require('metrics-sharelatex')
  9. logger = require("logger-sharelatex")
  10. rateLimiter = require("../../infrastructure/RateLimiter")
  11. module.exports = CompileManager =
  12. compile: (project_id, user_id, options = {}, _callback = (error) ->) ->
  13. timer = new Metrics.Timer("editor.compile")
  14. callback = (args...) ->
  15. timer.done()
  16. _callback(args...)
  17. logger.log project_id: project_id, user_id: user_id, "compiling project"
  18. CompileManager._checkIfRecentlyCompiled project_id, user_id, (error, recentlyCompiled) ->
  19. return callback(error) if error?
  20. if recentlyCompiled
  21. logger.warn {project_id, user_id}, "project was recently compiled so not continuing"
  22. return callback null, "too-recently-compiled", []
  23. CompileManager._checkIfAutoCompileLimitHasBeenHit options.isAutoCompile, "everyone", (err, canCompile)->
  24. if !canCompile
  25. return callback null, "autocompile-backoff", []
  26. ProjectRootDocManager.ensureRootDocumentIsSet project_id, (error) ->
  27. return callback(error) if error?
  28. CompileManager.getProjectCompileLimits project_id, (error, limits) ->
  29. return callback(error) if error?
  30. for key, value of limits
  31. options[key] = value
  32. # Put a lower limit on autocompiles for free users, based on compileGroup
  33. CompileManager._checkCompileGroupAutoCompileLimit options.isAutoCompile, limits.compileGroup, (err, canCompile)->
  34. if !canCompile
  35. return callback null, "autocompile-backoff", []
  36. # only pass user_id down to clsi if this is a per-user compile
  37. compileAsUser = if Settings.disablePerUserCompiles then undefined else user_id
  38. ClsiManager.sendRequest project_id, compileAsUser, options, (error, status, outputFiles, clsiServerId, validationProblems) ->
  39. return callback(error) if error?
  40. logger.log files: outputFiles, "output files"
  41. callback(null, status, outputFiles, clsiServerId, limits, validationProblems)
  42. stopCompile: (project_id, user_id, callback = (error) ->) ->
  43. CompileManager.getProjectCompileLimits project_id, (error, limits) ->
  44. return callback(error) if error?
  45. ClsiManager.stopCompile project_id, user_id, limits, callback
  46. deleteAuxFiles: (project_id, user_id, callback = (error) ->) ->
  47. CompileManager.getProjectCompileLimits project_id, (error, limits) ->
  48. return callback(error) if error?
  49. ClsiManager.deleteAuxFiles project_id, user_id, limits, callback
  50. getProjectCompileLimits: (project_id, callback = (error, limits) ->) ->
  51. ProjectGetter.getProject project_id, owner_ref: 1, (error, project) ->
  52. return callback(error) if error?
  53. UserGetter.getUser project.owner_ref, {"features":1}, (err, owner)->
  54. return callback(error) if error?
  55. callback null, {
  56. timeout: owner?.features?.compileTimeout || Settings.defaultFeatures.compileTimeout
  57. compileGroup: owner?.features?.compileGroup || Settings.defaultFeatures.compileGroup
  58. }
  59. COMPILE_DELAY: 1 # seconds
  60. _checkIfRecentlyCompiled: (project_id, user_id, callback = (error, recentlyCompiled) ->) ->
  61. key = "compile:#{project_id}:#{user_id}"
  62. rclient.set key, true, "EX", @COMPILE_DELAY, "NX", (error, ok) ->
  63. return callback(error) if error?
  64. if ok == "OK"
  65. return callback null, false
  66. else
  67. return callback null, true
  68. _checkCompileGroupAutoCompileLimit: (isAutoCompile, compileGroup, callback = (err, canCompile)->)->
  69. if !isAutoCompile
  70. return callback(null, true)
  71. if compileGroup is "standard"
  72. # apply extra limits to the standard compile group
  73. CompileManager._checkIfAutoCompileLimitHasBeenHit isAutoCompile, compileGroup, callback
  74. else
  75. Metrics.inc "auto-compile-#{compileGroup}"
  76. return callback(null, true) # always allow priority group users to compile
  77. _checkIfAutoCompileLimitHasBeenHit: (isAutoCompile, compileGroup, callback = (err, canCompile)->)->
  78. if !isAutoCompile
  79. return callback(null, true)
  80. Metrics.inc "auto-compile-#{compileGroup}"
  81. opts =
  82. endpointName:"auto_compile"
  83. timeInterval:20
  84. subjectName:compileGroup
  85. throttle: Settings?.rateLimit?.autoCompile?[compileGroup] || 25
  86. rateLimiter.addCount opts, (err, canCompile)->
  87. if err?
  88. canCompile = false
  89. if !canCompile
  90. Metrics.inc "auto-compile-#{compileGroup}-limited"
  91. callback err, canCompile
  92. wordCount: (project_id, user_id, file, callback = (error) ->) ->
  93. CompileManager.getProjectCompileLimits project_id, (error, limits) ->
  94. return callback(error) if error?
  95. ClsiManager.wordCount project_id, user_id, file, limits, callback