HistoryManager.coffee 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. async = require "async"
  2. logger = require "logger-sharelatex"
  3. request = require "request"
  4. Settings = require "settings-sharelatex"
  5. HistoryRedisManager = require "./HistoryRedisManager"
  6. ProjectHistoryRedisManager = require "./ProjectHistoryRedisManager"
  7. RedisManager = require "./RedisManager"
  8. module.exports = HistoryManager =
  9. flushDocChangesAsync: (project_id, doc_id) ->
  10. if !Settings.apis?.trackchanges?
  11. logger.warn { doc_id }, "track changes API is not configured, so not flushing"
  12. return
  13. url = "#{Settings.apis.trackchanges.url}/project/#{project_id}/doc/#{doc_id}/flush"
  14. logger.log { project_id, doc_id, url }, "flushing doc in track changes api"
  15. request.post url, (error, res, body)->
  16. if error?
  17. logger.error { error, doc_id, project_id}, "track changes doc to track changes api"
  18. else if res.statusCode < 200 and res.statusCode >= 300
  19. logger.error { doc_id, project_id }, "track changes api returned a failure status code: #{res.statusCode}"
  20. # flush changes in the background
  21. flushProjectChangesAsync: (project_id) ->
  22. return if !Settings.apis?.project_history?.enabled
  23. HistoryManager.flushProjectChanges project_id, {background:true}, ->
  24. # flush changes and callback (for when we need to know the queue is flushed)
  25. flushProjectChanges: (project_id, options, callback = (error) ->) ->
  26. return callback() if !Settings.apis?.project_history?.enabled
  27. url = "#{Settings.apis.project_history.url}/project/#{project_id}/flush"
  28. qs = {}
  29. qs.background = true if options.background # pass on the background flush option if present
  30. logger.log { project_id, url, qs }, "flushing doc in project history api"
  31. request.post {url: url, qs: qs}, (error, res, body)->
  32. if error?
  33. logger.error { error, project_id}, "project history doc to track changes api"
  34. return callback(error)
  35. else if res.statusCode < 200 and res.statusCode >= 300
  36. logger.error { project_id }, "project history api returned a failure status code: #{res.statusCode}"
  37. return callback(error)
  38. else
  39. return callback()
  40. FLUSH_DOC_EVERY_N_OPS: 100
  41. FLUSH_PROJECT_EVERY_N_OPS: 500
  42. recordAndFlushHistoryOps: (project_id, doc_id, ops = [], doc_ops_length, project_ops_length, callback = (error) ->) ->
  43. if ops.length == 0
  44. return callback()
  45. if Settings.apis?.project_history?.enabled
  46. if HistoryManager.shouldFlushHistoryOps(project_ops_length, ops.length, HistoryManager.FLUSH_PROJECT_EVERY_N_OPS)
  47. # Do this in the background since it uses HTTP and so may be too
  48. # slow to wait for when processing a doc update.
  49. logger.log { project_ops_length, project_id }, "flushing project history api"
  50. HistoryManager.flushProjectChangesAsync project_id
  51. HistoryRedisManager.recordDocHasHistoryOps project_id, doc_id, ops, (error) ->
  52. return callback(error) if error?
  53. if HistoryManager.shouldFlushHistoryOps(doc_ops_length, ops.length, HistoryManager.FLUSH_DOC_EVERY_N_OPS)
  54. # Do this in the background since it uses HTTP and so may be too
  55. # slow to wait for when processing a doc update.
  56. logger.log { doc_ops_length, doc_id, project_id }, "flushing track changes api"
  57. HistoryManager.flushDocChangesAsync project_id, doc_id
  58. callback()
  59. shouldFlushHistoryOps: (length, ops_length, threshold) ->
  60. return false if !length # don't flush unless we know the length
  61. # We want to flush every 100 ops, i.e. 100, 200, 300, etc
  62. # Find out which 'block' (i.e. 0-99, 100-199) we were in before and after pushing these
  63. # ops. If we've changed, then we've gone over a multiple of 100 and should flush.
  64. # (Most of the time, we will only hit 100 and then flushing will put us back to 0)
  65. previousLength = length - ops_length
  66. prevBlock = Math.floor(previousLength / threshold)
  67. newBlock = Math.floor(length / threshold)
  68. return newBlock != prevBlock
  69. MAX_PARALLEL_REQUESTS: 4
  70. resyncProjectHistory: (project_id, projectHistoryId, docs, files, callback) ->
  71. ProjectHistoryRedisManager.queueResyncProjectStructure project_id, projectHistoryId, docs, files, (error) ->
  72. return callback(error) if error?
  73. DocumentManager = require "./DocumentManager"
  74. resyncDoc = (doc, cb) ->
  75. DocumentManager.resyncDocContentsWithLock project_id, doc.doc, cb
  76. async.eachLimit docs, HistoryManager.MAX_PARALLEL_REQUESTS, resyncDoc, callback