ProjectManager.coffee 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. RedisManager = require "./RedisManager"
  2. DocumentManager = require "./DocumentManager"
  3. HistoryManager = require "./HistoryManager"
  4. async = require "async"
  5. logger = require "logger-sharelatex"
  6. Metrics = require "./Metrics"
  7. Errors = require "./Errors"
  8. module.exports = ProjectManager =
  9. flushProjectWithLocks: (project_id, _callback = (error) ->) ->
  10. timer = new Metrics.Timer("projectManager.flushProjectWithLocks")
  11. callback = (args...) ->
  12. timer.done()
  13. _callback(args...)
  14. RedisManager.getDocIdsInProject project_id, (error, doc_ids) ->
  15. return callback(error) if error?
  16. jobs = []
  17. errors = []
  18. for doc_id in (doc_ids or [])
  19. do (doc_id) ->
  20. jobs.push (callback) ->
  21. DocumentManager.flushDocIfLoadedWithLock project_id, doc_id, (error) ->
  22. if error? and error instanceof Errors.NotFoundError
  23. logger.warn err: error, project_id: project_id, doc_id: doc_id, "found deleted doc when flushing"
  24. callback()
  25. else if error?
  26. logger.error err: error, project_id: project_id, doc_id: doc_id, "error flushing doc"
  27. errors.push(error)
  28. callback()
  29. else
  30. callback()
  31. logger.log project_id: project_id, doc_ids: doc_ids, "flushing docs"
  32. async.series jobs, () ->
  33. if errors.length > 0
  34. callback new Error("Errors flushing docs. See log for details")
  35. else
  36. callback(null)
  37. flushAndDeleteProjectWithLocks: (project_id, _callback = (error) ->) ->
  38. timer = new Metrics.Timer("projectManager.flushAndDeleteProjectWithLocks")
  39. callback = (args...) ->
  40. timer.done()
  41. _callback(args...)
  42. RedisManager.getDocIdsInProject project_id, (error, doc_ids) ->
  43. return callback(error) if error?
  44. jobs = []
  45. errors = []
  46. for doc_id in (doc_ids or [])
  47. do (doc_id) ->
  48. jobs.push (callback) ->
  49. DocumentManager.flushAndDeleteDocWithLock project_id, doc_id, (error) ->
  50. if error?
  51. logger.error err: error, project_id: project_id, doc_id: doc_id, "error deleting doc"
  52. errors.push(error)
  53. callback()
  54. logger.log project_id: project_id, doc_ids: doc_ids, "deleting docs"
  55. async.series jobs, () ->
  56. # There is no harm in flushing project history if the previous call
  57. # failed and sometimes it is required
  58. HistoryManager.flushProjectChangesAsync project_id
  59. if errors.length > 0
  60. callback new Error("Errors deleting docs. See log for details")
  61. else
  62. callback(null)
  63. getProjectDocsAndFlushIfOld: (project_id, projectStateHash, excludeVersions = {}, _callback = (error, docs) ->) ->
  64. timer = new Metrics.Timer("projectManager.getProjectDocsAndFlushIfOld")
  65. callback = (args...) ->
  66. timer.done()
  67. _callback(args...)
  68. RedisManager.checkOrSetProjectState project_id, projectStateHash, (error, projectStateChanged) ->
  69. if error?
  70. logger.error err: error, project_id: project_id, "error getting/setting project state in getProjectDocsAndFlushIfOld"
  71. return callback(error)
  72. # we can't return docs if project structure has changed
  73. if projectStateChanged
  74. return callback Errors.ProjectStateChangedError("project state changed")
  75. # project structure hasn't changed, return doc content from redis
  76. RedisManager.getDocIdsInProject project_id, (error, doc_ids) ->
  77. if error?
  78. logger.error err: error, project_id: project_id, "error getting doc ids in getProjectDocs"
  79. return callback(error)
  80. jobs = []
  81. for doc_id in doc_ids or []
  82. do (doc_id) ->
  83. jobs.push (cb) ->
  84. # get the doc lines from redis
  85. DocumentManager.getDocAndFlushIfOldWithLock project_id, doc_id, (err, lines, version) ->
  86. if err?
  87. logger.error err:err, project_id: project_id, doc_id: doc_id, "error getting project doc lines in getProjectDocsAndFlushIfOld"
  88. return cb(err)
  89. doc = {_id:doc_id, lines:lines, v:version} # create a doc object to return
  90. cb(null, doc)
  91. async.series jobs, (error, docs) ->
  92. return callback(error) if error?
  93. callback(null, docs)
  94. clearProjectState: (project_id, callback = (error) ->) ->
  95. RedisManager.clearProjectState project_id, callback
  96. updateProjectWithLocks: (project_id, user_id, docUpdates, fileUpdates, _callback = (error) ->) ->
  97. timer = new Metrics.Timer("projectManager.updateProject")
  98. callback = (args...) ->
  99. timer.done()
  100. _callback(args...)
  101. project_ops_length = 0
  102. handleDocUpdate = (update, cb) ->
  103. doc_id = update.id
  104. if update.docLines?
  105. RedisManager.addEntity project_id, 'doc', doc_id, user_id, update, (error, count) =>
  106. project_ops_length = count
  107. cb(error)
  108. else
  109. DocumentManager.renameDocWithLock project_id, doc_id, user_id, update, (error, count) =>
  110. project_ops_length = count
  111. cb(error)
  112. handleFileUpdate = (update, cb) ->
  113. file_id = update.id
  114. if update.url?
  115. RedisManager.addEntity project_id, 'file', file_id, user_id, update, (error, count) =>
  116. project_ops_length = count
  117. cb(error)
  118. else
  119. RedisManager.renameFile project_id, file_id, user_id, update, (error, count) =>
  120. project_ops_length = count
  121. cb(error)
  122. async.each docUpdates, handleDocUpdate, (error) ->
  123. return callback(error) if error?
  124. async.each fileUpdates, handleFileUpdate, (error) ->
  125. return callback(error) if error?
  126. if HistoryManager.shouldFlushHistoryOps(project_ops_length, docUpdates.length + fileUpdates.length, HistoryManager.FLUSH_PROJECT_EVERY_N_OPS)
  127. HistoryManager.flushProjectChangesAsync project_id
  128. callback()